This commit is contained in:
Your Name
2025-03-25 14:55:50 +01:00
parent 6312cd8d72
commit 2129d45ef6
8 changed files with 812 additions and 240 deletions

View File

@@ -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)

View File

@@ -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"
@@ -30,8 +32,7 @@ done
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';"
@@ -39,8 +40,8 @@ sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET val
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
@@ -58,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"

View File

@@ -114,6 +114,10 @@
</div>
<div class="input-group mb-3" id="sondes_envea_div">
</div>
<!--<button type="submit" class="btn btn-primary">Submit</button>-->
@@ -212,6 +216,16 @@
//end document.addEventListener
/*
___ _ _
/ _ \ _ __ | | ___ __ _ __| |
| | | | '_ \| | / _ \ / _` |/ _` |
| |_| | | | | |__| (_) | (_| | (_| |
\___/|_| |_|_____\___/ \__,_|\__,_|
*/
window.onload = function() {
//NEW way to get config (SQLite)
@@ -267,6 +281,12 @@ window.onload = function() {
checkbox_envea.checked = response["envea/read_value_v2.py"];
checkbox_solar.checked = response["MPPT/read.py"];
checkbox_wind.checked = response["windMeter/read.py"];
//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);
@@ -368,6 +388,10 @@ window.onload = function() {
} //end window.onload
function update_config_sqlite(param, value){
console.log("Updating sqlite ",param," : ", value);
@@ -445,6 +469,12 @@ function update_config_scripts_sqlite(param, value) {
Value: ${response.enabled !== undefined ? response.enabled : value}<br>
${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');
@@ -558,6 +588,312 @@ function set_RTC_withBrowser(){
}); //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('<div id="sondes_envea_div" class="input-group mt-4 border p-3 rounded"><legend>Sondes Envea</legend></div>');
} else {
// Clear existing content if container exists
$('#sondes_envea_div').html('<legend>Sondes Envea</legend>');
}
// 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 = `
<div class="input-group mb-3" id="${sondeId}_container">
<div class="input-group-text">
<input class="form-check-input mt-0" type="checkbox" id="${sondeId}_enabled"
${sonde.connected ? 'checked' : ''}
onchange="updateSondeStatus(${sonde.id}, this.checked)">
</div>
<input type="text" class="form-control" placeholder="Name" value="${sonde.name}"
id="${sondeId}_name" onchange="updateSondeName(${sonde.id}, this.value)">
<input type="text" class="form-control" placeholder="Port" value="${sonde.port}"
id="${sondeId}_port" onchange="updateSondePort(${sonde.id}, this.value)">
<input type="number" class="form-control" placeholder="Coefficient" value="${sonde.coefficient}"
id="${sondeId}_coefficient" onchange="updateSondeCoefficient(${sonde.id}, this.value)">
</div>
`;
// 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 = `
<strong>Success!</strong><br>
Sonde ID: ${response.id}<br>
Connected: ${connected ? "Yes" : "No"}<br>
${response.message || ''}
`;
} else {
// Error message
toastLiveExample.classList.remove('text-bg-success');
toastLiveExample.classList.add('text-bg-danger');
formattedMessage = `
<strong>Error!</strong><br>
${response.error || 'Unknown error'}<br>
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 = `
<strong>Request Failed!</strong><br>
Error: ${error}<br>
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 = `
<strong>Success!</strong><br>
Sonde ID: ${response.id}<br>
Name: ${name}<br>
${response.message || ''}
`;
} else {
// Error message
toastLiveExample.classList.remove('text-bg-success');
toastLiveExample.classList.add('text-bg-danger');
formattedMessage = `
<strong>Error!</strong><br>
${response.error || 'Unknown error'}<br>
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 = `
<strong>Request Failed!</strong><br>
Error: ${error}<br>
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 = `
<strong>Success!</strong><br>
Sonde ID: ${response.id}<br>
Port: ${port}<br>
${response.message || ''}
`;
} else {
// Error message
toastLiveExample.classList.remove('text-bg-success');
toastLiveExample.classList.add('text-bg-danger');
formattedMessage = `
<strong>Error!</strong><br>
${response.error || 'Unknown error'}<br>
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 = `
<strong>Request Failed!</strong><br>
Error: ${error}<br>
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 = `
<strong>Success!</strong><br>
Sonde ID: ${response.id}<br>
Coefficient: ${coefficient}<br>
${response.message || ''}
`;
} else {
// Error message
toastLiveExample.classList.remove('text-bg-success');
toastLiveExample.classList.add('text-bg-danger');
formattedMessage = `
<strong>Error!</strong><br>
${response.error || 'Unknown error'}<br>
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 = `
<strong>Request Failed!</strong><br>
Error: ${error}<br>
Sonde ID: ${id}
`;
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
toastBootstrap.show();
}
});
}
</script>

View File

@@ -28,6 +28,9 @@ if ($type == "get_npm_sqlite_data") {
}
}
/*
*/
//GETING data from config_table (SQLite DB)
if ($type == "get_config_sqlite") {
try {
@@ -74,6 +77,9 @@ if ($type == "get_config_sqlite") {
}
}
/*
*/
//GETING data from config_scrips_table (SQLite DB)
if ($type == "get_config_scripts_sqlite") {
@@ -107,6 +113,51 @@ if ($type == "get_config_scripts_sqlite") {
}
/*
*/
//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") {
@@ -214,6 +265,87 @@ if ($type == "update_config_scripts_sqlite") {
}
}
//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.... ";

View File

@@ -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(`
<tr>
<td>${key}</td>
<td>${value} ppb</td>
</tr>
`);
}
});
},
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(`
<tr>
<td>${key}</td>
<td>${value} ppb</td>
</tr>
`);
}
});
},
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 = `
<tr>
<td colspan="2" class="text-danger">
❌ Error: unable to get data from sensor.<br>
<small>${status}: ${error}</small>
</td>
</tr>
`;
}
});
}
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port UART
</div>
<div class="card-body">
<h5 class="card-title">NextPM</h5>
<p class="card-text">Capteur particules fines.</p>
<button class="btn btn-primary" onclick="getNPM_values('ttyAMA5')">Get Data</button>
<br>
<div id="loading_ttyAMA5" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_ttyAMA5"></tbody>
</table>
</div>
</div>
</div>`;
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port I2C
</div>
<div class="card-body">
<h5 class="card-title">BME280 Temp/Hum sensor</h5>
<p class="card-text">Capteur température et humidité sur le port I2C.</p>
<button class="btn btn-primary mb-1" onclick="getBME280_values()">Get Data</button>
<br>
<div id="loading_BME280" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_BME280"></tbody>
</table>
</div>
</div>
</div>`;
container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met
}
//creates i2c sound card
if (response.i2C_sound) {
const i2C_HTML = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port I2C
</div>
<div class="card-body">
<h5 class="card-title">Decibel Meter</h5>
<p class="card-text">Capteur bruit sur le port I2C.</p>
<button class="btn btn-primary mb-1" onclick="getNoise_values()">Get Data</button>
<br>
<button class="btn btn-success" onclick="startNoise()">Start recording</button>
<button class="btn btn-danger" onclick="stopNoise()">Stop recording</button>
<div id="loading_noise" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_noise"></tbody>
</table>
</div>
</div>
</div>`;
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port UART ${port.replace('ttyAMA', '')}
</div>
<div class="card-body">
<h5 class="card-title">Sonde Envea ${name}</h5>
<p class="card-text">Capteur gas.</p>
<button class="btn btn-primary" onclick="getENVEA_values('${port}','${name}')">Get Data</button>
<div id="loading_envea${name}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_envea${name}"></tbody>
</table>
</div>
</div>
</div>`;
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port UART ${port.replace('ttyAMA', '')}
</div>
<div class="card-body">
<h5 class="card-title">NextPM ${String.fromCharCode(65 + index)}</h5>
<p class="card-text">Capteur particules fines.</p>
<button class="btn btn-primary" onclick="getNPM_values('${port}')">Get Data</button>
<div id="loading_${port}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_${port}"></tbody>
</table>
</div>
</div>
</div>`;
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port UART ${port.replace('ttyAMA', '')}
</div>
<div class="card-body">
<h5 class="card-title">Sonde Envea ${name}</h5>
<p class="card-text">Capteur gas.</p>
<button class="btn btn-primary" onclick="getENVEA_values('${port}','${name}','${coefficient}')">Get Data</button>
<div id="loading_envea${name}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_envea${name}"></tbody>
</table>
</div>
</div>
</div>`;
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 = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port I2C
</div>
<div class="card-body">
<h5 class="card-title">BME280 Temp/Hum sensor</h5>
<p class="card-text">Capteur température et humidité sur le port I2C.</p>
<button class="btn btn-primary mb-1" onclick="getBME280_values()">Get Data</button>
<br>
<div id="loading_BME280" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_BME280"></tbody>
</table>
</div>
</div>
</div>`;
container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met
}
//creates i2c sound card
if (data.i2C_sound) {
const i2C_HTML = `
<div class="col-sm-3">
<div class="card">
<div class="card-header">
Port I2C
</div>
<div class="card-body">
<h5 class="card-title">Decibel Meter</h5>
<p class="card-text">Capteur bruit sur le port I2C.</p>
<button class="btn btn-primary mb-1" onclick="getNoise_values()">Get Data</button>
<br>
<button class="btn btn-success" onclick="startNoise()">Start recording</button>
<button class="btn btn-danger" onclick="stopNoise()">Stop recording</button>
<div id="loading_noise" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
<table class="table table-striped-columns">
<tbody id="data-table-body_noise"></tbody>
</table>
</div>
</div>
</div>`;
container.innerHTML += i2C_HTML; // Add the I2C card if condition is met
}
})
.catch(error => console.error('Error loading config.json:', error));
}
} //end windows onload
</script>
</body>

View File

@@ -165,16 +165,6 @@ 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 (from JSON file)
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 {}
#get config data from SQLite table
def load_config_sqlite():
"""
@@ -208,60 +198,51 @@ def load_config_sqlite():
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 (JSON way)
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)
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
#Load config new way
config = load_config_sqlite()
#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
@@ -273,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,
@@ -767,8 +748,6 @@ try:
print('<span style="color: red;font-weight: bold;">ATTENTION: CME ERROR</span>')
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]:
@@ -807,7 +786,6 @@ try:
if len(parts) == 3 and parts[-1] == '0': # The third value indicates success
print("*****")
print('<span style="color: red;font-weight: bold;">⛔ATTENTION: HTTP operation failed</span>')
update_json_key(config_file, "SARA_R4_network_status", "disconnected")
print("*****")
print("Blink red LED")
# Run LED blinking in a separate thread
@@ -854,7 +832,6 @@ try:
# 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅)
else:
print('<span style="font-weight: bold;">✅✅HTTP operation successful.</span>')
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()
@@ -1118,7 +1095,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]:
@@ -1144,7 +1120,6 @@ try:
if len(parts) == 3 and parts[-1] == '0': # The third value indicates success
print("*****")
print('<span style="color: red;font-weight: bold;">⛔ATTENTION: HTTP operation failed</span>')
update_json_key(config_file, "SARA_R4_network_status", "disconnected")
print("*****")
print("Blink red LED")
# Run LED blinking in a separate thread
@@ -1184,7 +1159,6 @@ try:
else:
# Si la commande HTTP a réussi
print('<span style="font-weight: bold;">✅✅HTTP operation successful.</span>')
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()

0
config.json.dist → old/config.json.dist Executable file → Normal file
View File

0
install_software.yaml → old/install_software.yaml Executable file → Normal file
View File