diff --git a/well-api.py b/well-api.py index 98626be..ac27316 100644 --- a/well-api.py +++ b/well-api.py @@ -758,6 +758,30 @@ def GetDeviceDetailsSingle(device_id): return device_record +def GetDeviceDetailsSingleFromMac(device_mac): + + conn = get_db_connection() + + sql = "SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'devices';" + with conn.cursor() as cur: + cur.execute(sql) + columns_names = cur.fetchall() + + sql = "SELECT * FROM public.devices WHERE device_mac = '" + device_mac + "'" + + device_record = {} + with conn.cursor() as cur: + cur.execute(sql) + result = cur.fetchone() #cur.fetchall() + if result != None: + cnt = 0 + for field in columns_names: + device_record[field[0]] = result[cnt] + cnt += 1 + else: + #device is not in DB so first lets find it in + pass + return device_record def DeploymentDetails(deployment_id): @@ -1546,20 +1570,20 @@ def StoreDevice2DB(parameters, editing_device_id): description = '{CleanObject(parameters.get('description'))}', location = '{CleanObject(parameters.get('location'))}', close_to = '{CleanObject(parameters.get('close_to'))}', + group_id = {CleanObject(parameters.get('group_id'))}, radar_threshold = '{CleanObject(parameters.get('radar_threshold'))}', temperature_calib = '{CleanObject(parameters.get('temperature_calib'))}', humidity_calib = '{CleanObject(parameters.get('humidity_calib'))}' WHERE device_id = {editing_device_id}; """ - else: sql = f""" INSERT INTO public.devices - (device_mac, well_id, description, location, close_to, radar_threshold, temperature_calib, humidity_calib) + (device_mac, well_id, description, location, close_to, radar_threshold, temperature_calib, humidity_calib, group_id) VALUES ('{CleanObject(parameters.get('device_mac'))}', '{CleanObject(parameters.get('well_id'))}', '{CleanObject(parameters.get('description'))}', '{CleanObject(parameters.get('location'))}', '{CleanObject(parameters.get('close_to'))}', '{CleanObject(parameters.get('radar_threshold'))}', - '{CleanObject(parameters.get('temperature_calib'))}', '{CleanObject(parameters.get('humidity_calib'))}'); + '{CleanObject(parameters.get('temperature_calib'))}', '{CleanObject(parameters.get('humidity_calib'))}', {CleanObject(parameters.get('group_id'))}); """ logger.debug(f"sql= {sql}") # Execute update query @@ -7718,7 +7742,7 @@ def CreateMapFast(map_file, devices_list, selected_date, bw, time_zone_s, radar_ stripes = devices_c * sensors_c #2 for upper maxes, lower mins arr_source_template = np.full((stripes, minutes+4), -0.001, dtype=float) arr_stretched_template = np.zeros((int(stripes*stretch_by), minutes, 3), dtype=np.uint8) # 3 for RGB channels - arr_source = fast_fill_array_from_timescale_single(day_data, time_from_str, devices_list[1], arr_source_template, s_table_temp[0], time_zone_s) + arr_source = fast_fill_array_from_timescale(day_data, time_from_str, devices_list[1], arr_source_template, time_zone_s) arr_source = AddLimits_optimized(arr_source, devices_c, sensors_c, percentile=100) scaled_day = CalcExtremes(arr_source, minutes, stripes) arr_stretched, vocs_scaled = FillImage_optimized(scaled_day, devices_c, sensors_c, arr_stretched_template, group_by, bw) @@ -13575,6 +13599,17 @@ def decode_state(b64_state): logger.error(f"Failed to decode client_state '{b64_state}': {e}") return [] +def create_client_state(base_event, call_control_id, prefix): + """Create a base64 encoded client state string as required by Telnyx API""" + # Create the plain text client state string + plain_state = f"{prefix}_{base_event}_{call_control_id[:8]}" if call_control_id else f"{prefix}_{base_event}_unknownccid" + + # Encode to base64 as required by Telnyx API + base64_state = base64.b64encode(plain_state.encode('utf-8')).decode('ascii') + + logger.debug(f"Client state created: '{plain_state}' -> base64: '{base64_state}'") + return base64_state + def send_telnyx_command(action_path, params, api_key): """ Sends a command to the Telnyx Call Control API actions endpoint. @@ -14614,6 +14649,35 @@ def AddLimits_optimized_filtered(arr_source, devices_c, sensors_c, filtered_s_ta arr_source[:, 1441] = max_vals return arr_source + +def GetNextWellId(min_well_id): + + conn = get_db_connection() + + sql = """ + SELECT COALESCE(MAX(well_id), 0) AS max_well_id + FROM public.devices + """ + + try: + with conn.cursor() as cur: + cur.execute(sql) + result = cur.fetchone() + if result == None: + return min_well_id + else: + max_wel_id = result[0] + if max_wel_id != None and min_well_id != None: + if max_wel_id + 1 > min_well_id: + return max_wel_id + 1 + else: + return min_well_id + else: + if max_wel_id != None: + return max_wel_id + 1 + + except Exception as e: + return min_well_id #==================================== ADD FUNCTIONS BEFORE ============================================ # Main API class @@ -15925,10 +15989,9 @@ class WellApi: time_zone_s = GetTimeZoneOfDeployment(deployment_id) selected_date = form_data.get('date') date_to = form_data.get('to_date') - if date_to == None: + if date_to == None: date_to = selected_date - start_date = datetime.datetime.strptime(selected_date, '%Y-%m-%d') end_date = datetime.datetime.strptime(date_to, '%Y-%m-%d') @@ -16595,28 +16658,40 @@ class WellApi: elif function == "get_device": device_id = form_data.get('device_id') - devices = GetVisibleDevices(privileges) - dataa = {} - dataa['Function'] = "device_details" - dataa['device_details'] = [] - if privileges == "-1": - #device_det = GetDeviceDetails(device_id) - device_det = GetDeviceDetailsSingle(device_id) - if device_det['radar_threshold'] == None or device_det['radar_threshold'] == "": - device_det['radar_threshold'] = '["s3_max",12]' - dataa['device_details'] = device_det - else: - devices_list = [] - for device_id_temp in devices: - devices_list.append(str(device_id_temp[0])) + device_mac = form_data.get('mac') + min_well_id = form_data.get('min_well_id') - if device_id in devices_list: + if device_mac != None: + device_det = GetDeviceDetailsSingleFromMac(device_mac) + print(device_det) + dataa = {} + dataa['Function'] = "device_details" + dataa['device_details'] = device_det + if device_det == {}: + dataa['next_well_id'] = GetNextWellId(min_well_id) + else: + devices = GetVisibleDevices(privileges) + dataa = {} + dataa['Function'] = "device_details" + dataa['device_details'] = {} + if privileges == "-1": + #device_det = GetDeviceDetails(device_id) device_det = GetDeviceDetailsSingle(device_id) if device_det['radar_threshold'] == None or device_det['radar_threshold'] == "": device_det['radar_threshold'] = '["s3_max",12]' - - dataa['device_details'] = device_det + else: + devices_list = [] + for device_id_temp in devices: + devices_list.append(str(device_id_temp[0])) + + if device_id in devices_list: + device_det = GetDeviceDetailsSingle(device_id) + if device_det['radar_threshold'] == None or device_det['radar_threshold'] == "": + device_det['radar_threshold'] = '["s3_max",12]' + + + dataa['device_details'] = device_det resp.media = package_response(dataa) diff --git a/well_web_files/deployment.html b/well_web_files/deployment.html index 945be5f..eae7c65 100644 --- a/well_web_files/deployment.html +++ b/well_web_files/deployment.html @@ -5,7 +5,7 @@ @@ -1619,103 +1619,200 @@ function ProcessQueue() { vall = -1000000; switch(message["payload"]["mtype"]) { case "radar": + case 16: //lets charts dictate which sensors are to be displayed MAC = message["topic"].substring(1); + if(document.getElementById("map_type").value == 1){ + i = 4; //4=radar + if (MAC+sensor_tablesn[i] in series_indexes){ - i = 4; //4=radar - if (MAC+sensor_tablesn[i] in series_indexes){ + series_index = series_indexes[MAC+sensor_tablesn[i]]; + series = UniversalChart.series.items[series_index]; + if (series.visible == true) { + radar_part = document.getElementById("RadarPart").value; + timee = message["payload"]["time"]; + switch(radar_part) { + case "absent": + vall = message["payload"]["radar"][0][1]/message["payload"]["radar"][0][0]; + break; + case "moving": + vall = message["payload"]["radar"][0][2]/message["payload"]["radar"][0][0]; + break; + case "stationary": + vall = message["payload"]["radar"][0][3]/message["payload"]["radar"][0][0]; + break; + case "both": + vall = message["payload"]["radar"][0][4]/message["payload"]["radar"][0][0]; + break; + case "m0": + vall = message["payload"]["radar"][0][5]/message["payload"]["radar"][0][0]; + break; + case "m1": + vall = message["payload"]["radar"][0][6]/message["payload"]["radar"][0][0]; + break; + case "m2": + vall = message["payload"]["radar"][0][7]/message["payload"]["radar"][0][0]; + break; + case "m3": + vall = message["payload"]["radar"][0][8]/message["payload"]["radar"][0][0]; + break; + case "m4": + vall = message["payload"]["radar"][0][9]/message["payload"]["radar"][0][0]; + break; + case "m5": + vall = message["payload"]["radar"][0][10]/message["payload"]["radar"][0][0]; + break; + case "m6": + vall = message["payload"]["radar"][0][11]/message["payload"]["radar"][0][0]; + break; + case "m7": + vall = message["payload"]["radar"][0][12]/message["payload"]["radar"][0][0]; + break; + case "m8": + vall = message["payload"]["radar"][0][13]/message["payload"]["radar"][0][0]; + break; + case "s2": + vall = message["payload"]["radar"][1][3]/message["payload"]["radar"][1][0]; + break; + case "s3": + vall = message["payload"]["radar"][1][4]/message["payload"]["radar"][1][0]; + break; + case "s4": + vall = message["payload"]["radar"][1][5]/message["payload"]["radar"][1][0]; + break; + case "s5": + vall = message["payload"]["radar"][1][6]/message["payload"]["radar"][1][0]; + break; + case "s6": + vall = message["payload"]["radar"][1][7]/message["payload"]["radar"][1][0]; + break; + case "s7": + vall = message["payload"]["radar"][1][8]/message["payload"]["radar"][1][0]; + break; + case "s8": + vall = message["payload"]["radar"][1][9]/message["payload"]["radar"][1][0]; + break; + case "s28": + vall = 0; + for (i_=3; i_<=9; i_++){ + vall += message["payload"]["radar"][1][i_] + } + vall = vall/(7.0*message["payload"]["radar"][1][0]); + break; + } + } + if (vall != -1000000) { + epoch_ms = (timee+hours_delta * 3600) * 1000; + series.data.x.push(new Date(epoch_ms)); + series.data.values.push(vall); + //series.data.values.push(vall); + //time_obj = new Date(epoch_ms); + //series.data.x.push(time_obj); - series_index = series_indexes[MAC+sensor_tablesn[i]]; - series = UniversalChart.series.items[series_index]; - if (series.visible == true) { - radar_part = document.getElementById("RadarPart").value; - timee = message["payload"]["time"]; - switch(radar_part) { - case "absent": - vall = message["payload"]["radar"][0][1]/message["payload"]["radar"][0][0]; - break; - case "moving": - vall = message["payload"]["radar"][0][2]/message["payload"]["radar"][0][0]; - break; - case "stationary": - vall = message["payload"]["radar"][0][3]/message["payload"]["radar"][0][0]; - break; - case "both": - vall = message["payload"]["radar"][0][4]/message["payload"]["radar"][0][0]; - break; - case "m0": - vall = message["payload"]["radar"][0][5]/message["payload"]["radar"][0][0]; - break; - case "m1": - vall = message["payload"]["radar"][0][6]/message["payload"]["radar"][0][0]; - break; - case "m2": - vall = message["payload"]["radar"][0][7]/message["payload"]["radar"][0][0]; - break; - case "m3": - vall = message["payload"]["radar"][0][8]/message["payload"]["radar"][0][0]; - break; - case "m4": - vall = message["payload"]["radar"][0][9]/message["payload"]["radar"][0][0]; - break; - case "m5": - vall = message["payload"]["radar"][0][10]/message["payload"]["radar"][0][0]; - break; - case "m6": - vall = message["payload"]["radar"][0][11]/message["payload"]["radar"][0][0]; - break; - case "m7": - vall = message["payload"]["radar"][0][12]/message["payload"]["radar"][0][0]; - break; - case "m8": - vall = message["payload"]["radar"][0][13]/message["payload"]["radar"][0][0]; - break; - case "s2": - vall = message["payload"]["radar"][1][3]/message["payload"]["radar"][1][0]; - break; - case "s3": - vall = message["payload"]["radar"][1][4]/message["payload"]["radar"][1][0]; - break; - case "s4": - vall = message["payload"]["radar"][1][5]/message["payload"]["radar"][1][0]; - break; - case "s5": - vall = message["payload"]["radar"][1][6]/message["payload"]["radar"][1][0]; - break; - case "s6": - vall = message["payload"]["radar"][1][7]/message["payload"]["radar"][1][0]; - break; - case "s7": - vall = message["payload"]["radar"][1][8]/message["payload"]["radar"][1][0]; - break; - case "s8": - vall = message["payload"]["radar"][1][9]/message["payload"]["radar"][1][0]; - break; - case "s28": - vall = 0; - for (i_=3; i_<=9; i_++){ - vall += message["payload"]["radar"][1][i_] - } - vall = vall/(7.0*message["payload"]["radar"][1][0]); - break; + UniversalChart.axes.bottom.labels.roundFirst = true; + UniversalChart.axes.bottom.labels.dateFormat = "shortDateTime"; + UniversalChart.draw(); } } - if (vall != -1000000) { - epoch_ms = (timee+hours_delta * 3600) * 1000; - series.data.x.push(new Date(epoch_ms)); - series.data.values.push(vall); - //series.data.values.push(vall); - //time_obj = new Date(epoch_ms); - //series.data.x.push(time_obj); + } + else{ + for(i = 0; i<=18; i++){ + if (MAC+radar_tablesn[i] in series_indexes){ - UniversalChart.axes.bottom.labels.roundFirst = true; - UniversalChart.axes.bottom.labels.dateFormat = "shortDateTime"; - UniversalChart.draw(); + series_index = series_indexes[MAC+radar_tablesn[i]]; + series = UniversalChart.series.items[series_index]; + if (series.visible == true) { + radar_part = document.getElementById("RadarPart").value; + timee = message["payload"]["time"]; + switch(radar_part) { + case "absent": + vall = message["payload"]["radar"][0][1]/message["payload"]["radar"][0][0]; + break; + case "moving": + vall = message["payload"]["radar"][0][2]/message["payload"]["radar"][0][0]; + break; + case "stationary": + vall = message["payload"]["radar"][0][3]/message["payload"]["radar"][0][0]; + break; + case "both": + vall = message["payload"]["radar"][0][4]/message["payload"]["radar"][0][0]; + break; + case "m0": + vall = message["payload"]["radar"][0][5]/message["payload"]["radar"][0][0]; + break; + case "m1": + vall = message["payload"]["radar"][0][6]/message["payload"]["radar"][0][0]; + break; + case "m2": + vall = message["payload"]["radar"][0][7]/message["payload"]["radar"][0][0]; + break; + case "m3": + vall = message["payload"]["radar"][0][8]/message["payload"]["radar"][0][0]; + break; + case "m4": + vall = message["payload"]["radar"][0][9]/message["payload"]["radar"][0][0]; + break; + case "m5": + vall = message["payload"]["radar"][0][10]/message["payload"]["radar"][0][0]; + break; + case "m6": + vall = message["payload"]["radar"][0][11]/message["payload"]["radar"][0][0]; + break; + case "m7": + vall = message["payload"]["radar"][0][12]/message["payload"]["radar"][0][0]; + break; + case "m8": + vall = message["payload"]["radar"][0][13]/message["payload"]["radar"][0][0]; + break; + case "s2": + vall = message["payload"]["radar"][1][3]/message["payload"]["radar"][1][0]; + break; + case "s3": + vall = message["payload"]["radar"][1][4]/message["payload"]["radar"][1][0]; + break; + case "s4": + vall = message["payload"]["radar"][1][5]/message["payload"]["radar"][1][0]; + break; + case "s5": + vall = message["payload"]["radar"][1][6]/message["payload"]["radar"][1][0]; + break; + case "s6": + vall = message["payload"]["radar"][1][7]/message["payload"]["radar"][1][0]; + break; + case "s7": + vall = message["payload"]["radar"][1][8]/message["payload"]["radar"][1][0]; + break; + case "s8": + vall = message["payload"]["radar"][1][9]/message["payload"]["radar"][1][0]; + break; + case "s28": + vall = 0; + for (i_=3; i_<=9; i_++){ + vall += message["payload"]["radar"][1][i_] + } + vall = vall/(7.0*message["payload"]["radar"][1][0]); + break; + } + } + if (vall != -1000000) { + epoch_ms = (timee+hours_delta * 3600) * 1000; + series.data.x.push(new Date(epoch_ms)); + series.data.values.push(vall); + //series.data.values.push(vall); + //time_obj = new Date(epoch_ms); + //series.data.x.push(time_obj); + + UniversalChart.axes.bottom.labels.roundFirst = true; + UniversalChart.axes.bottom.labels.dateFormat = "shortDateTime"; + UniversalChart.draw(); + } + } } } break; case "sensors": + case 17: //case "light": //message["payload"]["topic"] map_type = document.getElementById("map_type").value; @@ -2218,7 +2315,7 @@ function createCustomLegend() { const legendWidth = 150; // Width for the legend const chartTopMargin = 35; // Approximate top margin of chart (for title, etc.) const chartBottomMargin = 40; // Approximate bottom margin (for axis labels, etc.) - + // Create or update custom legend container let legendDiv = document.getElementById('custom-chart-legend'); if (!legendDiv) { @@ -2235,20 +2332,20 @@ function createCustomLegend() { legendDiv.style.overflowY = 'auto'; chartContainer.appendChild(legendDiv); } - + // Clear existing legend legendDiv.innerHTML = ''; - + // Calculate series area height to match legend height if needed const seriesAreaHeight = canvasRect.height - chartTopMargin - chartBottomMargin; legendDiv.style.height = seriesAreaHeight + 'px'; - + // Create Select All/None controls const controlsDiv = document.createElement('div'); controlsDiv.style.marginBottom = '1px'; controlsDiv.style.paddingBottom = '1px'; controlsDiv.style.borderBottom = '1px solid #ddd'; - + // Select All button const selectAllBtn = document.createElement('button'); selectAllBtn.textContent = 'All'; @@ -2258,7 +2355,7 @@ function createCustomLegend() { selectAllBtn.style.cursor = 'pointer'; selectAllBtn.style.border = '1px solid #ccc'; selectAllBtn.style.backgroundColor = '#f8f8f8'; - + // Select None button const selectNoneBtn = document.createElement('button'); selectNoneBtn.textContent = 'None'; @@ -2267,12 +2364,12 @@ function createCustomLegend() { selectNoneBtn.style.cursor = 'pointer'; selectNoneBtn.style.border = '1px solid #ccc'; selectNoneBtn.style.backgroundColor = '#f8f8f8'; - + // Add buttons to controls controlsDiv.appendChild(selectAllBtn); controlsDiv.appendChild(selectNoneBtn); legendDiv.appendChild(controlsDiv); - + // Function to update all checkboxes function updateAllCheckboxes(checked) { const checkboxes = legendDiv.querySelectorAll('input[type="checkbox"][data-series-index]'); @@ -2283,40 +2380,40 @@ function createCustomLegend() { }); UniversalChart.draw(); } - + // Add event listeners for Select All/None buttons selectAllBtn.addEventListener('click', function() { updateAllCheckboxes(true); }); - + selectNoneBtn.addEventListener('click', function() { updateAllCheckboxes(false); }); - + // Add legend items for (let i = 0; i < UniversalChart.series.count(); i++) { const series = UniversalChart.series.items[i]; - + // Create legend item container const itemDiv = document.createElement('div'); itemDiv.style.marginBottom = '5px'; itemDiv.style.display = 'flex'; itemDiv.style.alignItems = 'center'; - + // Create checkbox const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = series.visible; checkbox.style.marginRight = '5px'; checkbox.setAttribute('data-series-index', i); - + // Add event listener for checkbox checkbox.addEventListener('change', function() { const seriesIndex = parseInt(this.getAttribute('data-series-index')); UniversalChart.series.items[seriesIndex].visible = this.checked; UniversalChart.draw(); }); - + // Create color swatch const colorSwatch = document.createElement('span'); colorSwatch.style.display = 'inline-block'; @@ -2324,31 +2421,31 @@ function createCustomLegend() { colorSwatch.style.height = '12px'; colorSwatch.style.backgroundColor = series.format.stroke.fill; colorSwatch.style.marginRight = '5px'; - + // Create label const label = document.createElement('span'); label.textContent = series.title; label.style.fontSize = '12px'; - + // Add elements to item itemDiv.appendChild(checkbox); itemDiv.appendChild(colorSwatch); itemDiv.appendChild(label); - + // Add item to legend legendDiv.appendChild(itemDiv); } - + // Hide the built-in legend UniversalChart.legend.visible = false; - + // Shift the chart panel to the right to make space for the legend // This might require adjusting the chart margins or bounds const chartWidth = UniversalChart.canvas.width; const marginPercentage = ((30+legendWidth) / chartWidth) * 100; UniversalChart.panel.margins.left = marginPercentage; // 20px extra spacing UniversalChart.panel.margins.right = 0; - + // Redraw the chart with new dimensions UniversalChart.draw(); } @@ -3619,12 +3716,12 @@ async function GetZGraph() { formData.append('time', timee); formData.append('data_type', data_type); if (document.getElementById("rcr-check").checked){ - formData.append('refresh', 1); + formData.append('refresh', 1); } else{ - formData.append('refresh', 0); + formData.append('refresh', 0); } - + data = await SubmitForm(formData); if (data.status !== "200 OK") {