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)
@@ -117,13 +128,6 @@
Clock
-
-
-
-
-
@@ -161,6 +165,22 @@
+
+
+
+
+
+
+
+
+ Hello, world! This is a toast message.
+
+
+
+
+
+
+
@@ -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 = `
-
- RTC and system time are in sync (Difference: ${timeDiff} sec).
-
');
+ }
+
+ // 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 @@
+