diff --git a/README.md b/README.md
index 185c745..a21a3d3 100755
--- a/README.md
+++ b/README.md
@@ -60,6 +60,7 @@ www-data ALL=(ALL) NOPASSWD: /usr/bin/git pull
www-data ALL=(ALL) NOPASSWD: /usr/bin/ssh
www-data ALL=(ALL) NOPASSWD: /usr/bin/python3 *
www-data ALL=(ALL) NOPASSWD: /bin/systemctl *
+www-data ALL=(ALL) NOPASSWD: /var/www/nebuleair_pro_4g/*
```
## Serial
diff --git a/html/admin.html b/html/admin.html
index b0df191..b9dd8e9 100755
--- a/html/admin.html
+++ b/html/admin.html
@@ -91,7 +91,7 @@
-
+
@@ -165,7 +165,34 @@
Updates
-
+
+
+
+
@@ -299,14 +326,20 @@ window.onload = function() {
const modem_version = document.getElementById("modem_version");
modem_version.value = response.modem_version;
- //nextPM send 5 channels
+
+
const checkbox_nmp5channels = document.getElementById("check_NPM_5channels");
- checkbox_nmp5channels.checked = response.npm_5channel;
- //windMeter (as a config not a script -> it's running with a systemd service)
const checkbox_wind = document.getElementById("check_WindMeter");
- checkbox_wind.checked = response["windMeter"];
- //send uSpot
const checkbox_uSpot = document.getElementById("check_uSpot");
+ const checkbox_bme = document.getElementById("check_bme280");
+ const checkbox_envea = document.getElementById("check_envea");
+ const checkbox_solar = document.getElementById("check_solarBattery");
+
+ checkbox_bme.checked = response["BME280"];
+ checkbox_envea.checked = response["envea"];
+ checkbox_solar.checked = response["MPPT"];
+ checkbox_nmp5channels.checked = response.npm_5channel;
+ checkbox_wind.checked = response["windMeter"];
checkbox_uSpot.checked = response["send_uSpot"];
},
@@ -315,37 +348,7 @@ window.onload = function() {
}
});//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 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");
-
- 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"];
-
-
- //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
+
//OLD way to get config (JSON)
@@ -516,28 +519,112 @@ function update_config(param, value){
});
}
-function updateGitPull(){
- console.log("Updating device (git pull)");
-
+function updateFirmware() {
+ console.log("Starting comprehensive firmware update...");
+
+ // Show loading state
+ const updateBtn = document.getElementById('updateBtn');
+ const updateBtnText = document.getElementById('updateBtnText');
+ const updateSpinner = document.getElementById('updateSpinner');
+ const updateOutput = document.getElementById('updateOutput');
+ const updateOutputContent = document.getElementById('updateOutputContent');
+
+ // Disable button and show spinner
+ updateBtn.disabled = true;
+ updateBtnText.textContent = 'Updating...';
+ updateSpinner.style.display = 'inline-block';
+
+ // Show output console
+ updateOutput.style.display = 'block';
+ updateOutputContent.textContent = 'Starting update process...\n';
+
$.ajax({
- url: 'launcher.php?type=git_pull',
- method: 'GET', // Use GET or POST depending on your needs
- dataType: 'text', // Specify that you expect a JSON response
-
+ url: 'launcher.php?type=update_firmware',
+ method: 'GET',
+ dataType: 'json',
+ timeout: 120000, // 2 minutes timeout
+
success: function(response) {
- // Handle success response if needed
- console.log(response);
- alert(response);
- // Reload the page after the device update
- location.reload(); // This will reload the page
-
+ console.log('Update completed:', response);
+
+ // Display formatted output
+ if (response.success && response.output) {
+ // Format the output for better readability
+ const formattedOutput = response.output
+ .replace(/\[\d{2}:\d{2}:\d{2}\]/g, function(match) {
+ return `
${match}`;
+ })
+ .replace(/✓/g, '
✓')
+ .replace(/✗/g, '
✗')
+ .replace(/⚠/g, '
⚠')
+ .replace(/ℹ/g, '
ℹ');
+
+ updateOutputContent.innerHTML = formattedOutput;
+
+ // Show success toast and reload button
+ showToast('Update completed successfully!', 'success');
+ document.getElementById('reloadBtn').style.display = 'inline-block';
+ } else {
+ updateOutputContent.textContent = 'Update completed but no output received.';
+ showToast('Update may have completed with issues', 'warning');
+ }
},
+
error: function(xhr, status, error) {
- console.error('AJAX request failed:', status, error);
+ console.error('Update failed:', status, error);
+ updateOutputContent.textContent = `Update failed: ${error}\n\nStatus: ${status}\nResponse: ${xhr.responseText || 'No response'}`;
+ showToast('Update failed! Check the output for details.', 'error');
+ },
+
+ complete: function() {
+ // Reset button state
+ updateBtn.disabled = false;
+ updateBtnText.textContent = 'Update firmware';
+ updateSpinner.style.display = 'none';
}
});
}
+function clearUpdateOutput() {
+ const updateOutput = document.getElementById('updateOutput');
+ const updateOutputContent = document.getElementById('updateOutputContent');
+ const reloadBtn = document.getElementById('reloadBtn');
+
+ updateOutputContent.textContent = '';
+ updateOutput.style.display = 'none';
+ reloadBtn.style.display = 'none';
+}
+
+function showToast(message, type) {
+ const toastLiveExample = document.getElementById('liveToast');
+ const toastBody = toastLiveExample.querySelector('.toast-body');
+
+ // Set toast color based on type
+ toastLiveExample.classList.remove('text-bg-primary', 'text-bg-success', 'text-bg-danger', 'text-bg-warning');
+ switch(type) {
+ case 'success':
+ toastLiveExample.classList.add('text-bg-success');
+ break;
+ case 'error':
+ toastLiveExample.classList.add('text-bg-danger');
+ break;
+ case 'warning':
+ toastLiveExample.classList.add('text-bg-warning');
+ break;
+ default:
+ toastLiveExample.classList.add('text-bg-primary');
+ }
+
+ toastBody.textContent = message;
+ const toastBootstrap = bootstrap.Toast.getOrCreateInstance(toastLiveExample);
+ toastBootstrap.show();
+}
+
+// Legacy function for backward compatibility
+function updateGitPull() {
+ updateFirmware();
+}
+
function set_RTC_withNTP(){
console.log("Set RTC module with WIFI (NTP server)");
diff --git a/html/launcher.php b/html/launcher.php
index 615c565..e217c47 100755
--- a/html/launcher.php
+++ b/html/launcher.php
@@ -349,6 +349,20 @@ if ($type == "git_pull") {
echo $output;
}
+if ($type == "update_firmware") {
+ // Execute the comprehensive update script
+ $command = 'sudo /var/www/nebuleair_pro_4g/update_firmware.sh 2>&1';
+ $output = shell_exec($command);
+
+ // Return the output as JSON for better web display
+ header('Content-Type: application/json');
+ echo json_encode([
+ 'success' => true,
+ 'output' => $output,
+ 'timestamp' => date('Y-m-d H:i:s')
+ ]);
+}
+
if ($type == "set_RTC_withNTP") {
$command = 'sudo /usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_NTP.py';
$output = shell_exec($command);
diff --git a/installation_part1.sh b/installation_part1.sh
index a07277a..6ba4aa8 100644
--- a/installation_part1.sh
+++ b/installation_part1.sh
@@ -100,7 +100,7 @@ fi
# Add sudo authorization (prevent duplicate entries)
info "Setting up sudo authorization..."
if ! sudo grep -q "/usr/bin/nmcli" /etc/sudoers; then
- echo -e "ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/git pull\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/ssh\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/python3 * www-data ALL=(ALL) NOPASSWD: /bin/systemctl *" | sudo tee -a /etc/sudoers > /dev/null
+ echo -e "ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/git pull\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/ssh\nwww-data ALL=(ALL) NOPASSWD: /usr/bin/python3 * www-data ALL=(ALL) NOPASSWD: /bin/systemctl * www-data ALL=(ALL) NOPASSWD: /var/www/nebuleair_pro_4g/*" | sudo tee -a /etc/sudoers > /dev/null
success "Sudo authorization added."
else
warning "Sudo authorization already set. Skipping."
diff --git a/sqlite/set_config.py b/sqlite/set_config.py
index de6f940..6a6d24b 100644
--- a/sqlite/set_config.py
+++ b/sqlite/set_config.py
@@ -20,10 +20,8 @@ cursor = conn.cursor()
print(f"Connected to database")
-# Clear existing data (if any)
-cursor.execute("DELETE FROM config_table")
-cursor.execute("DELETE FROM envea_sondes_table")
-print("Existing data cleared")
+# Note: Using INSERT OR IGNORE to add only new configurations without overwriting existing ones
+print("Adding new configurations (existing ones will be preserved)")
# Insert general configurations
@@ -54,7 +52,7 @@ config_entries = [
for key, value, value_type in config_entries:
cursor.execute(
- "INSERT INTO config_table (key, value, type) VALUES (?, ?, ?)",
+ "INSERT OR IGNORE INTO config_table (key, value, type) VALUES (?, ?, ?)",
(key, value, value_type)
)
@@ -67,7 +65,7 @@ envea_sondes = [
for connected, port, name, coefficient in envea_sondes:
cursor.execute(
- "INSERT INTO envea_sondes_table (connected, port, name, coefficient) VALUES (?, ?, ?, ?)",
+ "INSERT OR IGNORE INTO envea_sondes_table (connected, port, name, coefficient) VALUES (?, ?, ?, ?)",
(1 if connected else 0, port, name, coefficient)
)
diff --git a/update_firmware.sh b/update_firmware.sh
new file mode 100644
index 0000000..e39ba6a
--- /dev/null
+++ b/update_firmware.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# NebuleAir Pro 4G - Comprehensive Update Script
+# This script performs a complete system update including git pull,
+# config initialization, and service management
+
+echo "======================================"
+echo "NebuleAir Pro 4G - Firmware Update"
+echo "======================================"
+echo "Started at: $(date)"
+echo ""
+
+# Set working directory
+cd /var/www/nebuleair_pro_4g
+
+# Function to print status messages
+print_status() {
+ echo "[$(date '+%H:%M:%S')] $1"
+}
+
+# Function to check command success
+check_status() {
+ if [ $? -eq 0 ]; then
+ print_status "✓ $1 completed successfully"
+ else
+ print_status "✗ $1 failed"
+ return 1
+ fi
+}
+
+# Step 1: Git operations
+print_status "Step 1: Updating firmware from repository..."
+git fetch origin
+check_status "Git fetch"
+
+# Show current branch and any changes
+print_status "Current branch: $(git branch --show-current)"
+if [ -n "$(git status --porcelain)" ]; then
+ print_status "Warning: Local changes detected:"
+ git status --short
+fi
+
+# Pull latest changes
+git pull origin $(git branch --show-current)
+check_status "Git pull"
+
+# Step 2: Update database configuration
+print_status ""
+print_status "Step 2: Updating database configuration..."
+/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/set_config.py
+check_status "Database configuration update"
+
+# Step 3: Check and fix file permissions
+print_status ""
+print_status "Step 3: Checking file permissions..."
+sudo chmod +x /var/www/nebuleair_pro_4g/update_firmware.sh
+sudo chmod 755 /var/www/nebuleair_pro_4g/sqlite/*.py
+sudo chmod 755 /var/www/nebuleair_pro_4g/NPM/*.py
+sudo chmod 755 /var/www/nebuleair_pro_4g/BME280/*.py
+sudo chmod 755 /var/www/nebuleair_pro_4g/SARA/*.py
+sudo chmod 755 /var/www/nebuleair_pro_4g/envea/*.py
+check_status "File permissions update"
+
+# Step 4: Restart critical services if they exist
+print_status ""
+print_status "Step 4: Managing system services..."
+
+# List of services to check and restart
+services=(
+ "nebuleair-npm-data.timer"
+ "nebuleair-envea-data.timer"
+ "nebuleair-sara-data.timer"
+ "nebuleair-bme280-data.timer"
+ "nebuleair-mppt-data.timer"
+)
+
+for service in "${services[@]}"; do
+ if systemctl list-unit-files | grep -q "$service"; then
+ print_status "Restarting service: $service"
+ sudo systemctl restart "$service"
+ if systemctl is-active --quiet "$service"; then
+ print_status "✓ $service is running"
+ else
+ print_status "⚠ $service may not be active"
+ fi
+ else
+ print_status "ℹ Service $service not found (may not be installed)"
+ fi
+done
+
+# Step 5: System health check
+print_status ""
+print_status "Step 5: System health check..."
+
+# Check disk space
+disk_usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
+if [ "$disk_usage" -gt 90 ]; then
+ print_status "⚠ Warning: Disk usage is high ($disk_usage%)"
+else
+ print_status "✓ Disk usage is acceptable ($disk_usage%)"
+fi
+
+# Check if database is accessible
+if [ -f "/var/www/nebuleair_pro_4g/sqlite/sensors.db" ]; then
+ print_status "✓ Database file exists"
+else
+ print_status "⚠ Warning: Database file not found"
+fi
+
+# Step 6: Final cleanup
+print_status ""
+print_status "Step 6: Cleaning up..."
+sudo find /var/www/nebuleair_pro_4g/logs -name "*.log" -size +10M -exec truncate -s 0 {} \;
+check_status "Log cleanup"
+
+print_status "Update completed successfully!"
+
+exit 0
\ No newline at end of file