updates
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
183
html/admin.html
183
html/admin.html
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" value="" id="check_envea" onchange="update_config_sqlite('envea/read_value_v2.py', this.checked)">
|
||||
<input class="form-check-input" type="checkbox" value="" id="check_envea" onchange="update_config_sqlite('envea', this.checked)">
|
||||
<label class="form-check-label" for="check_envea">
|
||||
Send Envea sensor data
|
||||
</label>
|
||||
@@ -165,7 +165,34 @@
|
||||
<div class="col-lg-4 col-12">
|
||||
<h3 class="mt-4">Updates</h3>
|
||||
|
||||
<button type="submit" class="btn btn-primary" onclick="updateGitPull()">Update firmware</button>
|
||||
<button type="submit" class="btn btn-primary" onclick="updateFirmware()" id="updateBtn">
|
||||
<span id="updateBtnText">Update firmware</span>
|
||||
<span id="updateSpinner" class="spinner-border spinner-border-sm ms-2" style="display: none;"></span>
|
||||
</button>
|
||||
|
||||
<!-- Update Output Console -->
|
||||
<div id="updateOutput" class="mt-3" style="display: none;">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span class="fw-bold">Update Log</span>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-success me-2" onclick="location.reload()" id="reloadBtn" style="display: none;">
|
||||
<svg width="14" height="14" fill="currentColor" class="bi bi-arrow-clockwise me-1" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||
</svg>
|
||||
Reload Page
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearUpdateOutput()">
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<pre id="updateOutputContent" class="mb-0" style="max-height: 400px; overflow-y: auto; font-size: 0.85rem; background-color: #f8f9fa; padding: 1rem; border-radius: 0.375rem;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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 `<span style="color: #007bff; font-weight: bold;">${match}</span>`;
|
||||
})
|
||||
.replace(/✓/g, '<span style="color: #28a745;">✓</span>')
|
||||
.replace(/✗/g, '<span style="color: #dc3545;">✗</span>')
|
||||
.replace(/⚠/g, '<span style="color: #ffc107;">⚠</span>')
|
||||
.replace(/ℹ/g, '<span style="color: #17a2b8;">ℹ</span>');
|
||||
|
||||
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)");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
|
||||
118
update_firmware.sh
Normal file
118
update_firmware.sh
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user