diff --git a/MH-Z19/write_data.py b/MH-Z19/write_data.py index f669560..ecab40b 100755 --- a/MH-Z19/write_data.py +++ b/MH-Z19/write_data.py @@ -24,20 +24,7 @@ import sqlite3 conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() -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 {} - - -# Load the configuration data -config_file = '/var/www/moduleair_pro_4g/config.json' -config = load_config(config_file) -mh_z19_port = config.get('MH-Z19_port', '') #port du NPM solo +mh_z19_port = "/dev/ttyAMA4" #port du NPM solo ser = serial.Serial( port=mh_z19_port, diff --git a/NPM/get_data_modbus_v3.py b/NPM/get_data_modbus_v3.py index b766141..bacbad2 100755 --- a/NPM/get_data_modbus_v3.py +++ b/NPM/get_data_modbus_v3.py @@ -42,19 +42,8 @@ import time conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() -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 {} +npm_solo_port = "/dev/ttyAMA3" #port du NPM solo -# Load the configuration data -config_file = '/var/www/moduleair_pro_4g/config.json' -config = load_config(config_file) -npm_solo_port = config.get('NPM_solo_port', '') #port du NPM solo #GET RTC TIME from SQlite cursor.execute("SELECT * FROM timestamp_table LIMIT 1") diff --git a/html/admin.html b/html/admin.html index aa6c47c..026dcd6 100755 --- a/html/admin.html +++ b/html/admin.html @@ -59,48 +59,9 @@
- - -
- - -
- -
- - -
- -
- - -
-
- +
@@ -108,6 +69,49 @@
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+ +
+ +
@@ -117,12 +121,6 @@

Clock

-
- - -
@@ -155,12 +153,25 @@

Updates

- -
+ + + + +
+ +
+ @@ -193,112 +204,248 @@ }); }); +//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; - //device ID - const device_ID = document.getElementById("device_ID"); - device_ID.value = data.deviceID.toUpperCase(); + }, + 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); - //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; + const checkbox_NPM = document.getElementById("check_NPM"); + const checkbox_bme = document.getElementById("check_bme280"); + const checkbox_CO2 = document.getElementById("check_CO2"); + const checkbox_SFA30 = document.getElementById("check_sensirionSFA30"); - // Get the time difference - const timeDiff = response.time_difference_seconds; + checkbox_NPM.checked = response["NPM/get_data_modbus_v3.py"]; + checkbox_bme.checked = response["BME280/get_data_v2.py"]; + checkbox_SFA30.checked = response["sensirion/SFA30_read.py"]; + checkbox_CO2.checked = response["MH-Z19/write_data.py"]; - // Reference to the alert container - const alertContainer = document.getElementById("alert_container"); + //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 - // Remove any previous alert - alertContainer.innerHTML = ""; +//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; - // 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 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); - } - }); +//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 - }) - .catch(error => console.error('Error loading config.json:', error)); - } +} //end windows on load + +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){ diff --git a/html/database.html b/html/database.html index 4eecb64..9a8c24f 100755 --- a/html/database.html +++ b/html/database.html @@ -151,42 +151,48 @@ - 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; - - const elements = document.querySelectorAll('.sideBar_sensorName'); - elements.forEach((element) => { - element.innerText = deviceName; - }); +window.onload = function() { + + //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)); - } + +} diff --git a/html/index.html b/html/index.html index 81c8716..570d3fc 100755 --- a/html/index.html +++ b/html/index.html @@ -135,296 +135,296 @@ 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; + //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 device Name - const deviceName = data.deviceName; - - const elements = document.querySelectorAll('.sideBar_sensorName'); - elements.forEach((element) => { - element.innerText = deviceName; - }); + + //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); - } - }); + //get database size + $.ajax({ + url: 'launcher.php?type=database_size', + 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); + + if (response.size_megabytes !== undefined) { + // Extract and format the size in MB + const databaseSizeMB = response.size_megabytes + " MB"; - //get database size - $.ajax({ - url: 'launcher.php?type=database_size', - 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); - - if (response.size_megabytes !== undefined) { - // Extract and format the size in MB - const databaseSizeMB = response.size_megabytes + " MB"; + // Update the HTML element with the database size + const databaseSizeElement = document.getElementById("database_size"); + databaseSizeElement.textContent = databaseSizeMB; - // Update the HTML element with the database size - const databaseSizeElement = document.getElementById("database_size"); - databaseSizeElement.textContent = databaseSizeMB; - - console.log("Database size:", databaseSizeMB); - } else if (response.error) { - // Handle errors from the PHP response - console.error("Error from server:", response.error); - } - }, - error: function(xhr, status, error) { - console.error('AJAX request failed:', status, error); - } - }); + console.log("Database size:", databaseSizeMB); + } else if (response.error) { + // Handle errors from the PHP response + console.error("Error from server:", response.error); + } + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); - //get disk free space - $.ajax({ - url: 'launcher.php?type=linux_disk', - dataType: 'text', // Specify that you expect a JSON response - method: 'GET', // Use GET or POST depending on your needs - success: function(response) { - console.log("Linux disk space: " + response); - //1. disk size - const disk_size = document.getElementById("disk_size"); - const firstNumber = response.match(/(? d.timestamp); - const PM1 = data.map(d => d.PM1); - const PM25 = data.map(d => d.PM25); - const PM10 = data.map(d => d.PM10); - - const ctx = document.getElementById('sensorPMChart').getContext('2d'); - - if (!chart) { - chart = new Chart(ctx, { - type: 'line', - data: { - labels: labels, - datasets: [ - { - label: "PM1", - data: PM1, - borderColor: "rgba(0, 51, 153, 1)", - backgroundColor: "rgba(0, 51, 153, 0.2)", // Very light blue background - fill: true, - tension: 0.4, // Smooth curves - pointRadius: 2, // Larger points - pointHoverRadius: 6 // Bigger hover points - }, - { - label: "PM2.5", - data: PM25, - borderColor: "rgba(30, 144, 255, 1)", - backgroundColor: "rgba(30, 144, 255, 0.2)", // Very light medium blue background - fill: true, - tension: 0.4, - pointRadius: 2, - pointHoverRadius: 6 - }, - { - label: "PM10", - data: PM10, - borderColor: "rgba(135, 206, 250, 1)", - backgroundColor: "rgba(135, 206, 250, 0.2)", // Very light blue background - fill: true, - tension: 0.4, - pointRadius: 2, - pointHoverRadius: 6 - } - ] - }, - options: { - responsive: true, - maintainAspectRatio: true, - plugins: { - legend: { - position: 'top' - } - }, - scales: { - x: { - title: { - display: true, - text: 'Time (UTC)', - font: { - size: 16, - family: 'Arial, sans-serif' - }, - color: '#4A4A4A' - }, - ticks: { - autoSkip: true, - maxTicksLimit: 5, - color: '#4A4A4A', - callback: function(value, index) { - // Access the correct label from the `labels` array - const label = labels[index]; // Use the original `labels` array - if (label && typeof label === 'string' && label.includes(' ')) { - return label.split(' ')[1].slice(0, 5); // Extract "HH:MM" - } - return value; // Fallback for invalid labels - } - }, - grid: { - display: false // Remove gridlines for a cleaner look - } - - - }, - y: { - title: { - display: true, - text: 'Values (µg/m³)', - font: { - size: 16, - family: 'Arial, sans-serif' - }, - color: '#4A4A4A' - } - } - } - - } - }); - } else { - chart.data.labels = labels; - chart.data.datasets[0].data = PM1; - chart.data.datasets[1].data = PM25; - chart.data.datasets[2].data = PM10; - chart.update(); + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); } - } + }); + + //get memory free space + $.ajax({ + url: 'launcher.php?type=linux_memory', + dataType: 'text', // Specify that you expect a JSON response + method: 'GET', // Use GET or POST depending on your needs + success: function(response) { + console.log("Linux memory space: " + response); + //1. memory size + const memory_size = document.getElementById("memory_size"); + const memorySpace = document.getElementById("memory_space"); + const memLine = response.match(/Mem:\s+(\d+\.?\d*)Mi\s+(\d+\.?\d*)Mi/); + const totalMemory = parseFloat(memLine[1]); // Total memory in MiB + const usedMemory = parseFloat(memLine[2]); // Used memory in MiB + + // Calculate the percentage + const percentageUsed = ((usedMemory / totalMemory) * 100).toFixed(2); + + console.log(totalMemory); + + memory_size.innerHTML = totalMemory; - //end fetch config - }) - .catch(error => console.error('Error loading config.json:', error)); - //end windows on load + console.log(usedMemory); + console.log(percentageUsed); + + // Create the outer div with class and attributes + const progressDiv = document.createElement('div'); + progressDiv.className = 'progress mb-3'; + progressDiv.setAttribute('role', 'progressbar'); + progressDiv.setAttribute('aria-label', 'Example with label'); + progressDiv.setAttribute('aria-valuenow', percentageUsed); + progressDiv.setAttribute('aria-valuemin', 0); + progressDiv.setAttribute('aria-valuemax', 100); + + // Create the inner progress bar div + const progressBarDiv = document.createElement('div'); + progressBarDiv.className = 'progress-bar'; + progressBarDiv.style.width = `${percentageUsed}%`; // Set the width dynamically + progressBarDiv.textContent = `${percentageUsed}%`; // Set the text dynamically + + // Append the progress bar to the outer div + progressDiv.appendChild(progressBarDiv); + + // Append the entire progress bar to the body (or any other container) + memorySpace.appendChild(progressDiv); + + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); + + + // GET NPM SQLite values + $.ajax({ + url: 'launcher.php?type=get_npm_sqlite_data', + 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); + updatePMChart(response); + }, + error: function(xhr, status, error) { + console.error('AJAX request failed:', status, error); + } + }); + + let chart; // Store the Chart.js instance globally + + function updatePMChart(data) { + const labels = data.map(d => d.timestamp); + const PM1 = data.map(d => d.PM1); + const PM25 = data.map(d => d.PM25); + const PM10 = data.map(d => d.PM10); + + const ctx = document.getElementById('sensorPMChart').getContext('2d'); + + if (!chart) { + chart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [ + { + label: "PM1", + data: PM1, + borderColor: "rgba(0, 51, 153, 1)", + backgroundColor: "rgba(0, 51, 153, 0.2)", // Very light blue background + fill: true, + tension: 0.4, // Smooth curves + pointRadius: 2, // Larger points + pointHoverRadius: 6 // Bigger hover points + }, + { + label: "PM2.5", + data: PM25, + borderColor: "rgba(30, 144, 255, 1)", + backgroundColor: "rgba(30, 144, 255, 0.2)", // Very light medium blue background + fill: true, + tension: 0.4, + pointRadius: 2, + pointHoverRadius: 6 + }, + { + label: "PM10", + data: PM10, + borderColor: "rgba(135, 206, 250, 1)", + backgroundColor: "rgba(135, 206, 250, 0.2)", // Very light blue background + fill: true, + tension: 0.4, + pointRadius: 2, + pointHoverRadius: 6 + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: true, + plugins: { + legend: { + position: 'top' + } + }, + scales: { + x: { + title: { + display: true, + text: 'Time (UTC)', + font: { + size: 16, + family: 'Arial, sans-serif' + }, + color: '#4A4A4A' + }, + ticks: { + autoSkip: true, + maxTicksLimit: 5, + color: '#4A4A4A', + callback: function(value, index) { + // Access the correct label from the `labels` array + const label = labels[index]; // Use the original `labels` array + if (label && typeof label === 'string' && label.includes(' ')) { + return label.split(' ')[1].slice(0, 5); // Extract "HH:MM" + } + return value; // Fallback for invalid labels + } + }, + grid: { + display: false // Remove gridlines for a cleaner look + } + + + }, + y: { + title: { + display: true, + text: 'Values (µg/m³)', + font: { + size: 16, + family: 'Arial, sans-serif' + }, + color: '#4A4A4A' + } + } + } + + } + }); + } else { + chart.data.labels = labels; + chart.data.datasets[0].data = PM1; + chart.data.datasets[1].data = PM25; + chart.data.datasets[2].data = PM10; + chart.update(); + } + } + + } diff --git a/html/launcher.php b/html/launcher.php index a3a9f1b..0f7c84d 100755 --- a/html/launcher.php +++ b/html/launcher.php @@ -4,10 +4,11 @@ header("Content-Type: application/json"); header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Pragma: no-cache"); +$database_path = "/var/www/moduleair_pro_4g/sqlite/sensors.db"; + $type=$_GET['type']; if ($type == "get_npm_sqlite_data") { - $database_path = "/var/www/moduleair_pro_4g/sqlite/sensors.db"; //echo "Getting data from sqlite database"; try { $db = new PDO("sqlite:$database_path"); @@ -25,6 +26,193 @@ if ($type == "get_npm_sqlite_data") { } } +//GETING data from config_table (SQLite DB) +if ($type == "get_config_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 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()]); + } + +} + +//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()]); + } +} + + +//old JSON way if ($type == "update_config") { echo "updating...."; $param=$_GET['param']; diff --git a/html/logs.html b/html/logs.html index 7cb126d..24107b3 100755 --- a/html/logs.html +++ b/html/logs.html @@ -56,7 +56,9 @@
- Master logs + Master logs + +
@@ -69,6 +71,8 @@
Boot logs + +
@@ -89,121 +93,157 @@ diff --git a/master.py b/master.py index 5a801dc..f0cf452 100755 --- a/master.py +++ b/master.py @@ -152,6 +152,7 @@ SCRIPTS = [ ("NPM/get_data_modbus_v3.py", 10, 0), # Get NPM data (modbus 5 channels) every 10s ("MH-Z19/write_data.py", 10, 0), # Get Envea data every 10s ("loop/SARA_send_data_v2.py", 60, 1), # Send data every 60 seconds, with 1s delay + ("sensirion/SFA30_read.py", 10, 0), # Send data every 60 seconds, with 1s delay ("sqlite/flush_old_data.py", 86400, 0) # Flush old data inside db every day ] diff --git a/sqlite/create_db.py b/sqlite/create_db.py index f9fab2d..e96346d 100755 --- a/sqlite/create_db.py +++ b/sqlite/create_db.py @@ -87,6 +87,14 @@ CREATE TABLE IF NOT EXISTS data_CO2 ( ) """) +# Create a table sensirion SFA30 +cursor.execute(""" +CREATE TABLE IF NOT EXISTS data_sensirionSFA30 ( + timestamp TEXT, + CH2O INTEGER +) +""") + # Create a table BME280 cursor.execute(""" CREATE TABLE IF NOT EXISTS data_BME280 ( diff --git a/sqlite/set_config.py b/sqlite/set_config.py index 71eea5b..4b1b485 100644 --- a/sqlite/set_config.py +++ b/sqlite/set_config.py @@ -36,7 +36,7 @@ script_configs = [ ("BME280/get_data_v2.py", False), ("envea/read_value_v2.py", False), ("MH-Z19/write_data.py", True), - ("windMeter/read.py", False), + ("sensirion/SFA30_read.py", False), ("sqlite/flush_old_data.py", True) ]