diff --git a/NPM/get_data_modbus_v3.py b/NPM/get_data_modbus_v3.py index 4e26897..0c9c141 100755 --- a/NPM/get_data_modbus_v3.py +++ b/NPM/get_data_modbus_v3.py @@ -52,9 +52,7 @@ def load_config(config_file): return {} # Load the configuration data -config_file = '/var/www/nebuleair_pro_4g/config.json' -config = load_config(config_file) -npm_solo_port = config.get('NPM_solo_port', '') #port du NPM solo +npm_solo_port = "/dev/ttyAMA5" #port du NPM solo #GET RTC TIME from SQlite cursor.execute("SELECT * FROM timestamp_table LIMIT 1") diff --git a/README.md b/README.md index 7e4426c..ae66099 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Line by line installation. ``` sudo apt update -sudo apt install git gh apache2 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus -y +sudo apt install git gh apache2 sqlite3 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus -y sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil ntplib pytz gpiozero adafruit-circuitpython-ads1x15 numpy --break-system-packages sudo mkdir -p /var/www/.ssh sudo ssh-keygen -t rsa -b 4096 -f /var/www/.ssh/id_rsa -N "" diff --git a/SARA/reboot/start.py b/SARA/reboot/start.py index 7655a7c..27e3e4c 100644 --- a/SARA/reboot/start.py +++ b/SARA/reboot/start.py @@ -17,15 +17,43 @@ import time import sys import json import re +import sqlite3 -#get data from config -def load_config(config_file): +# database connection +conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") +cursor = conn.cursor() + +#get config data from SQLite table +def load_config_sqlite(): + """ + Load configuration data from SQLite config table + + Returns: + dict: Configuration data with proper type conversion + """ try: - with open(config_file, 'r') as file: - config_data = json.load(file) + + # Query the config table + cursor.execute("SELECT key, value, type FROM config_table") + rows = cursor.fetchall() + + # Create config dictionary + config_data = {} + for key, value, type_name in rows: + # Convert value based on its type + if type_name == 'bool': + config_data[key] = value == '1' or value == 'true' + elif type_name == 'int': + config_data[key] = int(value) + elif type_name == 'float': + config_data[key] = float(value) + else: + config_data[key] = value + return config_data + except Exception as e: - print(f"Error loading config file: {e}") + print(f"Error loading config from SQLite: {e}") return {} #Fonction pour mettre à jour le JSON de configuration @@ -57,10 +85,55 @@ def update_json_key(file_path, key, value): except Exception as e: print(f"Error updating the JSON file: {e}") -# Define the config file path -config_file = '/var/www/nebuleair_pro_4g/config.json' -# Load the configuration data -config = load_config(config_file) + +def update_sqlite_config(key, value): + """ + Updates a specific key in the SQLite config_table with a new value. + + :param key: The key to update in the config_table. + :param value: The new value to assign to the key. + """ + try: + + # Check if the key exists and get its type + cursor.execute("SELECT type FROM config_table WHERE key = ?", (key,)) + result = cursor.fetchone() + + if result is None: + print(f"Key '{key}' not found in the config_table.") + conn.close() + return + + # Get the type of the value from the database + value_type = result[0] + + # Convert the value to the appropriate string representation based on its type + if value_type == 'bool': + # Convert Python boolean or string 'true'/'false' to '1'/'0' + if isinstance(value, bool): + str_value = '1' if value else '0' + else: + str_value = '1' if str(value).lower() in ('true', '1', 'yes', 'y') else '0' + elif value_type == 'int': + str_value = str(int(value)) + elif value_type == 'float': + str_value = str(float(value)) + else: + str_value = str(value) + + # Update the value in the database + cursor.execute("UPDATE config_table SET value = ? WHERE key = ?", (str_value, key)) + + # Commit the changes and close the connection + conn.commit() + + print(f"💾 Updated '{key}' to '{value}' in database.") + except Exception as e: + print(f"Error updating the SQLite database: {e}") + +#Load config +config = load_config_sqlite() +#config baudrate = config.get('SaraR4_baudrate', 115200) #baudrate du sara R4 device_id = config.get('deviceID', '').upper() #device ID en maj @@ -151,7 +224,7 @@ try: print("⚠️ Could not identify modem model") print(f"🔍 Model: {model}") - update_json_key(config_file, "modem_version", model) + update_sqlite_config("modem_version", model) time.sleep(1) ''' @@ -332,9 +405,9 @@ try: else: print("❌ Failed to extract coordinates.") - #update config.json - update_json_key(config_file, "latitude_raw", float(latitude)) - update_json_key(config_file, "longitude_raw", float(longitude)) + #update sqlite table + update_sqlite_config("latitude_raw", float(latitude)) + update_sqlite_config("longitude_raw", float(longitude)) time.sleep(1) diff --git a/SARA/sara_connectNetwork.py b/SARA/sara_connectNetwork.py index 96b9a8f..6342cca 100755 --- a/SARA/sara_connectNetwork.py +++ b/SARA/sara_connectNetwork.py @@ -36,6 +36,51 @@ def load_config(config_file): print(f"Error loading config file: {e}") return {} +def read_complete_response(serial_connection, timeout=2, end_of_response_timeout=2, wait_for_lines=None, debug=True): + ''' + Fonction très importante !!! + Reads the complete response from a serial connection and waits for specific lines. + ''' + if wait_for_lines is None: + wait_for_lines = [] # Default to an empty list if not provided + + response = bytearray() + serial_connection.timeout = timeout + end_time = time.time() + end_of_response_timeout + start_time = time.time() + + while True: + elapsed_time = time.time() - start_time # Time since function start + if serial_connection.in_waiting > 0: + data = serial_connection.read(serial_connection.in_waiting) + response.extend(data) + end_time = time.time() + end_of_response_timeout # Reset timeout on new data + + # Decode and check for any target line + decoded_response = response.decode('utf-8', errors='replace') + for target_line in wait_for_lines: + if target_line in decoded_response: + if debug: + print(f"[DEBUG] 🔎 Found target line: {target_line} (in {elapsed_time:.2f}s)") + return decoded_response # Return response immediately if a target line is found + elif time.time() > end_time: + if debug: + print(f"[DEBUG] Timeout reached. No more data received.") + break + time.sleep(0.1) # Short sleep to prevent busy waiting + + # Final response and debug output + total_elapsed_time = time.time() - start_time + if debug: + print(f"[DEBUG] ⏱️ elapsed time: {total_elapsed_time:.2f}s. ⏱️") + # Check if the elapsed time exceeded 10 seconds + if total_elapsed_time > 10 and debug: + print(f"[ALERT] 🚨 The operation took too long 🚨") + print(f'[ALERT] ⚠️{total_elapsed_time:.2f}s⚠️') + + return response.decode('utf-8', errors='replace') # Return the full response if no target line is found + + # Define the config file path config_file = '/var/www/nebuleair_pro_4g/config.json' # Load the configuration data @@ -57,17 +102,11 @@ ser.write((command + '\r').encode('utf-8')) try: - # Read lines until a timeout occurs - response_lines = [] - while True: - line = ser.readline().decode('utf-8').strip() - if not line: - break # Break the loop if an empty line is encountered - response_lines.append(line) - - # Print the response - for line in response_lines: - print(line) + response = read_complete_response(ser, wait_for_lines=["OK", "ERROR"],timeout=5, end_of_response_timeout=120, debug=True) + + print('

') + print(response) + print("

", end="") except serial.SerialException as e: print(f"Error: {e}") diff --git a/boot_hotspot.sh b/boot_hotspot.sh index a9a4311..4bc85d8 100755 --- a/boot_hotspot.sh +++ b/boot_hotspot.sh @@ -2,6 +2,8 @@ # Script to check if wifi is connected and start hotspot if not # will also retreive unique RPi ID and store it to deviceID.txt +# script that starts at boot: +# @reboot /var/www/nebuleair_pro_4g/boot_hotspot.sh >> /var/www/nebuleair_pro_4g/logs/app.log 2>&1 OUTPUT_FILE="/var/www/nebuleair_pro_4g/wifi_list.csv" JSON_FILE="/var/www/nebuleair_pro_4g/config.json" @@ -27,15 +29,19 @@ for i in {1..5}; do sleep 1 done -echo "getting SARA R4 serial number" +echo "getting RPI serial number" # Get the last 8 characters of the serial number and write to text file serial_number=$(cat /proc/cpuinfo | grep Serial | awk '{print substr($3, length($3) - 7)}') -# Use jq to update the "deviceID" in the JSON file -jq --arg serial_number "$serial_number" '.deviceID = $serial_number' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" + +# update Sqlite database +echo "Updating SQLite database with device ID: $serial_number" +sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='$serial_number' WHERE key='deviceID';" + echo "id: $serial_number" -#get the SSH port for tunneling -SSH_TUNNEL_PORT=$(jq -r '.sshTunnel_port' "$JSON_FILE") + +# Get SSH tunnel port from SQLite config_table +SSH_TUNNEL_PORT=$(sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "SELECT value FROM config_table WHERE key='sshTunnel_port'") #need to wait for the network manager to be ready sleep 20 @@ -53,17 +59,16 @@ if [ "$STATE" == "30 (disconnected)" ]; then echo "Starting hotspot..." sudo nmcli device wifi hotspot ifname wlan0 ssid nebuleair_pro password nebuleaircfg - # Update JSON to reflect hotspot mode - jq --arg status "hotspot" '.WIFI_status = $status' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" - + # Update SQLite to reflect hotspot mode + sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='hotspot' WHERE key='WIFI_status'" else echo "🛜Success: wlan0 is connected!🛜" CONN_SSID=$(nmcli -g GENERAL.CONNECTION device show wlan0) echo "Connection: $CONN_SSID" - #update config JSON file - jq --arg status "connected" '.WIFI_status = $status' "$JSON_FILE" > temp.json && mv temp.json "$JSON_FILE" + # Update SQLite to reflect hotspot mode + sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" sudo chmod 777 "$JSON_FILE" diff --git a/envea/read_value_v2.py b/envea/read_value_v2.py index 469c381..3e91f7f 100755 --- a/envea/read_value_v2.py +++ b/envea/read_value_v2.py @@ -28,31 +28,14 @@ cursor.execute("SELECT * FROM timestamp_table LIMIT 1") row = cursor.fetchone() # Get the first (and only) row rtc_time_str = row[1] # '2025-02-07 12:30:45' -# Function to load config data -def load_config(config_file): - try: - with open(config_file, 'r') as file: - config_data = json.load(file) - return config_data - except Exception as e: - print(f"Error loading config file: {e}") - return {} +# Fetch connected ENVEA sondes from SQLite config table +cursor.execute("SELECT port, name, coefficient FROM envea_sondes_table WHERE connected = 1") +connected_envea_sondes = cursor.fetchall() # List of tuples (port, name, coefficient) -# Define the config file path -config_file = '/var/www/nebuleair_pro_4g/config.json' - -# Load the configuration data -config = load_config(config_file) - -# Initialize sensors and serial connections -envea_sondes = config.get('envea_sondes', []) -connected_envea_sondes = [sonde for sonde in envea_sondes if sonde.get('connected', False)] serial_connections = {} if connected_envea_sondes: - for device in connected_envea_sondes: - port = device.get('port', 'Unknown') - name = device.get('name', 'Unknown') + for port, name, coefficient in connected_envea_sondes: try: serial_connections[name] = serial.Serial( port=f'/dev/{port}', @@ -74,9 +57,7 @@ data_nh3 = 0 try: if connected_envea_sondes: - for device in connected_envea_sondes: - name = device.get('name', 'Unknown') - coefficient = device.get('coefficient', 1) + for port, name, coefficient in connected_envea_sondes: if name in serial_connections: serial_connection = serial_connections[name] try: diff --git a/html/admin.html b/html/admin.html index 8f5ef6b..e9f6cdd 100755 --- a/html/admin.html +++ b/html/admin.html @@ -55,52 +55,13 @@
-

Parameters

+

Parameters (config)

- - -
- - -
- -
- - -
- -
- - -
-
- +
@@ -108,6 +69,56 @@
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+ +
@@ -117,13 +128,6 @@

Clock

-
- - -
-
@@ -161,6 +165,22 @@
+ + + + +
+ +
+ + @@ -193,112 +213,290 @@ }); }); +//end document.addEventListener + + +/* + ___ _ _ + / _ \ _ __ | | ___ __ _ __| | + | | | | '_ \| | / _ \ / _` |/ _` | + | |_| | | | | |__| (_) | (_| | (_| | + \___/|_| |_|_____\___/ \__,_|\__,_| + +*/ + window.onload = function() { - fetch('../config.json') // Replace 'deviceID.txt' with 'config.json' - .then(response => response.json()) // Parse response as JSON - .then(data => { - console.log("Getting config file (onload)"); - //get device ID - const deviceID = data.deviceID.trim().toUpperCase(); - //document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID; - //get device Name - const deviceName = data.deviceName; - - console.log("Device Name: " + deviceName); - console.log("Device ID: " + deviceID); - - - - const elements = document.querySelectorAll('.sideBar_sensorName'); - elements.forEach((element) => { - element.innerText = deviceName; - }); - - //get BME check - const checkbox = document.getElementById("check_bme280"); - checkbox.checked = data["BME280/get_data_v2.py"]; - - //get NPM-5channels check - const checkbox_NPM_5channels = document.getElementById("check_NPM_5channels"); - checkbox_NPM_5channels.checked = data["NextPM_5channels"]; - - //get sonde Envea check - const checkbox_envea = document.getElementById("check_envea"); - checkbox_envea.checked = data["envea/read_value_v2.py"]; - - //get RTC check - const checkbox_RTC = document.getElementById("check_RTC"); - checkbox_RTC.checked = data.i2c_RTC; + //NEW way to get config (SQLite) + $.ajax({ + url: 'launcher.php?type=get_config_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config table:"); + console.log(response); + //device name + const deviceName = document.getElementById("device_name"); + deviceName.value = response.deviceName; + //device name_side bar + const elements = document.querySelectorAll('.sideBar_sensorName'); + elements.forEach((element) => { + element.innerText = response.deviceName; + }); + //device ID + const deviceID = response.deviceID.trim().toUpperCase(); + const device_ID = document.getElementById("device_ID"); + device_ID.value = response.deviceID.toUpperCase(); + //nextPM send 5 channels + const checkbox_nmp5channels = document.getElementById("check_NPM_5channels"); + checkbox_nmp5channels.checked = response.npm_5channel; - //device name - const device_name = document.getElementById("device_name"); - device_name.value = data.deviceName; + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX + + //getting config_scripts table + $.ajax({ + url: 'launcher.php?type=get_config_scripts_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config scripts table:"); + console.log(response); - //device ID - const device_ID = document.getElementById("device_ID"); - device_ID.value = data.deviceID.toUpperCase(); + const checkbox_NPM = document.getElementById("check_NPM"); + const checkbox_bme = document.getElementById("check_bme280"); + const checkbox_envea = document.getElementById("check_envea"); + const checkbox_solar = document.getElementById("check_solarBattery"); + const checkbox_wind = document.getElementById("check_WindMeter"); - //get system time and RTC module - $.ajax({ - url: 'launcher.php?type=sys_RTC_module_time', - dataType: 'json', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log(response); - // Update the input fields with the received JSON data - document.getElementById("sys_local_time").value = response.system_local_time; - document.getElementById("sys_UTC_time").value = response.system_utc_time; - document.getElementById("RTC_utc_time").value = response.rtc_module_time; + checkbox_NPM.checked = response["NPM/get_data_modbus_v3.py"]; + checkbox_bme.checked = response["BME280/get_data_v2.py"]; + checkbox_envea.checked = response["envea/read_value_v2.py"]; + checkbox_solar.checked = response["MPPT/read.py"]; + checkbox_wind.checked = response["windMeter/read.py"]; - // Get the time difference - const timeDiff = response.time_difference_seconds; + //si sonde envea is true + if (response["envea/read_value_v2.py"]) { + add_sondeEnveaContainer(); + + } + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX + - // Reference to the alert container - const alertContainer = document.getElementById("alert_container"); + //OLD way to get config (JSON) + /* + fetch('../config.json') // Replace 'deviceID.txt' with 'config.json' + .then(response => response.json()) // Parse response as JSON + .then(data => { + console.log("Getting config file (onload)"); + //get device ID + //document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID; + //get device Name + //const deviceName = data.deviceName; - // Remove any previous alert - alertContainer.innerHTML = ""; + //get BME check + const checkbox = document.getElementById("check_bme280"); + checkbox.checked = data["BME280/get_data_v2.py"]; - // Add an alert based on time difference - if (typeof timeDiff === "number") { - if (timeDiff >= 0 && timeDiff <= 10) { - alertContainer.innerHTML = ` - `; - } else if (timeDiff > 10) { - alertContainer.innerHTML = ` - `; - } - } - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } - }); + //get NPM-5channels check + const checkbox_NPM_5channels = document.getElementById("check_NPM_5channels"); + checkbox_NPM_5channels.checked = data["NextPM_5channels"]; - //get local RTC - $.ajax({ - url: 'launcher.php?type=RTC_time', - dataType: 'text', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log("Local RTC: " + response); - const RTC_Element = document.getElementById("RTC_time"); - RTC_Element.textContent = response; - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } - }); + //get sonde Envea check + const checkbox_envea = document.getElementById("check_envea"); + checkbox_envea.checked = data["envea/read_value_v2.py"]; - }) - .catch(error => console.error('Error loading config.json:', error)); - } + //device name + //const device_name = document.getElementById("device_name"); + //device_name.value = data.deviceName; + + + }) + .catch(error => console.error('Error loading config.json:', error)); + */ + + + + //get system time and RTC module + $.ajax({ + url: 'launcher.php?type=sys_RTC_module_time', + dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting RTC times"); + + console.log(response); + // Update the input fields with the received JSON data + document.getElementById("sys_local_time").value = response.system_local_time; + document.getElementById("sys_UTC_time").value = response.system_utc_time; + document.getElementById("RTC_utc_time").value = response.rtc_module_time; + + // Get the time difference + const timeDiff = response.time_difference_seconds; + + // Reference to the alert container + const alertContainer = document.getElementById("alert_container"); + + // Remove any previous alert + alertContainer.innerHTML = ""; + + // Add an alert based on time difference + if (typeof timeDiff === "number") { + if (timeDiff >= 0 && timeDiff <= 10) { + alertContainer.innerHTML = ` + `; + } else if (timeDiff > 10) { + alertContainer.innerHTML = ` + `; + } + } + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX + + //get local RTC + $.ajax({ + url: 'launcher.php?type=RTC_time', + dataType: 'text', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + //console.log("Local RTC: " + response); + const RTC_Element = document.getElementById("RTC_time"); + RTC_Element.textContent = response; + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); //end AJAx + + +} //end window.onload + + + + + +function update_config_sqlite(param, value){ + console.log("Updating sqlite ",param," : ", value); + const toastLiveExample = document.getElementById('liveToast') + const toastBody = toastLiveExample.querySelector('.toast-body'); + $.ajax({ + url: 'launcher.php?type=update_config_sqlite¶m='+param+'&value='+value, + dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + cache: false, // Prevent AJAX from caching + success: function(response) { + console.log(response); + // Format the response nicely + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Parameter: ${response.param || param}
+ Value: ${response.value || checked}
+ ${response.message || ''} + `; + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Parameter: ${response.param || param} + `; + } + + // Update the toast body with formatted content + toastBody.innerHTML = formattedMessage; + // Show the toast + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample) + toastBootstrap.show() + + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); +} + +function update_config_scripts_sqlite(param, value) { + console.log("Updating scripts sqlite ", param, " : ", value); + const toastLiveExample = document.getElementById('liveToast') + const toastBody = toastLiveExample.querySelector('.toast-body'); + + $.ajax({ + url: 'launcher.php?type=update_config_scripts_sqlite¶m=' + param + '&value=' + value, + dataType: 'json', + method: 'GET', + cache: false, + success: function(response) { + console.log(response); + // Format the response nicely + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Parameter: ${response.script_path || param}
+ Value: ${response.enabled !== undefined ? response.enabled : value}
+ ${response.message || ''} + `; + + if (response.script_path == "envea/read_value_v2.py") { + console.log("envea sondes activated"); + add_sondeEnveaContainer(); + + } + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Parameter: ${response.script_path || param} + `; + } + + // Update the toast body with formatted content + toastBody.innerHTML = formattedMessage; + // Show the toast + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample) + toastBootstrap.show() + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); +} function update_config(param, value){ @@ -358,7 +556,7 @@ function set_RTC_withNTP(){ error: function(xhr, status, error) { console.error('AJAX request failed:', status, error); } - }); + }); //end ajax } function set_RTC_withBrowser(){ @@ -386,7 +584,321 @@ function set_RTC_withBrowser(){ error: function(xhr, status, error) { console.error('AJAX request failed:', status, error); } - }); + }); //end ajax +} + +/* + ____ _ _____ + / ___| ___ _ __ __| | ___ ___ | ____|_ ____ _____ __ _ + \___ \ / _ \| '_ \ / _` |/ _ \/ __| | _| | '_ \ \ / / _ \/ _` | + ___) | (_) | | | | (_| | __/\__ \ | |___| | | \ V / __/ (_| | + |____/ \___/|_| |_|\__,_|\___||___/ |_____|_| |_|\_/ \___|\__,_| + +*/ + +function add_sondeEnveaContainer() { + console.log("Sonde Envea is true: need to add container!"); + + // Getting envea_sondes_table data + $.ajax({ + url: 'launcher.php?type=get_envea_sondes_table_sqlite', + dataType: 'json', + method: 'GET', + success: function(sondes) { + console.log("Getting SQLite envea sondes table:"); + console.log(sondes); + + // Create container div if it doesn't exist + if ($('#sondes_envea_div').length === 0) { + $('#advanced_options').append('
Sondes Envea

Plouf

'); + } else { + // Clear existing content if container exists + $('#sondes_envea_div').html('Sondes Envea'); + $('#envea_table').html(''+ + ''+ + '' + + '' + + '' + + '' + + '' + + '
SoftwareHardware (PCB)
ttyAMA5NPM1
ttyAMA4NPM2
ttyAMA3NPM3
ttyAMA2SARA
'); + } + + // Loop through each sonde and create UI elements + sondes.forEach(function(sonde) { + // Create a unique ID for this sonde + const sondeId = `sonde_${sonde.id}`; + + // Create HTML for this sonde + const sondeHtml = ` +
+
+ +
+ + + +
+ `; + + // Append this sonde to the container + $('#sondes_envea_div').append(sondeHtml); + }); + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); +} + +// Helper functions for updating sonde properties +function updateSondeStatus(id, connected) { + console.log(`Updating sonde ${id} connected status to: ${connected}`); + const toastLiveExample = document.getElementById('liveToast'); + const toastBody = toastLiveExample.querySelector('.toast-body'); + + $.ajax({ + url: `launcher.php?type=update_sonde&id=${id}&field=connected&value=${connected ? 1 : 0}`, + dataType: 'json', + method: 'GET', + cache: false, + success: function(response) { + console.log('Sonde status updated:', response); + + // Format the response for toast + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Sonde ID: ${response.id}
+ Connected: ${connected ? "Yes" : "No"}
+ ${response.message || ''} + `; + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Sonde ID: ${id} + `; + } + + // Update and show toast + toastBody.innerHTML = formattedMessage; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + }, + error: function(xhr, status, error) { + console.error('Failed to update sonde status:', error); + + // Show error toast + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + toastBody.innerHTML = ` + Request Failed!
+ Error: ${error}
+ Sonde ID: ${id} + `; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + } + }); +} + +function updateSondeName(id, name) { + console.log(`Updating sonde ${id} name to: ${name}`); + const toastLiveExample = document.getElementById('liveToast'); + const toastBody = toastLiveExample.querySelector('.toast-body'); + + $.ajax({ + url: `launcher.php?type=update_sonde&id=${id}&field=name&value=${encodeURIComponent(name)}`, + dataType: 'json', + method: 'GET', + cache: false, + success: function(response) { + console.log('Sonde name updated:', response); + + // Format the response for toast + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Sonde ID: ${response.id}
+ Name: ${name}
+ ${response.message || ''} + `; + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Sonde ID: ${id} + `; + } + + // Update and show toast + toastBody.innerHTML = formattedMessage; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + }, + error: function(xhr, status, error) { + console.error('Failed to update sonde name:', error); + + // Show error toast + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + toastBody.innerHTML = ` + Request Failed!
+ Error: ${error}
+ Sonde ID: ${id} + `; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + } + }); +} + +function updateSondePort(id, port) { + console.log(`Updating sonde ${id} port to: ${port}`); + const toastLiveExample = document.getElementById('liveToast'); + const toastBody = toastLiveExample.querySelector('.toast-body'); + + $.ajax({ + url: `launcher.php?type=update_sonde&id=${id}&field=port&value=${encodeURIComponent(port)}`, + dataType: 'json', + method: 'GET', + cache: false, + success: function(response) { + console.log('Sonde port updated:', response); + + // Format the response for toast + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Sonde ID: ${response.id}
+ Port: ${port}
+ ${response.message || ''} + `; + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Sonde ID: ${id} + `; + } + + // Update and show toast + toastBody.innerHTML = formattedMessage; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + }, + error: function(xhr, status, error) { + console.error('Failed to update sonde port:', error); + + // Show error toast + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + toastBody.innerHTML = ` + Request Failed!
+ Error: ${error}
+ Sonde ID: ${id} + `; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + } + }); +} + +function updateSondeCoefficient(id, coefficient) { + console.log(`Updating sonde ${id} coefficient to: ${coefficient}`); + const toastLiveExample = document.getElementById('liveToast'); + const toastBody = toastLiveExample.querySelector('.toast-body'); + + $.ajax({ + url: `launcher.php?type=update_sonde&id=${id}&field=coefficient&value=${coefficient}`, + dataType: 'json', + method: 'GET', + cache: false, + success: function(response) { + console.log('Sonde coefficient updated:', response); + + // Format the response for toast + let formattedMessage = ''; + + if (response.success) { + // Success message + toastLiveExample.classList.remove('text-bg-danger'); + toastLiveExample.classList.add('text-bg-success'); + + formattedMessage = ` + Success!
+ Sonde ID: ${response.id}
+ Coefficient: ${coefficient}
+ ${response.message || ''} + `; + } else { + // Error message + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + + formattedMessage = ` + Error!
+ ${response.error || 'Unknown error'}
+ Sonde ID: ${id} + `; + } + + // Update and show toast + toastBody.innerHTML = formattedMessage; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + }, + error: function(xhr, status, error) { + console.error('Failed to update sonde coefficient:', error); + + // Show error toast + toastLiveExample.classList.remove('text-bg-success'); + toastLiveExample.classList.add('text-bg-danger'); + toastBody.innerHTML = ` + Request Failed!
+ Error: ${error}
+ Sonde ID: ${id} + `; + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample); + toastBootstrap.show(); + } + }); } diff --git a/html/index.html b/html/index.html index a72bc31..cce3822 100755 --- a/html/index.html +++ b/html/index.html @@ -135,6 +135,30 @@ window.onload = function() { + //NEW way to get data from SQLITE + $.ajax({ + url: 'launcher.php?type=get_config_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config table:"); + console.log(response); + + //get device Name (for the side bar) + const deviceName = response.deviceName; + const elements = document.querySelectorAll('.sideBar_sensorName'); + elements.forEach((element) => { + element.innerText = deviceName; + }); + + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); + + /* OLD way of getting config data fetch('../config.json') // Replace 'deviceID.txt' with 'config.json' .then(response => response.json()) // Parse response as JSON .then(data => { @@ -151,7 +175,12 @@ window.onload = function() { elements.forEach((element) => { element.innerText = deviceName; }); - + + //end fetch config + }) + .catch(error => console.error('Error loading config.json:', error)); + //end windows on load + */ //get local RTC $.ajax({ url: 'launcher.php?type=RTC_time', @@ -421,10 +450,6 @@ window.onload = function() { - //end fetch config - }) - .catch(error => console.error('Error loading config.json:', error)); - //end windows on load } diff --git a/html/launcher.php b/html/launcher.php index bb60cbe..8008684 100755 --- a/html/launcher.php +++ b/html/launcher.php @@ -1,13 +1,16 @@ setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Get all main configuration entries + $config_query = $db->query("SELECT key, value, type FROM config_table"); + $config_data = $config_query->fetchAll(PDO::FETCH_ASSOC); + + // Convert data types according to their 'type' field + $result = []; + foreach ($config_data as $item) { + $key = $item['key']; + $value = $item['value']; + $type = $item['type']; + + // Convert value based on its type + switch ($type) { + case 'bool': + $parsed_value = ($value == '1' || $value == 'true') ? true : false; + break; + case 'int': + $parsed_value = intval($value); + break; + case 'float': + $parsed_value = floatval($value); + break; + default: + $parsed_value = $value; + } + + $result[$key] = $parsed_value; + } + + // Return JSON response + header('Content-Type: application/json'); + echo json_encode($result, JSON_PRETTY_PRINT); + + } catch (PDOException $e) { + // Return error as JSON + header('Content-Type: application/json'); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); + } +} + +/* + +*/ +//GETING data from config_scrips_table (SQLite DB) +if ($type == "get_config_scripts_sqlite") { + + try { + $db = new PDO("sqlite:$database_path"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Get all main configuration entries + $config_query = $db->query("SELECT * FROM config_scripts_table"); + $config_data = $config_query->fetchAll(PDO::FETCH_ASSOC); + + // Convert data types according to their 'type' field + $result = []; + foreach ($config_data as $item) { + $script_path = $item['script_path']; + $enabled = $item['enabled']; + + // Convert the enabled field to a proper boolean + $result[$script_path] = ($enabled == 1); + } + + // Return JSON response + header('Content-Type: application/json'); + echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + } catch (PDOException $e) { + // Return error as JSON + header('Content-Type: application/json'); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); + } + +} + +/* + +*/ +//GETING data from envea_sondes_table (SQLite DB) +if ($type == "get_envea_sondes_table_sqlite") { + + try { + $db = new PDO("sqlite:$database_path"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Get all entries from envea_sondes_table + $query = $db->query("SELECT id, connected, port, name, coefficient FROM envea_sondes_table"); + $data = $query->fetchAll(PDO::FETCH_ASSOC); + + // Convert data types appropriately + $result = []; + foreach ($data as $item) { + // Create object for each sonde with proper data types + $sonde = [ + 'id' => (int)$item['id'], + 'connected' => $item['connected'] == 1, // Convert to boolean + 'port' => $item['port'], + 'name' => $item['name'], + 'coefficient' => (float)$item['coefficient'] // Convert to float + ]; + + // Add to results array + $result[] = $sonde; + } + + + // Return JSON response + header('Content-Type: application/json'); + echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + + } catch (PDOException $e) { + // Return error as JSON + header('Content-Type: application/json'); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); + } + +} + + + + +//UPDATING the config_table from SQLite DB +if ($type == "update_config_sqlite") { + $param = $_GET['param'] ?? null; + $value = $_GET['value'] ?? null; + + if ($param === null || $value === null) { + echo json_encode(["error" => "Missing parameter or value"]); + exit; + } + + try { + $db = new PDO("sqlite:$database_path"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // First, check if parameter exists and get its type + $checkStmt = $db->prepare("SELECT type FROM config_table WHERE key = :param"); + $checkStmt->bindParam(':param', $param); + $checkStmt->execute(); + $result = $checkStmt->fetch(PDO::FETCH_ASSOC); + + if ($result) { + // Parameter exists, determine type and update + $type = $result['type']; + + // Convert value according to type if needed + $convertedValue = $value; + if ($type == "bool") { + // Convert various boolean representations to 0/1 + $convertedValue = (filter_var($value, FILTER_VALIDATE_BOOLEAN)) ? "1" : "0"; + } elseif ($type == "int") { + $convertedValue = (string)intval($value); + } elseif ($type == "float") { + $convertedValue = (string)floatval($value); + } + + // Update the value + $updateStmt = $db->prepare("UPDATE config_table SET value = :value WHERE key = :param"); + $updateStmt->bindParam(':value', $convertedValue); + $updateStmt->bindParam(':param', $param); + $updateStmt->execute(); + + echo json_encode([ + "success" => true, + "message" => "Configuration updated successfully", + "param" => $param, + "value" => $convertedValue, + "type" => $type + ]); + } else { + echo json_encode([ + "error" => "Parameter not found in configuration", + "param" => $param + ]); + } + } catch (PDOException $e) { + echo json_encode(["error" => $e->getMessage()]); + } +} + +//UPDATING the config_scripts table from SQLite DB +if ($type == "update_config_scripts_sqlite") { + $script_path = $_GET['param'] ?? null; + $enabled = $_GET['value'] ?? null; + + if ($script_path === null || $enabled === null) { + echo json_encode(["error" => "Missing parameter or value"]); + exit; + } + + try { + $db = new PDO("sqlite:$database_path"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // First, check if parameter exists and get its type + $checkStmt = $db->prepare("SELECT enabled FROM config_scripts_table WHERE script_path = :script_path"); + $checkStmt->bindParam(':script_path', $script_path); + $checkStmt->execute(); + $result = $checkStmt->fetch(PDO::FETCH_ASSOC); + + if ($result) { + // Convert enabled value to 0 or 1 + $enabledValue = (filter_var($enabled, FILTER_VALIDATE_BOOLEAN)) ? 1 : 0; + + // Update the enabled status + $updateStmt = $db->prepare("UPDATE config_scripts_table SET enabled = :enabled WHERE script_path = :script_path"); + $updateStmt->bindParam(':enabled', $enabledValue, PDO::PARAM_INT); + $updateStmt->bindParam(':script_path', $script_path); + $updateStmt->execute(); + + echo json_encode([ + "success" => true, + "message" => "Script configuration updated successfully", + "script_path" => $script_path, + "enabled" => (bool)$enabledValue + ], JSON_UNESCAPED_SLASHES); // Prevent escaping forward slashes + } else { + echo json_encode([ + "error" => "Script path not found in configuration", + "script_path" => $script_path + ], JSON_UNESCAPED_SLASHES); // Prevent escaping forward slashes + } + } catch (PDOException $e) { + echo json_encode(["error" => $e->getMessage()]); + } +} + +//UPDATING the envea_sondes_table table from SQLite DB +if ($type == "update_sonde") { + $id = $_GET['id'] ?? null; + $field = $_GET['field'] ?? null; + $value = $_GET['value'] ?? null; + + // Validate parameters + if ($id === null || $field === null || $value === null) { + echo json_encode([ + "success" => false, + "error" => "Missing required parameters (id, field, or value)" + ]); + exit; + } + + // Validate field name (whitelist approach for security) + $allowed_fields = ['connected', 'port', 'name', 'coefficient']; + if (!in_array($field, $allowed_fields)) { + echo json_encode([ + "success" => false, + "error" => "Invalid field name: " . $field + ]); + exit; + } + + try { + // Connect to the database + $db = new PDO("sqlite:$database_path"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Check if the sonde exists + $checkStmt = $db->prepare("SELECT id FROM envea_sondes_table WHERE id = :id"); + $checkStmt->bindParam(':id', $id, PDO::PARAM_INT); + $checkStmt->execute(); + + if (!$checkStmt->fetch()) { + echo json_encode([ + "success" => false, + "error" => "Sonde with ID $id not found" + ]); + exit; + } + + // Process value based on field type + if ($field == 'connected') { + // Convert to integer (0 or 1) + $processedValue = filter_var($value, FILTER_VALIDATE_BOOLEAN) ? 1 : 0; + $paramType = PDO::PARAM_INT; + } else if ($field == 'coefficient') { + // Convert to float + $processedValue = floatval($value); + $paramType = PDO::PARAM_STR; // SQLite doesn't have PARAM_FLOAT + } else { + // For text fields (port, name) + $processedValue = $value; + $paramType = PDO::PARAM_STR; + } + + // Update the sonde record + $updateStmt = $db->prepare("UPDATE envea_sondes_table SET $field = :value WHERE id = :id"); + $updateStmt->bindParam(':value', $processedValue, $paramType); + $updateStmt->bindParam(':id', $id, PDO::PARAM_INT); + $updateStmt->execute(); + + // Return success response + echo json_encode([ + "success" => true, + "message" => "Sonde $id updated successfully", + "field" => $field, + "value" => $processedValue + ]); + + } catch (PDOException $e) { + // Return error as JSON + echo json_encode([ + "success" => false, + "error" => "Database error: " . $e->getMessage() + ]); + } +} + +//update the config (old JSON updating) if ($type == "update_config") { - echo "updating...."; + echo "updating.... "; $param=$_GET['param']; $value=$_GET['value']; $configFile = '../config.json'; @@ -269,7 +591,7 @@ if ($type == "sara_connectNetwork") { $timeout=$_GET['timeout']; $networkID=$_GET['networkID']; - echo "updating SARA_R4_networkID in config file"; + //echo "updating SARA_R4_networkID in config file"; // Convert `networkID` to an integer (or float if needed) $networkID = is_numeric($networkID) ? (strpos($networkID, '.') !== false ? (float)$networkID : (int)$networkID) : 0; #save to config.json @@ -296,10 +618,10 @@ if ($type == "sara_connectNetwork") { die("Error: Could not write to JSON file."); } - echo "SARA_R4_networkID updated successfully."; + //echo "SARA_R4_networkID updated successfully."; - echo "connecting to network... please wait..."; + //echo "connecting to network... please wait..."; $command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_connectNetwork.py ' . $port . ' ' . $networkID . ' ' . $timeout; $output = shell_exec($command); echo $output; diff --git a/html/logs.html b/html/logs.html index 62127be..d3d97ed 100755 --- a/html/logs.html +++ b/html/logs.html @@ -179,40 +179,46 @@ window.onload = function() { getModem_busy_status(); setInterval(getModem_busy_status, 2000); - fetch('../config.json') // Replace 'deviceID.txt' with 'config.json' - .then(response => response.json()) // Parse response as JSON - .then(data => { - console.log("Getting config file (onload)"); - //get device ID - const deviceID = data.deviceID.trim().toUpperCase(); - // document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID; - //get device Name - const deviceName = data.deviceName; - - const elements = document.querySelectorAll('.sideBar_sensorName'); - elements.forEach((element) => { - element.innerText = deviceName; - }); + //NEW way to get config (SQLite) + $.ajax({ + url: 'launcher.php?type=get_config_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config table:"); + console.log(response); + + //device name_side bar + const elements = document.querySelectorAll('.sideBar_sensorName'); + elements.forEach((element) => { + element.innerText = response.deviceName; + }); + + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX - //get local RTC - $.ajax({ - url: 'launcher.php?type=RTC_time', - dataType: 'text', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log("Local RTC: " + response); - const RTC_Element = document.getElementById("RTC_time"); - RTC_Element.textContent = response; - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } - }); + //get local RTC + $.ajax({ + url: 'launcher.php?type=RTC_time', + dataType: 'text', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Local RTC: " + response); + const RTC_Element = document.getElementById("RTC_time"); + RTC_Element.textContent = response; + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); - }) - .catch(error => console.error('Error loading config.json:', error)); - } + + }//end onload function clear_loopLogs(){ diff --git a/html/saraR4.html b/html/saraR4.html index f779680..4bf5ec3 100755 --- a/html/saraR4.html +++ b/html/saraR4.html @@ -59,11 +59,12 @@ +
@@ -71,7 +72,7 @@

General information.

- +
@@ -84,7 +85,7 @@

SIM card information.

- +
@@ -109,7 +110,7 @@

Signal strength

- +
@@ -121,7 +122,7 @@

Modem Reset

- +
@@ -304,7 +305,20 @@
- + + + +
+ +
+
@@ -317,35 +331,62 @@ diff --git a/html/sensors.html b/html/sensors.html index dbcd3e9..9484850 100755 --- a/html/sensors.html +++ b/html/sensors.html @@ -144,40 +144,50 @@ function getNPM_values(port){ } function getENVEA_values(port, name){ - console.log("Data from Envea "+ name+" (port "+port+"):"); - $("#loading_envea"+name).show(); + console.log("Data from Envea " + name + " (port " + port + "):"); + $("#loading_envea" + name).show(); - $.ajax({ - url: 'launcher.php?type=envea&port='+port+'&name='+name, - dataType: 'json', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log(response); - const tableBody = document.getElementById("data-table-body_envea"+name); - tableBody.innerHTML = ""; + $.ajax({ + url: 'launcher.php?type=envea&port=' + port + '&name=' + name, + dataType: 'json', + method: 'GET', + success: function(response) { + console.log(response); + const tableBody = document.getElementById("data-table-body_envea" + name); + tableBody.innerHTML = ""; - $("#loading_envea"+name).hide(); - // Create an array of the desired keys - // Create an array of the desired keys - const keysToShow = [name]; - // Add only the specified elements to the table - keysToShow.forEach(key => { - if (response !== undefined) { // Check if the key exists in the response - const value = response; - $("#data-table-body_envea"+name).append(` - - ${key} - ${value} ppb - - `); - } - }); - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } + $("#loading_envea" + name).hide(); + + const keysToShow = [name]; + keysToShow.forEach(key => { + if (response !== undefined) { + const value = response; + $("#data-table-body_envea" + name).append(` + + ${key} + ${value} ppb + + `); + } }); + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + const tableBody = document.getElementById("data-table-body_envea" + name); + $("#loading_envea" + name).hide(); + + tableBody.innerHTML = ` + + + ❌ Error: unable to get data from sensor.
+ ${status}: ${error} + + + `; } + }); +} + function getNoise_values(){ console.log("Data from I2C Noise Sensor:"); @@ -261,143 +271,190 @@ function getBME280_values(){ window.onload = function() { - fetch('../config.json') // Replace 'deviceID.txt' with 'config.json' - .then(response => response.json()) // Parse response as JSON - .then(data => { - //get device ID - const deviceID = data.deviceID.trim().toUpperCase(); - //document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID; - //get device Name - const deviceName = data.deviceName; - - const elements = document.querySelectorAll('.sideBar_sensorName'); - elements.forEach((element) => { - element.innerText = deviceName; - }); + + //NEW way to get config (SQLite) +$.ajax({ + url: 'launcher.php?type=get_config_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config table:"); + console.log(response); + + + //device name_side bar + const elements = document.querySelectorAll('.sideBar_sensorName'); + elements.forEach((element) => { + element.innerText = response.deviceName; + }); + + }, +error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); +} + });//end AJAX + + //getting config_scripts table + $.ajax({ + url: 'launcher.php?type=get_config_scripts_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Getting SQLite config scripts table:"); + console.log(response); + + const container = document.getElementById('card-container'); // Conteneur des cartes + + //creates NPM card + if (response["NPM/get_data_modbus_v3.py"]) { + const cardHTML = ` +
+
+
+ Port UART +
+
+
NextPM
+

Capteur particules fines.

+ +
+ + + +
+
+
+
`; + + container.innerHTML += cardHTML; // Add the I2C card if condition is met + } + + //creates i2c BME280 card + if (response["BME280/get_data_v2.py"]) { + const i2C_BME_HTML = ` +
+
+
+ Port I2C +
+
+
BME280 Temp/Hum sensor
+

Capteur température et humidité sur le port I2C.

+ +
+ + + +
+
+
+
`; + + container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met + } + + //creates i2c sound card + if (response.i2C_sound) { + const i2C_HTML = ` +
+
+
+ Port I2C +
+
+
Decibel Meter
+

Capteur bruit sur le port I2C.

+ +
+ + + + + +
+
+
+
`; + + container.innerHTML += i2C_HTML; // Add the I2C card if condition is met + } + + //Si on a des SONDES ENVEA connectée il faut faire un deuxième call dans la table envea_sondes_table + //creates ENVEA cards + if (response["envea/read_value_v2.py"]) { + console.log("Need to display ENVEA sondes"); + //getting config_scripts table + $.ajax({ + url: 'launcher.php?type=get_envea_sondes_table_sqlite', + dataType:'json', + //dataType: 'json', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(sondes) { + console.log("Getting SQLite envea sondes table:"); + console.log(sondes); + const ENVEA_sensors = sondes.filter(sonde => sonde.connected); // Filter only connected sondes + + ENVEA_sensors.forEach((sensor, index) => { + const port = sensor.port; // Port from the sensor object + const name = sensor.name; // Port from the sensor object + const coefficient = sensor.coefficient; + const cardHTML = ` +
+
+
+ Port UART ${port.replace('ttyAMA', '')} +
+
+
Sonde Envea ${name}
+

Capteur gas.

+ + + + +
+
+
+
`; + container.innerHTML += cardHTML; // Ajouter la carte au conteneur + }); + + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX envea Sondes - //get local RTC - $.ajax({ - url: 'launcher.php?type=RTC_time', - dataType: 'text', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log("Local RTC: " + response); - const RTC_Element = document.getElementById("RTC_time"); - RTC_Element.textContent = response; - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } - }); + }//end if + - const container = document.getElementById('card-container'); // Conteneur des cartes - - //creates NPM cards - const NPM_ports = data.NextPM_ports; // Récupère les ports - NPM_ports.forEach((port, index) => { - const cardHTML = ` -
-
-
- Port UART ${port.replace('ttyAMA', '')} -
-
-
NextPM ${String.fromCharCode(65 + index)}
-

Capteur particules fines.

- - - - -
-
-
-
`; - container.innerHTML += cardHTML; // Ajouter la carte au conteneur - }); +}, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + });//end AJAX (config_scripts) - //creates ENVEA cards - const ENVEA_sensors = data.envea_sondes.filter(sonde => sonde.connected); // Filter only connected sondes + //get local RTC + $.ajax({ + url: 'launcher.php?type=RTC_time', + dataType: 'text', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Local RTC: " + response); + const RTC_Element = document.getElementById("RTC_time"); + RTC_Element.textContent = response; - ENVEA_sensors.forEach((sensor, index) => { - const port = sensor.port; // Port from the sensor object - const name = sensor.name; // Port from the sensor object - const coefficient = sensor.coefficient; - const cardHTML = ` -
-
-
- Port UART ${port.replace('ttyAMA', '')} -
-
-
Sonde Envea ${name}
-

Capteur gas.

- - - - -
-
-
-
`; - container.innerHTML += cardHTML; // Ajouter la carte au conteneur - }); + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); - //creates i2c BME280 card - if (data["BME280/get_data_v2.py"]) { - const i2C_BME_HTML = ` -
-
-
- Port I2C -
-
-
BME280 Temp/Hum sensor
-

Capteur température et humidité sur le port I2C.

- -
- - - -
-
-
-
`; - - container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met - } - - //creates i2c sound card - if (data.i2C_sound) { - const i2C_HTML = ` -
-
-
- Port I2C -
-
-
Decibel Meter
-

Capteur bruit sur le port I2C.

- -
- - - - - -
-
-
-
`; - - container.innerHTML += i2C_HTML; // Add the I2C card if condition is met - } - - }) - .catch(error => console.error('Error loading config.json:', error)); - } +} //end windows onload diff --git a/installation_part1.sh b/installation_part1.sh index 8935d60..400912a 100644 --- a/installation_part1.sh +++ b/installation_part1.sh @@ -23,7 +23,7 @@ fi # Update and install necessary packages info "Updating package list and installing necessary packages..." -sudo apt update && sudo apt install -y git gh apache2 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus || error "Failed to install required packages." +sudo apt update && sudo apt install -y git gh apache2 sqlite3 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus || error "Failed to install required packages." # Install Python libraries info "Installing Python libraries..." diff --git a/loop/SARA_send_data_v2.py b/loop/SARA_send_data_v2.py index a0fbf2c..e14a140 100755 --- a/loop/SARA_send_data_v2.py +++ b/loop/SARA_send_data_v2.py @@ -165,65 +165,84 @@ def blink_led(pin, blink_count, delay=1): GPIO.output(pin, GPIO.LOW) # Ensure LED is off print(f"LED on GPIO {pin} turned OFF (cleanup avoided)") -#get data from config -def load_config(config_file): +#get config data from SQLite table +def load_config_sqlite(): + """ + Load configuration data from SQLite config table + + Returns: + dict: Configuration data with proper type conversion + """ try: - with open(config_file, 'r') as file: - config_data = json.load(file) + + # Query the config table + cursor.execute("SELECT key, value, type FROM config_table") + rows = cursor.fetchall() + + # Create config dictionary + config_data = {} + for key, value, type_name in rows: + # Convert value based on its type + if type_name == 'bool': + config_data[key] = value == '1' or value == 'true' + elif type_name == 'int': + config_data[key] = int(value) + elif type_name == 'float': + config_data[key] = float(value) + else: + config_data[key] = value + return config_data + except Exception as e: - print(f"Error loading config file: {e}") + print(f"Error loading config from SQLite: {e}") return {} -#Fonction pour mettre à jour le JSON de configuration -def update_json_key(file_path, key, value): +def load_config_scripts_sqlite(): """ - Updates a specific key in a JSON file with a new value. - - :param file_path: Path to the JSON file. - :param key: The key to update in the JSON file. - :param value: The new value to assign to the key. + Load script configuration data from SQLite config_scripts_table + + Returns: + dict: Script paths as keys and enabled status as boolean values """ try: - # Load the existing data - with open(file_path, "r") as file: - data = json.load(file) + # Query the config_scripts_table + cursor.execute("SELECT script_path, enabled FROM config_scripts_table") + rows = cursor.fetchall() - # Check if the key exists in the JSON file - if key in data: - data[key] = value # Update the key with the new value - else: - print(f"Key '{key}' not found in the JSON file.") - return + # Create config dictionary with script paths as keys and enabled status as boolean values + scripts_config = {} + for script_path, enabled in rows: + # Convert integer enabled value (0/1) to boolean + scripts_config[script_path] = bool(enabled) - # Write the updated data back to the file - with open(file_path, "w") as file: - json.dump(data, file, indent=2) # Use indent for pretty printing + return scripts_config - print(f"💾updating '{key}' to '{value}'.") except Exception as e: - print(f"Error updating the JSON file: {e}") + print(f"Error loading scripts config from SQLite: {e}") + return {} -# Define the config file path -config_file = '/var/www/nebuleair_pro_4g/config.json' - -# Load the configuration data -config = load_config(config_file) +#Load config +config = load_config_sqlite() +#config +device_id = config.get('deviceID', 'unknown') +device_id = device_id.upper() +modem_config_mode = config.get('modem_config_mode', False) device_latitude_raw = config.get('latitude_raw', 0) device_longitude_raw = config.get('longitude_raw', 0) -baudrate = config.get('SaraR4_baudrate', 115200) #baudrate du sara R4 -device_id = config.get('deviceID', '').upper() #device ID en maj -bme_280_config = config.get('BME280/get_data_v2.py', False) #présence du BME280 -envea_cairsens= config.get('envea/read_value_v2.py', False) -mppt_charger= config.get('MPPT/read.py', False) -wind_meter= config.get('windMeter/read.py', False) -send_aircarto = config.get('send_aircarto', True) #envoi sur AirCarto (data.nebuleair.fr) +modem_version=config.get('modem_version', "") +Sara_baudrate = config.get('SaraR4_baudrate', 115200) +npm_5channel = config.get('NextPM_5channels', False) #5 canaux du NPM +selected_networkID = int(config.get('SARA_R4_neworkID', 0)) send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot () reset_uSpot_url = False -selected_networkID = int(config.get('SARA_R4_neworkID', 0)) -npm_5channel = config.get('NextPM_5channels', False) #5 canaux du NPM -modem_version=config.get('modem_version', "") -modem_config_mode = config.get('modem_config_mode', False) #modem 4G en mode configuration + +#config_scripts +config_scripts = load_config_scripts_sqlite() +bme_280_config = config_scripts.get('BME280/get_data_v2.py', False) +envea_cairsens= config_scripts.get('envea/read_value_v2.py', False) +mppt_charger= config_scripts.get('MPPT/read.py', False) +wind_meter= config_scripts.get('windMeter/read.py', False) #update device id in the payload json payload_json["nebuleairid"] = device_id @@ -235,7 +254,7 @@ if modem_config_mode: ser_sara = serial.Serial( port='/dev/ttyAMA2', - baudrate=baudrate, #115200 ou 9600 + baudrate=Sara_baudrate, #115200 ou 9600 parity=serial.PARITY_NONE, #PARITY_NONE, PARITY_EVEN or PARITY_ODD stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, @@ -601,9 +620,9 @@ try: payload_csv[10] = averages[1] # envea_h2s payload_csv[11] = averages[2] # envea_nh3 - #Add data to payload JSON + #Add data to payload JSON payload_json["sensordatavalues"].append({"value_type": "CAIRSENS_NO2", "value": str(averages[0])}) - payload_json["sensordatavalues"].append({"value_type": "CAIRSENS_NO2", "value": str(averages[1])}) + payload_json["sensordatavalues"].append({"value_type": "CAIRSENS_H2S", "value": str(averages[1])}) payload_json["sensordatavalues"].append({"value_type": "CAIRSENS_NH3", "value": str(averages[2])}) #Wind meter @@ -768,8 +787,6 @@ try: print('ATTENTION: CME ERROR') print("error:", lines[-1]) print("*****") - #update status - update_json_key(config_file, "SARA_R4_network_status", "disconnected") # Gestion de l'erreur spécifique if "No connection to phone" in lines[-1]: @@ -808,7 +825,6 @@ try: if len(parts) == 3 and parts[-1] == '0': # The third value indicates success print("*****") print('⛔ATTENTION: HTTP operation failed') - update_json_key(config_file, "SARA_R4_network_status", "disconnected") print("*****") print("Blink red LED") # Run LED blinking in a separate thread @@ -855,7 +871,6 @@ try: # 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅) else: print('✅✅HTTP operation successful.') - update_json_key(config_file, "SARA_R4_network_status", "connected") print("Blink blue LED") led_thread = Thread(target=blink_led, args=(23, 5, 0.5)) led_thread.start() @@ -1122,7 +1137,6 @@ try: print("error:", lines[-1]) print("*****") #update status - #update_json_key(config_file, "SARA_R4_network_status", "disconnected") # Gestion de l'erreur spécifique if "No connection to phone" in lines[-1]: @@ -1148,7 +1162,6 @@ try: if len(parts) == 3 and parts[-1] == '0': # The third value indicates success print("*****") print('⛔ATTENTION: HTTP operation failed') - update_json_key(config_file, "SARA_R4_network_status", "disconnected") print("*****") print("Blink red LED") # Run LED blinking in a separate thread @@ -1188,7 +1201,6 @@ try: else: # Si la commande HTTP a réussi print('✅✅HTTP operation successful.') - update_json_key(config_file, "SARA_R4_network_status", "connected") print("Blink blue LED") led_thread = Thread(target=blink_led, args=(23, 5, 0.5)) led_thread.start() diff --git a/master.py b/master.py index 96305b3..0cb008a 100755 --- a/master.py +++ b/master.py @@ -52,17 +52,73 @@ Specific scripts can be disabled with config.json import time import threading import subprocess -import json import os +import sqlite3 + # Base directory where scripts are stored SCRIPT_DIR = "/var/www/nebuleair_pro_4g/" -CONFIG_FILE = "/var/www/nebuleair_pro_4g/config.json" +DB_PATH = "/var/www/nebuleair_pro_4g/sqlite/sensors.db" + +# Lock file path for SARA script +SARA_LOCK_FILE = "/var/www/nebuleair_pro_4g/sara_script.lock" + + +def is_script_enabled(script_name): + """Check if a script is enabled in the database.""" + try: + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + cursor.execute( + "SELECT enabled FROM config_scripts_table WHERE script_path = ?", + (script_name,) + ) + + result = cursor.fetchone() + conn.close() + + if result is None: + return True # Default to enabled if not found in database + + return bool(result[0]) + except Exception: + # If any database error occurs, default to enabled + return True + + +def create_lock_file(): + """Create a lock file for the SARA script.""" + with open(SARA_LOCK_FILE, 'w') as f: + f.write(str(int(time.time()))) + + +def remove_lock_file(): + """Remove the SARA script lock file.""" + if os.path.exists(SARA_LOCK_FILE): + os.remove(SARA_LOCK_FILE) + + +def is_script_locked(): + """Check if the SARA script is currently locked.""" + if not os.path.exists(SARA_LOCK_FILE): + return False + + # Check if lock is older than 60 seconds (stale) + with open(SARA_LOCK_FILE, 'r') as f: + try: + lock_time = int(f.read().strip()) + if time.time() - lock_time > 60: + # Lock is stale, remove it + remove_lock_file() + return False + except ValueError: + # Invalid lock file content + remove_lock_file() + return False + + return True -def load_config(): - """Load the configuration file to determine which scripts to run.""" - with open(CONFIG_FILE, "r") as f: - return json.load(f) def run_script(script_name, interval, delay=0): """Run a script in a synchronized loop with an optional start delay.""" @@ -70,33 +126,41 @@ def run_script(script_name, interval, delay=0): next_run = time.monotonic() + delay # Apply the initial delay while True: - config = load_config() - if config.get(script_name, True): # Default to True if not found - subprocess.run(["python3", script_path]) + if is_script_enabled(script_name): + # Special handling for SARA script to prevent concurrent runs + if script_name == "loop/SARA_send_data_v2.py": + if not is_script_locked(): + create_lock_file() + try: + subprocess.run(["python3", script_path]) + finally: + remove_lock_file() + else: + # Run other scripts normally + subprocess.run(["python3", script_path]) # Wait until the next exact interval next_run += interval sleep_time = max(0, next_run - time.monotonic()) # Prevent negative sleep times time.sleep(sleep_time) + # Define scripts and their execution intervals (seconds) SCRIPTS = [ - #("RTC/save_to_db.py", 1, 0), # --> will run as a separated system service (rtc_save_to_db.service) - ("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s, with 2s delay - ("envea/read_value_v2.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s, with 2s delay - ("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 2s delay - ("BME280/get_data_v2.py", 120, 0), # Get BME280 data every 120 seconds, no delay - ("MPPT/read.py", 120, 0), # Get MPPT data every 120 seconds, no delay - #("windMeter/read.py", 60, 2), # --> will run as a separated system service () - ("sqlite/flush_old_data.py", 86400, 0) # flush old data inside db every day () + # Format: (script_name, interval_in_seconds, start_delay_in_seconds) + ("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s + ("envea/read_value_v2.py", 10, 0), # Get Envea data every 10s + ("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 1s delay + ("BME280/get_data_v2.py", 120, 0), # Get BME280 data every 120 seconds + ("MPPT/read.py", 120, 0), # Get MPPT data every 120 seconds + ("sqlite/flush_old_data.py", 86400, 0) # Flush old data inside db every day ] -# Start threads for enabled scripts +# Start threads for scripts for script_name, interval, delay in SCRIPTS: thread = threading.Thread(target=run_script, args=(script_name, interval, delay), daemon=True) thread.start() # Keep the main script running while True: - time.sleep(1) - + time.sleep(1) \ No newline at end of file diff --git a/config.json.dist b/old/config.json.dist old mode 100755 new mode 100644 similarity index 100% rename from config.json.dist rename to old/config.json.dist diff --git a/install_software.yaml b/old/install_software.yaml old mode 100755 new mode 100644 similarity index 100% rename from install_software.yaml rename to old/install_software.yaml diff --git a/sqlite/create_db.py b/sqlite/create_db.py index 22b8553..0b430ec 100755 --- a/sqlite/create_db.py +++ b/sqlite/create_db.py @@ -18,6 +18,35 @@ import sqlite3 conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() +#create a config table +cursor.execute(""" +CREATE TABLE IF NOT EXISTS config_table ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL, + type TEXT NOT NULL +) +""") + +#creates a config_scripts table +cursor.execute(''' +CREATE TABLE IF NOT EXISTS config_scripts_table ( + script_path TEXT PRIMARY KEY, + enabled INTEGER NOT NULL +) +''') + +#creates a config table for envea sondes +cursor.execute(""" +CREATE TABLE IF NOT EXISTS envea_sondes_table ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + connected INTEGER NOT NULL, + port TEXT NOT NULL, + name TEXT NOT NULL, + coefficient REAL NOT NULL +) +""") + + # Create a table timer cursor.execute(""" CREATE TABLE IF NOT EXISTS timestamp_table ( diff --git a/sqlite/read.py b/sqlite/read.py index e75713b..c5a47ea 100755 --- a/sqlite/read.py +++ b/sqlite/read.py @@ -36,7 +36,6 @@ cursor = conn.cursor() #cursor.execute("SELECT * FROM timestamp_table") if table_name == "timestamp_table": cursor.execute("SELECT * FROM timestamp_table") - else: query = f"SELECT * FROM {table_name} ORDER BY timestamp DESC LIMIT ?" cursor.execute(query, (limit_num,)) diff --git a/sqlite/read_config.py b/sqlite/read_config.py new file mode 100644 index 0000000..bc7b2b7 --- /dev/null +++ b/sqlite/read_config.py @@ -0,0 +1,43 @@ +''' + ____ ___ _ _ _ + / ___| / _ \| | (_) |_ ___ + \___ \| | | | | | | __/ _ \ + ___) | |_| | |___| | || __/ + |____/ \__\_\_____|_|\__\___| + +Script to read data from a sqlite database +/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/read_config.py config_table + +Available table are +config_table +config_scripts_table +envea_sondes_table + +''' + +import sqlite3 +import sys +parameter = sys.argv[1:] # Exclude the script name +#print("Parameters received:") +table_name=parameter[0] + + +# Connect to the SQLite database +conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") +cursor = conn.cursor() + +# Retrieve the data +query = f"SELECT * FROM {table_name}" +cursor.execute(query) + + +rows = cursor.fetchall() +rows.reverse() # Reverse the order in Python (to get ascending order) + + +# Display the results +for row in rows: + print(row) + +# Close the database connection +conn.close() diff --git a/sqlite/set_config.py b/sqlite/set_config.py new file mode 100644 index 0000000..2a9460c --- /dev/null +++ b/sqlite/set_config.py @@ -0,0 +1,95 @@ +''' + ____ ___ _ _ _ + / ___| / _ \| | (_) |_ ___ + \___ \| | | | | | | __/ _ \ + ___) | |_| | |___| | || __/ + |____/ \__\_\_____|_|\__\___| + +Script to set the config +/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/set_config.py + +in case of readonly error: +sudo chmod 777 /var/www/nebuleair_pro_4g/sqlite/sensors.db +''' + +import sqlite3 + +# Connect to (or create if not existent) the database +conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") +cursor = conn.cursor() + +print(f"Connected to database") + +# Clear existing data (if any) +cursor.execute("DELETE FROM config_table") +cursor.execute("DELETE FROM config_scripts_table") +cursor.execute("DELETE FROM envea_sondes_table") +print("Existing data cleared") + +#add values + +# Insert script configurations +script_configs = [ + ("NPM/get_data_modbus_v3.py", True), + ("loop/SARA_send_data_v2.py", True), + ("RTC/save_to_db.py", True), + ("BME280/get_data_v2.py", True), + ("envea/read_value_v2.py", False), + ("MPPT/read.py", False), + ("windMeter/read.py", False), + ("sqlite/flush_old_data.py", True) +] + +for script_path, enabled in script_configs: + cursor.execute( + "INSERT INTO config_scripts_table (script_path, enabled) VALUES (?, ?)", + (script_path, 1 if enabled else 0) + ) + +# Insert general configurations +config_entries = [ + ("modem_config_mode", "0", "bool"), + ("deviceID", "XXXX", "str"), + ("npm_5channel", "0", "bool"), + ("latitude_raw", "0", "int"), + ("longitude_raw", "0", "int"), + ("latitude_precision", "0", "int"), + ("longitude_precision", "0", "int"), + ("deviceName", "NebuleAir-proXXX", "str"), + ("SaraR4_baudrate", "115200", "int"), + ("NPM_solo_port", "/dev/ttyAMA5", "str"), + ("sshTunnel_port", "59228", "int"), + ("SARA_R4_general_status", "connected", "str"), + ("SARA_R4_SIM_status", "connected", "str"), + ("SARA_R4_network_status", "connected", "str"), + ("SARA_R4_neworkID", "20810", "int"), + ("WIFI_status", "connected", "str"), + ("send_uSpot", "0", "bool"), + ("modem_version", "XXX", "str") +] + +for key, value, value_type in config_entries: + cursor.execute( + "INSERT INTO config_table (key, value, type) VALUES (?, ?, ?)", + (key, value, value_type) + ) + +# Insert envea sondes +envea_sondes = [ + (False, "ttyAMA4", "h2s", 4), + (False, "ttyAMA3", "no2", 1), + (False, "ttyAMA2", "o3", 1) +] + +for connected, port, name, coefficient in envea_sondes: + cursor.execute( + "INSERT INTO envea_sondes_table (connected, port, name, coefficient) VALUES (?, ?, ?, ?)", + (1 if connected else 0, port, name, coefficient) + ) + + +# Commit and close the connection +conn.commit() +conn.close() + +print("Database updated successfully!")