Compare commits
19 Commits
d2d881205b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc71a271eb | ||
|
|
29458c4841 | ||
|
|
3b8b51172c | ||
|
|
acdc736a38 | ||
|
|
c5f120106f | ||
|
|
93d0e3b9cf | ||
|
|
5027f9945b | ||
|
|
943243f769 | ||
|
|
5adc814e1b | ||
|
|
d481608c9a | ||
|
|
76cf2a30bf | ||
|
|
d8e004e9bd | ||
|
|
675482929e | ||
|
|
fea7c03bfb | ||
|
|
1b3cf16d55 | ||
|
|
677595835a | ||
|
|
806f95df75 | ||
|
|
96231582f5 | ||
|
|
45fa8b382a |
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(sqlite3:*)"
|
||||||
|
],
|
||||||
|
"deny": []
|
||||||
|
},
|
||||||
|
"enableAllProjectMcpServers": false
|
||||||
|
}
|
||||||
74
BME280/get_data_v2.py
Normal file
74
BME280/get_data_v2.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
'''
|
||||||
|
____ __ __ _____ ____ ___ ___
|
||||||
|
| __ )| \/ | ____|___ \( _ ) / _ \
|
||||||
|
| _ \| |\/| | _| __) / _ \| | | |
|
||||||
|
| |_) | | | | |___ / __/ (_) | |_| |
|
||||||
|
|____/|_| |_|_____|_____\___/ \___/
|
||||||
|
|
||||||
|
Script to read data from BME280
|
||||||
|
Sensor connected to i2c on address 76 (use sudo i2cdetect -y 1 to get the address )
|
||||||
|
-> save data to database (table data_BME280 )
|
||||||
|
sudo python3 /var/www/moduleair_pro_4g/BME280/get_data_v2.py
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
import board
|
||||||
|
import busio
|
||||||
|
import json
|
||||||
|
import sqlite3
|
||||||
|
from adafruit_bme280 import basic as adafruit_bme280
|
||||||
|
|
||||||
|
# Connect to the SQLite database
|
||||||
|
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create I2C bus
|
||||||
|
i2c = busio.I2C(board.SCL, board.SDA)
|
||||||
|
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)
|
||||||
|
|
||||||
|
# Configure settings
|
||||||
|
bme280.sea_level_pressure = 1013.25 # Update this value for your location
|
||||||
|
|
||||||
|
# Read sensor data
|
||||||
|
|
||||||
|
#print(f"Temperature: {bme280.temperature:.2f} °C")
|
||||||
|
#print(f"Humidity: {bme280.humidity:.2f} %")
|
||||||
|
#print(f"Pressure: {bme280.pressure:.2f} hPa")
|
||||||
|
#print(f"Altitude: {bme280.altitude:.2f} m")
|
||||||
|
|
||||||
|
temperature = round(bme280.temperature, 2)
|
||||||
|
humidity = round(bme280.humidity, 2)
|
||||||
|
pressure = round(bme280.pressure, 2)
|
||||||
|
|
||||||
|
sensor_data = {
|
||||||
|
"temp": temperature, # Temperature in °C
|
||||||
|
"hum": humidity, # Humidity in %
|
||||||
|
"press": pressure # Pressure in hPa
|
||||||
|
}
|
||||||
|
|
||||||
|
#GET RTC TIME from SQlite
|
||||||
|
cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
|
||||||
|
row = cursor.fetchone() # Get the first (and only) row
|
||||||
|
rtc_time_str = row[1] # '2025-02-07 12:30:45'
|
||||||
|
|
||||||
|
|
||||||
|
# Convert to JSON and print
|
||||||
|
#print(json.dumps(sensor_data, indent=4))
|
||||||
|
|
||||||
|
|
||||||
|
#save to sqlite database
|
||||||
|
try:
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT INTO data_BME280 (timestamp,temperature, humidity, pressure) VALUES (?,?,?,?)'''
|
||||||
|
, (rtc_time_str,temperature,humidity,pressure))
|
||||||
|
|
||||||
|
# Commit and close the connection
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
#print("Sensor data saved successfully!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Database error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
conn.close()
|
||||||
@@ -30,7 +30,8 @@ sudo mkdir /var/www/moduleair_pro_4g/logs
|
|||||||
sudo touch /var/www/moduleair_pro_4g/logs/app.log /var/www/moduleair_pro_4g/logs/loop.log /var/www/moduleair_pro_4g/matrix/input_NPM.txt /var/www/moduleair_pro_4g/matrix/input_MHZ16.txt /var/www/moduleair_pro_4g/wifi_list.csv
|
sudo touch /var/www/moduleair_pro_4g/logs/app.log /var/www/moduleair_pro_4g/logs/loop.log /var/www/moduleair_pro_4g/matrix/input_NPM.txt /var/www/moduleair_pro_4g/matrix/input_MHZ16.txt /var/www/moduleair_pro_4g/wifi_list.csv
|
||||||
sudo cp /var/www/moduleair_pro_4g/config.json.dist /var/www/moduleair_pro_4g/config.json
|
sudo cp /var/www/moduleair_pro_4g/config.json.dist /var/www/moduleair_pro_4g/config.json
|
||||||
sudo chmod -R 777 /var/www/moduleair_pro_4g/
|
sudo chmod -R 777 /var/www/moduleair_pro_4g/
|
||||||
git config core.fileMode false
|
git -C /var/www/moduleair_pro_4g config core.fileMode false
|
||||||
|
git config --global --add safe.directory /var/www/moduleair_pro_4g
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import re
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
# database connection
|
# database connection
|
||||||
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
#get config data from SQLite table
|
#get config data from SQLite table
|
||||||
@@ -205,7 +205,7 @@ try:
|
|||||||
# 1. Set AIRCARTO URL
|
# 1. Set AIRCARTO URL
|
||||||
print('➡️Set aircarto URL')
|
print('➡️Set aircarto URL')
|
||||||
aircarto_profile_id = 0
|
aircarto_profile_id = 0
|
||||||
aircarto_url="data.nebuleair.fr"
|
aircarto_url="data.moduleair.fr"
|
||||||
command = f'AT+UHTTP={aircarto_profile_id},1,"{aircarto_url}"\r'
|
command = f'AT+UHTTP={aircarto_profile_id},1,"{aircarto_url}"\r'
|
||||||
ser_sara.write(command.encode('utf-8'))
|
ser_sara.write(command.encode('utf-8'))
|
||||||
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
|||||||
@@ -4,7 +4,5 @@
|
|||||||
|
|
||||||
#@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1
|
#@reboot sleep 30 && /usr/bin/python3 /var/www/moduleair_pro_4g/SARA/reboot/start.py >> /var/www/moduleair_pro_4g/logs/app.log 2>&1
|
||||||
|
|
||||||
0 0 * * * > /var/www/moduleair_pro_4g/logs/master.log
|
0 0 * * * find /var/www/moduleair_pro_4g/logs -name "*.log" -type f -exec truncate -s 0 {} \;
|
||||||
|
|
||||||
0 0 * * * find /var/www/nebuleair_pro_4g/logs -name "*.log" -type f -exec truncate -s 0 {} \;
|
|
||||||
|
|
||||||
|
|||||||
133
html/admin.html
133
html/admin.html
@@ -155,7 +155,34 @@
|
|||||||
|
|
||||||
<div class="col-lg-4 col-12">
|
<div class="col-lg-4 col-12">
|
||||||
<h3 class="mt-4">Updates</h3>
|
<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="updateGitPull()">Update firmware (v1)</button>
|
||||||
|
<button type="submit" class="btn btn-primary" onclick="updateFirmware()" id="updateBtn">
|
||||||
|
<span id="updateBtnText">Update firmware (v2)</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>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -285,11 +312,13 @@ window.onload = function() {
|
|||||||
const checkbox_bme = document.getElementById("check_bme280");
|
const checkbox_bme = document.getElementById("check_bme280");
|
||||||
const checkbox_CO2 = document.getElementById("check_CO2");
|
const checkbox_CO2 = document.getElementById("check_CO2");
|
||||||
const checkbox_SFA30 = document.getElementById("check_sensirionSFA30");
|
const checkbox_SFA30 = document.getElementById("check_sensirionSFA30");
|
||||||
|
const checkbox_uSpot = document.getElementById("check_uSpot");
|
||||||
|
|
||||||
checkbox_nmp5channels.checked = response.npm_5channel;
|
checkbox_nmp5channels.checked = response.npm_5channel;
|
||||||
checkbox_bme.checked = response["BME280"];
|
checkbox_bme.checked = response["BME280"];
|
||||||
checkbox_SFA30.checked = response["SFA30"];
|
checkbox_SFA30.checked = response["SFA30"];
|
||||||
checkbox_CO2.checked = response["MHZ19"];
|
checkbox_CO2.checked = response["MHZ19"];
|
||||||
|
checkbox_uSpot.checked = response["send_uSpot"];
|
||||||
|
|
||||||
//si sonde envea is true
|
//si sonde envea is true
|
||||||
if (response["envea"]) {
|
if (response["envea"]) {
|
||||||
@@ -439,6 +468,108 @@ function updateGitPull(){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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=update_firmware',
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
timeout: 120000, // 2 minutes timeout
|
||||||
|
|
||||||
|
success: function(response) {
|
||||||
|
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('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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function set_RTC_withNTP(){
|
function set_RTC_withNTP(){
|
||||||
console.log("Set RTC module with WIFI (NTP server)");
|
console.log("Set RTC module with WIFI (NTP server)");
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,20 @@ if ($type == "git_pull") {
|
|||||||
echo $output;
|
echo $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($type == "update_firmware") {
|
||||||
|
// Execute the comprehensive update script
|
||||||
|
$command = 'sudo /var/www/moduleair_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") {
|
if ($type == "set_RTC_withNTP") {
|
||||||
$command = 'sudo /usr/bin/python3 /var/www/moduleair_pro_4g/RTC/set_with_NTP.py';
|
$command = 'sudo /usr/bin/python3 /var/www/moduleair_pro_4g/RTC/set_with_NTP.py';
|
||||||
$output = shell_exec($command);
|
$output = shell_exec($command);
|
||||||
|
|||||||
378
installation.sh
378
installation.sh
@@ -61,9 +61,33 @@ check_sudo() {
|
|||||||
print_success "Sudo privileges confirmed"
|
print_success "Sudo privileges confirmed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pre-configure packages to avoid prompts
|
||||||
|
pre_configure_packages() {
|
||||||
|
print_step "Pre-configuring packages to avoid prompts..."
|
||||||
|
|
||||||
|
# Pre-configure common package questions
|
||||||
|
|
||||||
|
# For any package that uses debconf
|
||||||
|
echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
|
||||||
|
|
||||||
|
# For timezone data (if tzdata gets updated)
|
||||||
|
echo 'tzdata tzdata/Areas select Europe' | sudo debconf-set-selections
|
||||||
|
echo 'tzdata tzdata/Zones/Europe select Paris' | sudo debconf-set-selections
|
||||||
|
|
||||||
|
# For keyboard configuration
|
||||||
|
echo 'keyboard-configuration keyboard-configuration/layoutcode string us' | sudo debconf-set-selections
|
||||||
|
|
||||||
|
# For any service restart prompts
|
||||||
|
echo '* libraries/restart-without-asking boolean true' | sudo debconf-set-selections
|
||||||
|
|
||||||
|
print_success "Package pre-configuration completed"
|
||||||
|
}
|
||||||
|
|
||||||
# System update
|
# System update
|
||||||
update_system() {
|
update_system() {
|
||||||
print_step "Updating system packages..."
|
print_step "Updating system packages..."
|
||||||
|
# Set non-interactive mode for apt
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
sudo apt update -y
|
sudo apt update -y
|
||||||
print_success "System packages updated"
|
print_success "System packages updated"
|
||||||
}
|
}
|
||||||
@@ -72,10 +96,14 @@ update_system() {
|
|||||||
install_dependencies() {
|
install_dependencies() {
|
||||||
print_step "Installing system dependencies..."
|
print_step "Installing system dependencies..."
|
||||||
|
|
||||||
|
# Ensure non-interactive mode
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
local packages=(
|
local packages=(
|
||||||
"git"
|
"git"
|
||||||
"apache2"
|
"apache2"
|
||||||
"php"
|
"php"
|
||||||
|
"php-sqlite3"
|
||||||
"sqlite3"
|
"sqlite3"
|
||||||
"python3"
|
"python3"
|
||||||
"python3-pip"
|
"python3-pip"
|
||||||
@@ -88,11 +116,12 @@ install_dependencies() {
|
|||||||
"python3-serial"
|
"python3-serial"
|
||||||
"python3-requests"
|
"python3-requests"
|
||||||
"python3-schedule"
|
"python3-schedule"
|
||||||
|
"python3-rpi.gpio"
|
||||||
)
|
)
|
||||||
|
|
||||||
for package in "${packages[@]}"; do
|
for package in "${packages[@]}"; do
|
||||||
print_status "Installing $package..."
|
print_status "Installing $package..."
|
||||||
if sudo apt install -y "$package"; then
|
if sudo DEBIAN_FRONTEND=noninteractive apt install -y -q "$package"; then
|
||||||
print_success "$package installed"
|
print_success "$package installed"
|
||||||
else
|
else
|
||||||
print_error "Failed to install $package"
|
print_error "Failed to install $package"
|
||||||
@@ -112,7 +141,6 @@ install_python_packages() {
|
|||||||
"pyserial"
|
"pyserial"
|
||||||
"requests"
|
"requests"
|
||||||
"schedule"
|
"schedule"
|
||||||
"RPi.GPIO"
|
|
||||||
"gpiozero"
|
"gpiozero"
|
||||||
"smbus2"
|
"smbus2"
|
||||||
"adafruit-circuitpython-bme280"
|
"adafruit-circuitpython-bme280"
|
||||||
@@ -123,7 +151,7 @@ install_python_packages() {
|
|||||||
|
|
||||||
for package in "${pip_packages[@]}"; do
|
for package in "${pip_packages[@]}"; do
|
||||||
print_status "Installing Python package: $package..."
|
print_status "Installing Python package: $package..."
|
||||||
if pip3 install "$package" --break-system-packages; then
|
if sudo pip3 install "$package" --break-system-packages; then
|
||||||
print_success "$package installed"
|
print_success "$package installed"
|
||||||
else
|
else
|
||||||
print_warning "Failed to install $package (may already be installed)"
|
print_warning "Failed to install $package (may already be installed)"
|
||||||
@@ -157,49 +185,6 @@ clone_repository() {
|
|||||||
print_success "Repository permissions set"
|
print_success "Repository permissions set"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup directory structure
|
|
||||||
setup_directories() {
|
|
||||||
print_step "Setting up additional directories and files..."
|
|
||||||
|
|
||||||
# Create additional directories
|
|
||||||
local dirs=(
|
|
||||||
"sqlite"
|
|
||||||
"logs"
|
|
||||||
"matrix/input"
|
|
||||||
)
|
|
||||||
|
|
||||||
for dir in "${dirs[@]}"; do
|
|
||||||
sudo mkdir -p "/var/www/moduleair_pro_4g/$dir"
|
|
||||||
print_success "Created directory: $dir"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Create required log files
|
|
||||||
local files=(
|
|
||||||
"logs/app.log"
|
|
||||||
"logs/loop.log"
|
|
||||||
"matrix/input_NPM.txt"
|
|
||||||
"matrix/input_MHZ16.txt"
|
|
||||||
"wifi_list.csv"
|
|
||||||
)
|
|
||||||
|
|
||||||
for file in "${files[@]}"; do
|
|
||||||
sudo touch "/var/www/moduleair_pro_4g/$file"
|
|
||||||
print_success "Created file: $file"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Copy config template if it exists
|
|
||||||
if [ -f "/var/www/moduleair_pro_4g/config.json.dist" ]; then
|
|
||||||
sudo cp /var/www/moduleair_pro_4g/config.json.dist /var/www/moduleair_pro_4g/config.json
|
|
||||||
print_success "Configuration file created from template"
|
|
||||||
else
|
|
||||||
print_warning "config.json.dist not found - skipping config file creation"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set proper permissions
|
|
||||||
sudo chown -R www-data:www-data /var/www/moduleair_pro_4g
|
|
||||||
sudo chmod -R 755 /var/www/moduleair_pro_4g
|
|
||||||
print_success "Directory permissions updated"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure Apache
|
# Configure Apache
|
||||||
configure_apache() {
|
configure_apache() {
|
||||||
@@ -222,6 +207,70 @@ configure_apache() {
|
|||||||
print_success "Apache configured and restarted"
|
print_success "Apache configured and restarted"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# System optimizations (disable audio, isolate CPU)
|
||||||
|
system_optimizations() {
|
||||||
|
print_step "Applying system optimizations..."
|
||||||
|
|
||||||
|
# Find config.txt location
|
||||||
|
local config_file="/boot/firmware/config.txt"
|
||||||
|
if [ ! -f "$config_file" ]; then
|
||||||
|
config_file="/boot/config.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$config_file" ]; then
|
||||||
|
print_error "Could not find config.txt file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disable onboard audio
|
||||||
|
print_status "Disabling onboard audio..."
|
||||||
|
if grep -q "^dtparam=audio=" "$config_file"; then
|
||||||
|
# Replace existing audio parameter
|
||||||
|
sudo sed -i 's/^dtparam=audio=.*/dtparam=audio=off/' "$config_file"
|
||||||
|
print_success "Audio parameter updated to off"
|
||||||
|
else
|
||||||
|
# Add audio parameter if not exists
|
||||||
|
echo "dtparam=audio=off" | sudo tee -a "$config_file" > /dev/null
|
||||||
|
print_success "Audio disabled in config.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Blacklist audio module
|
||||||
|
print_status "Blacklisting audio module..."
|
||||||
|
echo "blacklist snd_bcm2835" | sudo tee /etc/modprobe.d/blacklist.conf > /dev/null
|
||||||
|
print_success "Audio module blacklisted"
|
||||||
|
|
||||||
|
# Find cmdline.txt location
|
||||||
|
local cmdline_file="/boot/firmware/cmdline.txt"
|
||||||
|
if [ ! -f "$cmdline_file" ]; then
|
||||||
|
cmdline_file="/boot/cmdline.txt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$cmdline_file" ]; then
|
||||||
|
print_error "Could not find cmdline.txt file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add CPU isolation
|
||||||
|
print_status "Setting up CPU isolation..."
|
||||||
|
if ! grep -q "isolcpus=3" "$cmdline_file"; then
|
||||||
|
# Read current cmdline and append isolcpus
|
||||||
|
local current_cmdline=$(cat "$cmdline_file")
|
||||||
|
echo "${current_cmdline} isolcpus=3" | sudo tee "$cmdline_file" > /dev/null
|
||||||
|
print_success "CPU core 3 isolated"
|
||||||
|
else
|
||||||
|
print_warning "CPU isolation already configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if audio module is currently loaded
|
||||||
|
if lsmod | grep -q snd_bcm2835; then
|
||||||
|
print_warning "Audio module is currently loaded. It will be disabled after reboot."
|
||||||
|
else
|
||||||
|
print_success "Audio module is not loaded"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "System optimizations completed"
|
||||||
|
}
|
||||||
|
|
||||||
# Enable hardware interfaces
|
# Enable hardware interfaces
|
||||||
enable_hardware() {
|
enable_hardware() {
|
||||||
print_step "Enabling hardware interfaces..."
|
print_step "Enabling hardware interfaces..."
|
||||||
@@ -283,6 +332,58 @@ enable_hardware() {
|
|||||||
print_success "Hardware interfaces configuration completed"
|
print_success "Hardware interfaces configuration completed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Setup sudo authorization
|
||||||
|
setup_sudo_authorization() {
|
||||||
|
print_step "Setting up sudo authorization..."
|
||||||
|
|
||||||
|
SUDOERS_FILE="/etc/sudoers"
|
||||||
|
|
||||||
|
# First, fix any existing syntax errors
|
||||||
|
if sudo visudo -c 2>&1 | grep -q "syntax error"; then
|
||||||
|
print_warning "Syntax error detected in sudoers file. Attempting to fix..."
|
||||||
|
# Remove the problematic line if it exists
|
||||||
|
sudo sed -i '/www-data ALL=(ALL) NOPASSWD: \/usr\/bin\/python3 \* www-data/d' "$SUDOERS_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add proper sudo rules (each on a separate line)
|
||||||
|
if ! sudo grep -q "/usr/bin/nmcli" "$SUDOERS_FILE"; then
|
||||||
|
# Create a temporary file with the new rules
|
||||||
|
cat <<EOF | sudo tee /tmp/sudoers_additions > /dev/null
|
||||||
|
# ModuleAir Pro 4G sudo rules
|
||||||
|
ALL ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/sbin/reboot
|
||||||
|
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/moduleair_pro_4g/*
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Validate the temporary file
|
||||||
|
if sudo visudo -c -f /tmp/sudoers_additions; then
|
||||||
|
# Append to sudoers if valid using tee with sudo
|
||||||
|
cat /tmp/sudoers_additions | sudo EDITOR='tee -a' visudo >/dev/null 2>&1
|
||||||
|
print_success "Sudo authorization added"
|
||||||
|
else
|
||||||
|
print_error "Failed to add sudo rules - syntax validation failed"
|
||||||
|
sudo rm -f /tmp/sudoers_additions
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
sudo rm -f /tmp/sudoers_additions
|
||||||
|
else
|
||||||
|
print_warning "Sudo authorization already set. Skipping."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate sudoers file after changes
|
||||||
|
if ! sudo visudo -c; then
|
||||||
|
print_error "Sudoers file has syntax errors! Please fix manually with 'sudo visudo'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Sudo authorization configured successfully"
|
||||||
|
}
|
||||||
|
|
||||||
# Set device permissions
|
# Set device permissions
|
||||||
set_permissions() {
|
set_permissions() {
|
||||||
print_step "Setting device permissions..."
|
print_step "Setting device permissions..."
|
||||||
@@ -301,36 +402,137 @@ set_permissions() {
|
|||||||
print_success "Device permissions set"
|
print_success "Device permissions set"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check matrix binaries
|
# Setup RTC save to DB service
|
||||||
check_matrix_binaries() {
|
setup_rtc_service() {
|
||||||
print_step "Checking matrix display binaries..."
|
print_step "Setting up RTC save to DB service..."
|
||||||
|
|
||||||
local programs=(
|
# Create the service file
|
||||||
"screenNetwork/network_status"
|
cat << 'EOF' | sudo tee /etc/systemd/system/rtc_save_to_db.service > /dev/null
|
||||||
"screenSensors/displayAll4_v2"
|
[Unit]
|
||||||
"screenSensors/displayCO2_PM_Network"
|
Description=RTC Save to DB Script
|
||||||
"screenSensors/blank_screen"
|
After=network.target
|
||||||
"welcomeScreen/welcome_screen"
|
|
||||||
)
|
[Service]
|
||||||
|
ExecStart=/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/save_to_db.py
|
||||||
|
Restart=always
|
||||||
|
RestartSec=1
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/var/www/moduleair_pro_4g
|
||||||
|
StandardOutput=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db.log
|
||||||
|
StandardError=append:/var/www/moduleair_pro_4g/logs/rtc_save_to_db_errors.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
for program in "${programs[@]}"; do
|
print_success "RTC service file created"
|
||||||
local binary_file="/var/www/moduleair_pro_4g/matrix/${program}"
|
|
||||||
|
|
||||||
if [ -f "$binary_file" ] && [ -x "$binary_file" ]; then
|
|
||||||
print_success "Matrix binary found: $program"
|
|
||||||
else
|
|
||||||
print_warning "Matrix binary missing or not executable: $program"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check matrix library
|
# Create logs directory if it doesn't exist
|
||||||
if [ -f "/var/www/moduleair_pro_4g/matrix/lib/librgbmatrix.a" ]; then
|
sudo mkdir -p /var/www/moduleair_pro_4g/logs
|
||||||
print_success "Matrix library found: librgbmatrix.a"
|
sudo chown -R www-data:www-data /var/www/moduleair_pro_4g/logs
|
||||||
|
print_status "Logs directory created/verified"
|
||||||
|
|
||||||
|
# Reload systemd daemon
|
||||||
|
print_status "Reloading systemd daemon..."
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
# Enable the service
|
||||||
|
print_status "Enabling RTC service..."
|
||||||
|
if sudo systemctl enable rtc_save_to_db.service; then
|
||||||
|
print_success "RTC service enabled"
|
||||||
else
|
else
|
||||||
print_warning "Matrix library missing: librgbmatrix.a"
|
print_error "Failed to enable RTC service"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_success "Matrix binaries check completed"
|
# Start the service
|
||||||
|
print_status "Starting RTC service..."
|
||||||
|
if sudo systemctl start rtc_save_to_db.service; then
|
||||||
|
print_success "RTC service started"
|
||||||
|
else
|
||||||
|
print_warning "Failed to start RTC service - may need reboot"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
print_status "Checking RTC service status..."
|
||||||
|
if systemctl is-active --quiet rtc_save_to_db.service; then
|
||||||
|
print_success "RTC service is running"
|
||||||
|
else
|
||||||
|
print_warning "RTC service is not running - will start after reboot"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Setup cron jobs
|
||||||
|
setup_cron_jobs() {
|
||||||
|
print_step "Setting up cron jobs..."
|
||||||
|
|
||||||
|
if [ -f "/var/www/moduleair_pro_4g/cron_jobs" ]; then
|
||||||
|
if sudo crontab "/var/www/moduleair_pro_4g/cron_jobs"; then
|
||||||
|
print_success "Cron jobs set up successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to set up cron jobs"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "Cron jobs file not found. Skipping."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create databases
|
||||||
|
create_databases() {
|
||||||
|
print_step "Creating databases..."
|
||||||
|
|
||||||
|
if [ -f "/var/www/moduleair_pro_4g/sqlite/create_db.py" ]; then
|
||||||
|
if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/create_db.py"; then
|
||||||
|
print_success "Databases created successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to create databases"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "Database creation script not found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fix database permissions
|
||||||
|
fix_database_permissions() {
|
||||||
|
print_step "Fixing database permissions..."
|
||||||
|
|
||||||
|
# Set full permissions for sqlite directory
|
||||||
|
if [ -d "/var/www/moduleair_pro_4g/sqlite" ]; then
|
||||||
|
sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/
|
||||||
|
print_success "Set permissions for sqlite directory"
|
||||||
|
else
|
||||||
|
print_warning "sqlite directory not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set full permissions for sensors.db
|
||||||
|
if [ -f "/var/www/moduleair_pro_4g/sqlite/sensors.db" ]; then
|
||||||
|
sudo chmod 777 /var/www/moduleair_pro_4g/sqlite/sensors.db
|
||||||
|
print_success "Set permissions for sensors.db"
|
||||||
|
else
|
||||||
|
print_warning "sensors.db not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Database permissions fixed"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set configuration
|
||||||
|
set_configuration() {
|
||||||
|
print_step "Setting configuration..."
|
||||||
|
|
||||||
|
if [ -f "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py" ]; then
|
||||||
|
if sudo /usr/bin/python3 "/var/www/moduleair_pro_4g/sqlite/set_config_smart.py"; then
|
||||||
|
print_success "Configuration set successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to set configuration"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "Configuration script not found"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup systemd services
|
# Setup systemd services
|
||||||
@@ -403,6 +605,7 @@ final_status() {
|
|||||||
"moduleair-sara-data.timer"
|
"moduleair-sara-data.timer"
|
||||||
"moduleair-bme-data.timer"
|
"moduleair-bme-data.timer"
|
||||||
"moduleair-boot.service"
|
"moduleair-boot.service"
|
||||||
|
"rtc_save_to_db.service"
|
||||||
)
|
)
|
||||||
|
|
||||||
for service in "${services[@]}"; do
|
for service in "${services[@]}"; do
|
||||||
@@ -420,9 +623,15 @@ final_status() {
|
|||||||
echo -e "${GREEN}✓${NC} Directory structure created"
|
echo -e "${GREEN}✓${NC} Directory structure created"
|
||||||
echo -e "${GREEN}✓${NC} Apache web server configured"
|
echo -e "${GREEN}✓${NC} Apache web server configured"
|
||||||
echo -e "${GREEN}✓${NC} Hardware interfaces enabled"
|
echo -e "${GREEN}✓${NC} Hardware interfaces enabled"
|
||||||
|
echo -e "${GREEN}✓${NC} System optimizations applied"
|
||||||
echo -e "${GREEN}✓${NC} Device permissions set"
|
echo -e "${GREEN}✓${NC} Device permissions set"
|
||||||
|
echo -e "${GREEN}✓${NC} Sudo authorization configured"
|
||||||
|
echo -e "${GREEN}✓${NC} Databases created"
|
||||||
|
echo -e "${GREEN}✓${NC} Configuration set"
|
||||||
|
echo -e "${GREEN}✓${NC} Cron jobs configured"
|
||||||
echo -e "${GREEN}✓${NC} Matrix binaries verified"
|
echo -e "${GREEN}✓${NC} Matrix binaries verified"
|
||||||
echo -e "${GREEN}✓${NC} Systemd services configured"
|
echo -e "${GREEN}✓${NC} Systemd services configured"
|
||||||
|
echo -e "${GREEN}✓${NC} RTC save to DB service installed"
|
||||||
echo -e "${GREEN}✓${NC} Boot scripts created"
|
echo -e "${GREEN}✓${NC} Boot scripts created"
|
||||||
|
|
||||||
print_header "NEXT STEPS"
|
print_header "NEXT STEPS"
|
||||||
@@ -434,6 +643,7 @@ final_status() {
|
|||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "${YELLOW}3.${NC} Check service status:"
|
echo -e "${YELLOW}3.${NC} Check service status:"
|
||||||
echo -e " ${CYAN}systemctl list-timers | grep moduleair${NC}"
|
echo -e " ${CYAN}systemctl list-timers | grep moduleair${NC}"
|
||||||
|
echo -e " ${CYAN}systemctl status rtc_save_to_db.service${NC}"
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "${YELLOW}4.${NC} Test RTC module (after reboot):"
|
echo -e "${YELLOW}4.${NC} Test RTC module (after reboot):"
|
||||||
echo -e " ${CYAN}python3 /var/www/moduleair_pro_4g/RTC/read.py${NC}"
|
echo -e " ${CYAN}python3 /var/www/moduleair_pro_4g/RTC/read.py${NC}"
|
||||||
@@ -443,8 +653,19 @@ final_status() {
|
|||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "${YELLOW}6.${NC} Check logs if needed:"
|
echo -e "${YELLOW}6.${NC} Check logs if needed:"
|
||||||
echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/*.log${NC}"
|
echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/*.log${NC}"
|
||||||
|
echo -e " ${CYAN}tail -f /var/www/moduleair_pro_4g/logs/rtc_save_to_db.log${NC}"
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${YELLOW}7.${NC} Check cron jobs:"
|
||||||
|
echo -e " ${CYAN}sudo crontab -l${NC}"
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${YELLOW}8.${NC} Verify audio is disabled (after reboot):"
|
||||||
|
echo -e " ${CYAN}lsmod | grep snd_bcm2835${NC} (should return nothing)"
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${YELLOW}9.${NC} Verify CPU isolation (after reboot):"
|
||||||
|
echo -e " ${CYAN}cat /proc/cmdline | grep isolcpus${NC}"
|
||||||
echo -e ""
|
echo -e ""
|
||||||
print_warning "I2C and UART devices will NOT work until after reboot!"
|
print_warning "I2C and UART devices will NOT work until after reboot!"
|
||||||
|
print_warning "Audio and CPU isolation changes require reboot to take effect!"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main installation function
|
# Main installation function
|
||||||
@@ -452,18 +673,31 @@ main() {
|
|||||||
print_header "MODULEAIR PRO 4G INSTALLATION"
|
print_header "MODULEAIR PRO 4G INSTALLATION"
|
||||||
print_status "Starting automated installation..."
|
print_status "Starting automated installation..."
|
||||||
|
|
||||||
|
# Set non-interactive mode globally
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
export NEEDRESTART_MODE=a
|
||||||
|
|
||||||
check_root
|
check_root
|
||||||
check_sudo
|
check_sudo
|
||||||
|
pre_configure_packages
|
||||||
update_system
|
update_system
|
||||||
install_dependencies
|
install_dependencies
|
||||||
install_python_packages
|
install_python_packages
|
||||||
clone_repository
|
clone_repository
|
||||||
setup_directories
|
|
||||||
configure_apache
|
configure_apache
|
||||||
enable_hardware
|
enable_hardware
|
||||||
|
system_optimizations
|
||||||
set_permissions
|
set_permissions
|
||||||
check_matrix_binaries
|
setup_sudo_authorization
|
||||||
|
|
||||||
|
# Database and configuration setup
|
||||||
|
create_databases
|
||||||
|
set_configuration
|
||||||
|
fix_database_permissions # Fix permissions after database creation
|
||||||
|
setup_cron_jobs
|
||||||
|
|
||||||
setup_services
|
setup_services
|
||||||
|
setup_rtc_service # New function for RTC service
|
||||||
create_boot_script
|
create_boot_script
|
||||||
final_status
|
final_status
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ JSON PAYLOAD (Micro-Spot Servers)
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import board
|
#import board
|
||||||
import json
|
import json
|
||||||
import serial
|
import serial
|
||||||
import time
|
import time
|
||||||
@@ -509,11 +509,133 @@ def reset_server_hostname(profile_id):
|
|||||||
if not http_reset_success:
|
if not http_reset_success:
|
||||||
print("⚠️ AirCarto HTTP profile reset failed")
|
print("⚠️ AirCarto HTTP profile reset failed")
|
||||||
elif profile_id ==1:
|
elif profile_id ==1:
|
||||||
pass # TODO: implement handling for profile 1
|
pass # on utilise la fonction reset_server_hostname_https pour uSpot
|
||||||
else:
|
else:
|
||||||
print(f"❌ Unsupported profile ID: {profile_id}")
|
print(f"❌ Unsupported profile ID: {profile_id}")
|
||||||
http_reset_success = False
|
http_reset_success = False
|
||||||
return http_reset_success
|
return http_reset_success
|
||||||
|
|
||||||
|
def reset_server_hostname_https(profile_id):
|
||||||
|
"""
|
||||||
|
Function that reset server hostname (URL) connection for the SARA R5
|
||||||
|
returns true or false
|
||||||
|
"""
|
||||||
|
print("⚠️Reseting Server Hostname HTTS secure connection ")
|
||||||
|
http_reset_success = False # Default fallback
|
||||||
|
|
||||||
|
#Pour uSpot
|
||||||
|
if profile_id == 1:
|
||||||
|
print('<span style="color: orange;font-weight: bold;">🔧 Resetting uSpot HTTPs Profile</span>')
|
||||||
|
uSpot_url="api-prod.uspot.probesys.net"
|
||||||
|
security_profile_id = 1
|
||||||
|
|
||||||
|
#step 1: import the certificate
|
||||||
|
print("➡️ import certificate")
|
||||||
|
certificate_name = "e6"
|
||||||
|
with open("/var/www/moduleair_pro_4g/SARA/SSL/certificate/e6.pem", "rb") as cert_file:
|
||||||
|
certificate = cert_file.read()
|
||||||
|
size_of_string = len(certificate)
|
||||||
|
|
||||||
|
# AT+USECMNG=0,<type>,<internal_name>,<data_size>
|
||||||
|
# type-> 0 -> trusted root CA
|
||||||
|
command = f'AT+USECMNG=0,0,"{certificate_name}",{size_of_string}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_1 = read_complete_response(ser_sara, wait_for_lines=[">"])
|
||||||
|
print(response_SARA_1)
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
print("➡️ add certificate")
|
||||||
|
ser_sara.write(certificate)
|
||||||
|
response_SARA_2 = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_2)
|
||||||
|
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# op_code: 0 -> certificate validation level
|
||||||
|
# param_val : 0 -> Level 0 No validation; 1-> Level 1 Root certificate validation
|
||||||
|
print("➡️Set the security profile (params)")
|
||||||
|
certification_level=0
|
||||||
|
command = f'AT+USECPRF={security_profile_id},0,{certification_level}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5b = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5b)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# op_code: 1 -> minimum SSL/TLS version
|
||||||
|
# param_val : 0 -> any; server can use any version for the connection; 1-> LSv1.0; 2->TLSv1.1; 3->TLSv1.2;
|
||||||
|
print("➡️Set the security profile (params)")
|
||||||
|
minimum_SSL_version = 0
|
||||||
|
command = f'AT+USECPRF={security_profile_id},1,{minimum_SSL_version}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5bb = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5bb)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
#op_code: 2 -> legacy cipher suite selection
|
||||||
|
# 0 (factory-programmed value): a list of default cipher suites is proposed at the beginning of handshake process, and a cipher suite will be negotiated among the cipher suites proposed in the list.
|
||||||
|
print("➡️Set cipher")
|
||||||
|
cipher_suite = 0
|
||||||
|
command = f'AT+USECPRF={security_profile_id},2,{cipher_suite}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5cc = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5cc)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# op_code: 3 -> trusted root certificate internal name
|
||||||
|
print("➡️Set the security profile (choose cert)")
|
||||||
|
command = f'AT+USECPRF={security_profile_id},3,"{certificate_name}"\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5c = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5c)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# op_code: 10 -> SNI (server name indication)
|
||||||
|
print("➡️Set the SNI")
|
||||||
|
command = f'AT+USECPRF={security_profile_id},10,"{uSpot_url}"\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5cf = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5cf)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
#step 4: set url (op_code = 1)
|
||||||
|
print("➡️SET URL")
|
||||||
|
command = f'AT+UHTTP={profile_id},1,"{uSpot_url}"\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
#step 4: set PORT (op_code = 5)
|
||||||
|
print("➡️SET PORT")
|
||||||
|
port = 443
|
||||||
|
command = f'AT+UHTTP={profile_id},5,{port}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_55 = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_55)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
#step 4: set url to SSL (op_code = 6) (http_secure = 1 for HTTPS)(USECMNG_PROFILE = 2)
|
||||||
|
print("➡️SET SSL")
|
||||||
|
http_secure = 1
|
||||||
|
command = f'AT+UHTTP={profile_id},6,{http_secure},{security_profile_id}\r'
|
||||||
|
|
||||||
|
ser_sara.write(command.encode('utf-8'))
|
||||||
|
response_SARA_5fg = read_complete_response(ser_sara, wait_for_lines=["OK"])
|
||||||
|
print(response_SARA_5fg)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
http_reset_success = response_SARA_5 is not None and "OK" in response_SARA_5
|
||||||
|
if not http_reset_success:
|
||||||
|
print("⚠️ AirCarto HTTP profile reset failed")
|
||||||
|
#Pour uSpot
|
||||||
|
elif profile_id ==1:
|
||||||
|
pass #on utilise la fonction reset_server_hostname_https pour uSpot
|
||||||
|
else:
|
||||||
|
print(f"❌ Unsupported profile ID: {profile_id}")
|
||||||
|
http_reset_success = False
|
||||||
|
return http_reset_success
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
'''
|
'''
|
||||||
@@ -524,7 +646,7 @@ try:
|
|||||||
|_____\___/ \___/|_|
|
|_____\___/ \___/|_|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
print('<h3>START LOOP</h3>')
|
print('<h3>START LOOP</h3>', end="")
|
||||||
|
|
||||||
# Check system uptime
|
# Check system uptime
|
||||||
with open('/proc/uptime', 'r') as f:
|
with open('/proc/uptime', 'r') as f:
|
||||||
@@ -654,7 +776,7 @@ try:
|
|||||||
payload_csv[20] = co2_average # Choose appropriate index
|
payload_csv[20] = co2_average # Choose appropriate index
|
||||||
|
|
||||||
# Add data to payload JSON
|
# Add data to payload JSON
|
||||||
payload_json["sensordatavalues"].append({"value_type": "CO2", "value": str(co2_average)})
|
payload_json["sensordatavalues"].append({"value_type": "MHZ16_CO2", "value": str(co2_average)})
|
||||||
|
|
||||||
print(f"CO2 average from {len(co2_values)} measurements: {co2_average}")
|
print(f"CO2 average from {len(co2_values)} measurements: {co2_average}")
|
||||||
else:
|
else:
|
||||||
@@ -732,19 +854,19 @@ try:
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
# On vérifie si le signal n'est pas à 99 pour déconnexion
|
# On vérifie si le signal n'est pas à 99 pour déconnexion
|
||||||
# si c'est le cas on essaie de se reconnecter
|
|
||||||
if signal_quality == 99:
|
if signal_quality == 99:
|
||||||
update_config_sqlite('SARA_network_status', 'disconnected')
|
update_config_sqlite('SARA_network_status', 'disconnected')
|
||||||
update_config_sqlite('SARA_signal_quality', '99')
|
update_config_sqlite('SARA_signal_quality', '99')
|
||||||
|
|
||||||
print('<span style="color: red;font-weight: bold;">⚠️ATTENTION: Signal Quality indicates no signal (99)⚠️</span>')
|
print('<span style="color: red;font-weight: bold;">⚠️ATTENTION: Signal Quality indicates no signal (99)⚠️</span>')
|
||||||
print("TRY TO RECONNECT:")
|
|
||||||
command = f'AT+COPS=1,2,"{selected_networkID}"\r'
|
#print("TRY TO RECONNECT:")
|
||||||
ser_sara.write(command.encode('utf-8'))
|
#command = f'AT+COPS=1,2,"{selected_networkID}"\r'
|
||||||
responseReconnect = read_complete_response(ser_sara, timeout=20, end_of_response_timeout=20, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=True)
|
#ser_sara.write(command.encode('utf-8'))
|
||||||
print('<p class="text-danger-emphasis">')
|
#responseReconnect = read_complete_response(ser_sara, timeout=20, end_of_response_timeout=20, wait_for_lines=["OK", "+CME ERROR", "ERROR"], debug=True)
|
||||||
print(responseReconnect)
|
#print('<p class="text-danger-emphasis">')
|
||||||
print("</p>", end="")
|
#print(responseReconnect)
|
||||||
|
#print("</p>", end="")
|
||||||
|
|
||||||
print('🛑STOP LOOP🛑')
|
print('🛑STOP LOOP🛑')
|
||||||
print("<hr>")
|
print("<hr>")
|
||||||
@@ -816,7 +938,7 @@ try:
|
|||||||
# 1.Vérifier si la réponse contient un message d'erreur CME
|
# 1.Vérifier si la réponse contient un message d'erreur CME
|
||||||
if "+CME ERROR" in lines[-1]:
|
if "+CME ERROR" in lines[-1]:
|
||||||
print("*****")
|
print("*****")
|
||||||
print('<span style="color: red;font-weight: bold;">ATTENTION: CME ERROR</span>')
|
print('<span style="color: red;font-weight: bold;">⛔ATTENTION: CME ERROR</span>')
|
||||||
print("error:", lines[-1])
|
print("error:", lines[-1])
|
||||||
print("*****")
|
print("*****")
|
||||||
|
|
||||||
@@ -851,16 +973,16 @@ try:
|
|||||||
# 2.1 code 0 (HTTP failed) ⛔⛔⛔
|
# 2.1 code 0 (HTTP failed) ⛔⛔⛔
|
||||||
if len(parts) == 3 and parts[-1] == '0': # The third value indicates success
|
if len(parts) == 3 and parts[-1] == '0': # The third value indicates success
|
||||||
print("*****")
|
print("*****")
|
||||||
print('<span style="color: red;font-weight: bold;">ATTENTION: HTTP operation failed</span>')
|
print('<span style="color: red;font-weight: bold;">⛔ATTENTION: HTTP operation failed</span>')
|
||||||
print("*****")
|
print("*****")
|
||||||
update_config_sqlite('SARA_network_status', 'error')
|
update_config_sqlite('SARA_network_status', 'error')
|
||||||
|
|
||||||
|
|
||||||
# Get error code
|
# Get error code
|
||||||
print("Getting error code")
|
print("Getting error code", end="")
|
||||||
command = f'AT+UHTTPER={aircarto_profile_id}\r'
|
command = f'AT+UHTTPER={aircarto_profile_id}\r'
|
||||||
ser_sara.write(command.encode('utf-8'))
|
ser_sara.write(command.encode('utf-8'))
|
||||||
response_SARA_9 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
response_SARA_9 = read_complete_response(ser_sara, wait_for_lines=["OK","ERROR"], debug=False)
|
||||||
print('<p class="text-danger-emphasis">')
|
print('<p class="text-danger-emphasis">')
|
||||||
print(response_SARA_9)
|
print(response_SARA_9)
|
||||||
print("</p>", end="")
|
print("</p>", end="")
|
||||||
@@ -871,50 +993,57 @@ try:
|
|||||||
if error_code is not None:
|
if error_code is not None:
|
||||||
# Display interpretation based on error code
|
# Display interpretation based on error code
|
||||||
if error_code == 0:
|
if error_code == 0:
|
||||||
print('<p class="text-success">No error detected</p>')
|
print('<p class="text-success">No error detected</p>', end="")
|
||||||
|
# N°4 INVALID SERVER HOSTNAME
|
||||||
elif error_code == 4:
|
elif error_code == 4:
|
||||||
print('<p class="text-danger">Error 4: Invalid server Hostname</p>')
|
print('<p class="text-danger">Error 4: Invalid server Hostname</p>', end="")
|
||||||
send_error_notification(device_id, "UHTTPER (error n°4) -> Invalid Server Hostname")
|
send_error_notification(device_id, "UHTTPER (error n°4) -> Invalid Server Hostname")
|
||||||
server_hostname_resets = reset_server_hostname(aircarto_profile_id)
|
server_hostname_resets = reset_server_hostname(aircarto_profile_id)
|
||||||
if server_hostname_resets:
|
if server_hostname_resets:
|
||||||
print("✅server hostname reset successfully")
|
print("✅server hostname reset successfully", end="")
|
||||||
else:
|
else:
|
||||||
print("⛔There were issues with the modem server hostname reinitialize process")
|
print("⛔There were issues with the modem server hostname reinitialize process")
|
||||||
|
# N°11 SERVER CONNECTION ERROR
|
||||||
elif error_code == 11:
|
elif error_code == 11:
|
||||||
print('<p class="text-danger">Error 11: Server connection error</p>')
|
print('<p class="text-danger">Error 11: AirCarto - Server connection error</p>', end="")
|
||||||
|
hardware_reboot_success = modem_hardware_reboot()
|
||||||
|
if hardware_reboot_success:
|
||||||
|
print("✅Modem successfully rebooted and reinitialized")
|
||||||
|
else:
|
||||||
|
print("⛔There were issues with the modem reboot/reinitialize process")
|
||||||
|
# N°22 PSD CSD CONNECTION NOT ESTABLISHED
|
||||||
elif error_code == 22:
|
elif error_code == 22:
|
||||||
print('<p class="text-danger">⚠️Error 22: PSD or CSD connection not established (SARA-R5 need to reset PDP conection)⚠️</p>')
|
print('<p class="text-danger">⚠️Error 22: PSD or CSD connection not established (SARA-R5 need to reset PDP conection)⚠️</p>', end="")
|
||||||
send_error_notification(device_id, "UHTTPER (error n°22) -> PSD or CSD connection not established")
|
send_error_notification(device_id, "UHTTPER (error n°22) -> PSD or CSD connection not established")
|
||||||
psd_csd_resets = reset_PSD_CSD_connection()
|
psd_csd_resets = reset_PSD_CSD_connection()
|
||||||
if psd_csd_resets:
|
if psd_csd_resets:
|
||||||
print("✅PSD CSD connection reset successfully")
|
print("✅PSD CSD connection reset successfully", end="")
|
||||||
else:
|
else:
|
||||||
print("⛔There were issues with the modem CSD PSD reinitialize process")
|
print("⛔There were issues with the modem CSD PSD reinitialize process", end="")
|
||||||
|
# N°26 CONNECTION TIMED OUT
|
||||||
elif error_code == 26:
|
elif error_code == 26:
|
||||||
print('<p class="text-danger">Error 26: Connection timed out</p>')
|
print('<p class="text-danger">Error 26: Connection timed out</p>', end="")
|
||||||
send_error_notification(device_id, "UHTTPER (error n°26) -> Connection timed out")
|
send_error_notification(device_id, "UHTTPER (error n°26) -> Connection timed out")
|
||||||
|
# N°26 CONNECTION LOST
|
||||||
elif error_code == 44:
|
elif error_code == 44:
|
||||||
print('<p class="text-danger">Error 44: Connection lost</p>')
|
print('<p class="text-danger">Error 44: Connection lost</p>')
|
||||||
send_error_notification(device_id, "UHTTPER (error n°44) -> Connection lost")
|
send_error_notification(device_id, "UHTTPER (error n°44) -> Connection lost")
|
||||||
elif error_code == 73:
|
elif error_code == 73:
|
||||||
print('<p class="text-danger">Error 73: Secure socket connect error</p>')
|
print('<p class="text-danger">Error 73: Secure socket connect error</p>', end="")
|
||||||
else:
|
else:
|
||||||
print(f'<p class="text-danger">Unknown error code: {error_code}</p>')
|
print(f'<p class="text-danger">Unknown error code: {error_code}</p>', end="")
|
||||||
else:
|
else:
|
||||||
print('<p class="text-danger">Could not extract error code from response</p>')
|
print('<p class="text-danger">Could not extract error code from response</p>',end="")
|
||||||
|
|
||||||
|
if "ERROR" in response_SARA_9:
|
||||||
|
print('<p class="text-danger">⚠️Sara Module returned an error</p>',end="")
|
||||||
|
print('<p class="text-warning">⚠️set up hardware reboot here???</p>',end="")
|
||||||
|
|
||||||
#Software Reboot
|
|
||||||
#software_reboot_success = modem_complete_reboot_and_reinitialize(modem_version, aircarto_profile_id)
|
|
||||||
#if software_reboot_success:
|
|
||||||
# print("Modem successfully rebooted and reinitialized")
|
|
||||||
#else:
|
|
||||||
# print("There were issues with the modem reboot/reinitialize process")
|
|
||||||
|
|
||||||
# 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅)
|
# 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅)
|
||||||
else:
|
else:
|
||||||
# Si la commande HTTP a réussi
|
# Si la commande HTTP a réussi
|
||||||
print('<span class="badge text-bg-success">✅✅HTTP operation successful.</span>')
|
print('<span class="badge text-bg-success">✅✅HTTP operation successful.</span>',end="")
|
||||||
|
|
||||||
#update SARA_network_status
|
#update SARA_network_status
|
||||||
update_config_sqlite('SARA_network_status', 'connected')
|
update_config_sqlite('SARA_network_status', 'connected')
|
||||||
@@ -972,12 +1101,12 @@ try:
|
|||||||
server_time_formatted = server_datetime.strftime('%Y-%m-%d %H:%M:%S')
|
server_time_formatted = server_datetime.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
#update RTC module do not wait for answer, non blocking
|
#update RTC module do not wait for answer, non blocking
|
||||||
#/usr/bin/python3 /var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py '2024-01-30 12:48:39'
|
#/usr/bin/python3 /var/www/moduleair_pro_4g/RTC/set_with_browserTime.py '2024-01-30 12:48:39'
|
||||||
# Launch RTC update script as non-blocking subprocess
|
# Launch RTC update script as non-blocking subprocess
|
||||||
import subprocess
|
import subprocess
|
||||||
update_command = [
|
update_command = [
|
||||||
"/usr/bin/python3",
|
"/usr/bin/python3",
|
||||||
"/var/www/nebuleair_pro_4g/RTC/set_with_browserTime.py",
|
"/var/www/moduleair_pro_4g/RTC/set_with_browserTime.py",
|
||||||
server_time_formatted
|
server_time_formatted
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1045,10 +1174,16 @@ try:
|
|||||||
# print("Modem successfully rebooted and reinitialized")
|
# print("Modem successfully rebooted and reinitialized")
|
||||||
#else:
|
#else:
|
||||||
# print("There were issues with the modem reboot/reinitialize process")
|
# print("There were issues with the modem reboot/reinitialize process")
|
||||||
|
#Hardware Reboot
|
||||||
|
hardware_reboot_success = modem_hardware_reboot()
|
||||||
|
if hardware_reboot_success:
|
||||||
|
print("✅Modem successfully rebooted and reinitialized")
|
||||||
|
else:
|
||||||
|
print("⛔There were issues with the modem reboot/reinitialize process")
|
||||||
|
|
||||||
|
|
||||||
#5. empty json
|
#5. empty json
|
||||||
print("Empty SARA memory:")
|
print("Empty SARA memory:", end="")
|
||||||
ser_sara.write(b'AT+UDELFILE="sensordata_csv.json"\r')
|
ser_sara.write(b'AT+UDELFILE="sensordata_csv.json"\r')
|
||||||
response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
response_SARA_5 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
||||||
print('<p class="text-danger-emphasis">')
|
print('<p class="text-danger-emphasis">')
|
||||||
@@ -1057,12 +1192,192 @@ try:
|
|||||||
|
|
||||||
if "+CME ERROR" in response_SARA_5:
|
if "+CME ERROR" in response_SARA_5:
|
||||||
print("⛔ Attention CME ERROR ⛔")
|
print("⛔ Attention CME ERROR ⛔")
|
||||||
|
|
||||||
|
'''
|
||||||
|
_ ____ _
|
||||||
|
___ ___ _ __ __| | _ _/ ___| _ __ ___ | |_
|
||||||
|
/ __|/ _ \ '_ \ / _` | | | | \___ \| '_ \ / _ \| __|
|
||||||
|
\__ \ __/ | | | (_| | | |_| |___) | |_) | (_) | |_
|
||||||
|
|___/\___|_| |_|\__,_| \__,_|____/| .__/ \___/ \__|
|
||||||
|
|_|
|
||||||
|
'''
|
||||||
|
if send_uSpot:
|
||||||
|
print('<p class="fw-bold">➡️SEND TO uSPOT SERVERS</p>', end="")
|
||||||
|
|
||||||
|
# 1. Open sensordata_json.json (with correct data size)
|
||||||
|
print("Open JSON:")
|
||||||
|
payload_string = json.dumps(payload_json) # Convert dict to JSON string
|
||||||
|
size_of_string = len(payload_string)
|
||||||
|
command = f'AT+UDWNFILE="sensordata_json.json",{size_of_string}\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_6 = read_complete_response(ser_sara, wait_for_lines=[">"], debug=False)
|
||||||
|
print(response_SARA_6)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
#2. Write to shell
|
||||||
|
print("Write to memory:")
|
||||||
|
ser_sara.write(payload_string.encode())
|
||||||
|
response_SARA_7 = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
||||||
|
print(response_SARA_7)
|
||||||
|
|
||||||
|
#step 4: trigger the request (http_command=1 for GET and http_command=1 for POST)
|
||||||
|
print("****")
|
||||||
|
print("Trigger POST REQUEST")
|
||||||
|
command = f'AT+UHTTPC={uSpot_profile_id},4,"/moduleair?token=2AFF6dQk68daFZ","uSpot_server_response.txt","sensordata_json.json",4\r'
|
||||||
|
ser_sara.write(command.encode('utf-8'))
|
||||||
|
|
||||||
|
response_SARA_8 = read_complete_response(ser_sara, timeout=5, end_of_response_timeout=120, wait_for_lines=["+UUHTTPCR", "+CME ERROR"], debug=True)
|
||||||
|
|
||||||
|
print('<p class="text-danger-emphasis">')
|
||||||
|
print(response_SARA_8)
|
||||||
|
print("</p>", end="")
|
||||||
|
|
||||||
|
# si on recoit la réponse UHTTPCR
|
||||||
|
if "+UUHTTPCR" in response_SARA_8:
|
||||||
|
print("✅ Received +UUHTTPCR response.")
|
||||||
|
lines = response_SARA_8.strip().splitlines()
|
||||||
|
# 1.Vérifier si la réponse contient un message d'erreur CME
|
||||||
|
if "+CME ERROR" in lines[-1]:
|
||||||
|
print("*****")
|
||||||
|
print('<span style="color: red;font-weight: bold;">⛔ATTENTION: CME ERROR</span>')
|
||||||
|
print("error:", lines[-1])
|
||||||
|
print("*****")
|
||||||
|
#update status
|
||||||
|
|
||||||
|
# Gestion de l'erreur spécifique
|
||||||
|
if "No connection to phone" in lines[-1]:
|
||||||
|
print("No connection to the phone.")
|
||||||
|
|
||||||
|
elif "Operation not allowed" in lines[-1]:
|
||||||
|
print("Operation not allowed. This may require a different configuration.")
|
||||||
|
# Actions spécifiques pour ce type d'erreur
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
# 2.Si la réponse contient une réponse HTTP valide
|
||||||
|
# Extract HTTP response code from the last line
|
||||||
|
# ATTENTION: lines[-1] renvoie l'avant dernière ligne et il peut y avoir un soucis avec le OK
|
||||||
|
# rechercher plutot
|
||||||
|
http_response = lines[-1] # "+UUHTTPCR: 0,4,0"
|
||||||
|
parts = http_response.split(',')
|
||||||
|
|
||||||
|
# 2.1 code 0 (HTTP failed)
|
||||||
|
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>')
|
||||||
|
print("*****")
|
||||||
|
|
||||||
|
# Get error code
|
||||||
|
print("Getting error code", end="")
|
||||||
|
command = f'AT+UHTTPER={uSpot_profile_id}\r'
|
||||||
|
ser_sara.write(command.encode('utf-8'))
|
||||||
|
response_SARA_9b = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
||||||
|
print('<p class="text-danger-emphasis">')
|
||||||
|
print(response_SARA_9b)
|
||||||
|
print("</p>", end="")
|
||||||
|
# Extract just the error code
|
||||||
|
error_code = extract_error_code(response_SARA_9b)
|
||||||
|
if error_code is not None:
|
||||||
|
# Display interpretation based on error code
|
||||||
|
if error_code == 0:
|
||||||
|
print('<p class="text-success">No error detected</p>')
|
||||||
|
# INVALID SERVER HOSTNAME
|
||||||
|
elif error_code == 4:
|
||||||
|
print('<p class="text-danger">Error 4: Invalid server Hostname</p>', end="")
|
||||||
|
send_error_notification(device_id, "UHTTPER (4) uSpot Invalid server Hostname")
|
||||||
|
server_hostname_resets = reset_server_hostname_https(uSpot_profile_id)
|
||||||
|
if server_hostname_resets:
|
||||||
|
print("✅server hostname reset successfully")
|
||||||
|
else:
|
||||||
|
print("⛔There were issues with the modem server hostname reinitialize process")
|
||||||
|
# SERVER CONNECTION ERROR
|
||||||
|
elif error_code == 11:
|
||||||
|
print('<p class="text-danger">Error 11: uSpot - Server connection error</p>', end="")
|
||||||
|
elif error_code == 22:
|
||||||
|
print('<p class="text-danger">Error 22: PSD or CSD connection not established</p>', end="")
|
||||||
|
elif error_code == 26:
|
||||||
|
print('<p class="text-danger">Error 26: Connection timed out</p>')
|
||||||
|
elif error_code == 44:
|
||||||
|
print('<p class="text-danger">Error 44: Connection lost</p>')
|
||||||
|
elif error_code == 73:
|
||||||
|
print('<p class="text-danger">Error 73: Secure socket connect error</p>', end="")
|
||||||
|
send_error_notification(device_id, "uSpot - Secure socket connect error")
|
||||||
|
#Software Reboot ??
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f'<p class="text-danger">Unknown error code: {error_code}</p>',end="")
|
||||||
|
else:
|
||||||
|
print('<p class="text-danger">Could not extract error code from response</p>', end="")
|
||||||
|
|
||||||
|
#Pas forcément un moyen de résoudre le soucis
|
||||||
|
|
||||||
|
# 2.2 code 1 (✅✅HHTP / UUHTTPCR succeded✅✅)
|
||||||
|
else:
|
||||||
|
# Si la commande HTTP a réussi
|
||||||
|
print('<span style="font-weight: bold;">✅✅HTTP operation successful.</span>')
|
||||||
|
|
||||||
|
#4. Read reply from server
|
||||||
|
print("Reply from server:")
|
||||||
|
command = f'AT+URDFILE="uSpot_server_response.txt"\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_4b = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
||||||
|
print('<p class="text-success">')
|
||||||
|
print(response_SARA_4b)
|
||||||
|
print("</p>", end="")
|
||||||
|
|
||||||
|
# Initialize http_response_code to 0 as a default value
|
||||||
|
http_response_code = 0
|
||||||
|
|
||||||
|
# Safely extract HTTP code
|
||||||
|
try:
|
||||||
|
http_prefix = "HTTP/"
|
||||||
|
# response_SARA_4b is a string, not a function - use .find() method
|
||||||
|
http_pos = response_SARA_4b.find(http_prefix)
|
||||||
|
|
||||||
|
if http_pos != -1:
|
||||||
|
# Find the space after the HTTP version
|
||||||
|
space_pos = response_SARA_4b.find(" ", http_pos)
|
||||||
|
if space_pos != -1:
|
||||||
|
# Extract the code after the space
|
||||||
|
code_start = space_pos + 1
|
||||||
|
code_end = response_SARA_4b.find(" ", code_start)
|
||||||
|
if code_end != -1:
|
||||||
|
# Extract and convert to integer
|
||||||
|
http_code_str = response_SARA_4b[code_start:code_end]
|
||||||
|
http_response_code = int(http_code_str)
|
||||||
|
print(f"HTTP response code: {http_response_code}")
|
||||||
|
if http_response_code == 201:
|
||||||
|
print('<span style="font-weight: bold;">✅✅HTTP 201 ressource created.</span>')
|
||||||
|
elif http_response_code == 308:
|
||||||
|
print('<span style="font-weight: bold;"> ⚠️⚠️HTTP 308 Redirect, need to set up HTTPS.</span>')
|
||||||
|
server_hostname_resets = reset_server_hostname_https(uSpot_profile_id)
|
||||||
|
if server_hostname_resets:
|
||||||
|
print("✅server hostname reset successfully")
|
||||||
|
else:
|
||||||
|
print("⛔There were issues with the modem server hostname reinitialize process")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# If any error occurs during parsing, keep the default value
|
||||||
|
print(f"Error parsing HTTP code: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#5. empty json
|
||||||
|
print("Empty SARA memory:", end="")
|
||||||
|
command = f'AT+UDELFILE="sensordata_json.json"\r'
|
||||||
|
ser_sara.write((command + '\r').encode('utf-8'))
|
||||||
|
response_SARA_9t = read_complete_response(ser_sara, wait_for_lines=["OK"], debug=False)
|
||||||
|
print(response_SARA_9t)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Calculate and print the elapsed time
|
# Calculate and print the elapsed time
|
||||||
elapsed_time = time.time() - start_time_script
|
elapsed_time = time.time() - start_time_script
|
||||||
print(f"Elapsed time: {elapsed_time:.2f} seconds")
|
print(f"Elapsed time: {elapsed_time:.2f} seconds")
|
||||||
print("<hr>")
|
print("<hr>", end="")
|
||||||
|
print("<hr>", end="")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("An error occurred:", e)
|
print("An error occurred:", e)
|
||||||
|
|||||||
Binary file not shown.
@@ -7,14 +7,16 @@
|
|||||||
|
|
||||||
Script to launch the welcome screen
|
Script to launch the welcome screen
|
||||||
|
|
||||||
|
Avant de tester:
|
||||||
|
sudo systemctl stop moduleair-boot.service
|
||||||
|
|
||||||
|
|
||||||
Pour compiler:
|
Pour compiler:
|
||||||
g++ -Iinclude -Llib welcome_screen.cc -o welcome_screen -lrgbmatrix
|
g++ -I /var/www/moduleair_pro_4g/matrix/include -L /var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_FM6126.cc -o /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_FM6126 -lrgbmatrix
|
||||||
g++ -I /var/www/moduleair_pro_4g/matrix/include -L /var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/welcome_screen.cc -o /var/www/moduleair_pro_4g/matrix/welcome_screen -lrgbmatrix
|
|
||||||
|
|
||||||
|
|
||||||
Pour lancer:
|
Pour lancer:
|
||||||
sudo /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen
|
sudo /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_FM6126
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ void draw_welcome_screen(Canvas *canvas) {
|
|||||||
int letter_spacing = 0;
|
int letter_spacing = 0;
|
||||||
int x = 10, y1 = 20, y2 = 50;
|
int x = 10, y1 = 20, y2 = 50;
|
||||||
|
|
||||||
rgb_matrix::DrawText(canvas, font2, x, y1, myCYAN, &bg_color, "Welcome to", letter_spacing);
|
rgb_matrix::DrawText(canvas, font2, x, y1, myCYAN, &bg_color, "PLOUF", letter_spacing);
|
||||||
rgb_matrix::DrawText(canvas, font2, x, y2, myWHITE, &bg_color, "ModuleAir", letter_spacing);
|
rgb_matrix::DrawText(canvas, font2, x, y2, myWHITE, &bg_color, "ModuleAir", letter_spacing);
|
||||||
|
|
||||||
usleep(2000000); // Display the welcome screen for 2 seconds
|
usleep(2000000); // Display the welcome screen for 2 seconds
|
||||||
BIN
matrix/welcomeScreen/welcome_screen_ICN2037
Normal file
BIN
matrix/welcomeScreen/welcome_screen_ICN2037
Normal file
Binary file not shown.
100
matrix/welcomeScreen/welcome_screen_ICN2037.cc
Normal file
100
matrix/welcomeScreen/welcome_screen_ICN2037.cc
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
__ __ _ _____ ____ _____ __
|
||||||
|
| \/ | / \|_ _| _ \|_ _\ \/ /
|
||||||
|
| |\/| | / _ \ | | | |_) || | \ /
|
||||||
|
| | | |/ ___ \| | | _ < | | / \
|
||||||
|
|_| |_/_/ \_\_| |_| \_\___/_/\_\
|
||||||
|
|
||||||
|
Script to launch the welcome screen
|
||||||
|
|
||||||
|
Avant de tester:
|
||||||
|
sudo systemctl stop moduleair-boot.service
|
||||||
|
|
||||||
|
|
||||||
|
Pour compiler:
|
||||||
|
g++ -I /var/www/moduleair_pro_4g/matrix/include -L /var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_ICN2037.cc -o /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_ICN2037 -lrgbmatrix
|
||||||
|
|
||||||
|
|
||||||
|
Pour lancer:
|
||||||
|
sudo /var/www/moduleair_pro_4g/matrix/welcomeScreen/welcome_screen_ICN2037 \
|
||||||
|
--led-row-addr-type=2 \
|
||||||
|
--led-slowdown-gpio=4 \
|
||||||
|
--led-multiplexing=0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "led-matrix.h"
|
||||||
|
#include "graphics.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
using rgb_matrix::RGBMatrix;
|
||||||
|
using rgb_matrix::Canvas;
|
||||||
|
|
||||||
|
std::atomic<bool> running(true);
|
||||||
|
|
||||||
|
void signal_handler(int signum) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//message d'accueil
|
||||||
|
void draw_welcome_screen(Canvas *canvas) {
|
||||||
|
canvas->Clear();
|
||||||
|
|
||||||
|
rgb_matrix::Color myCYAN(0, 255, 255);
|
||||||
|
rgb_matrix::Color myWHITE(255, 255, 255);
|
||||||
|
rgb_matrix::Color bg_color(0, 0, 0);
|
||||||
|
|
||||||
|
rgb_matrix::Font font1;
|
||||||
|
rgb_matrix::Font font2;
|
||||||
|
|
||||||
|
if (!font1.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/6x9.bdf")) return;
|
||||||
|
if (!font2.LoadFont("/var/www/moduleair_pro_4g/matrix/fonts/9x18B.bdf")) return;
|
||||||
|
|
||||||
|
int letter_spacing = 0;
|
||||||
|
int x = 10, y1 = 20, y2 = 50;
|
||||||
|
|
||||||
|
rgb_matrix::DrawText(canvas, font2, x, y1, myCYAN, &bg_color, "PLOUF", letter_spacing);
|
||||||
|
rgb_matrix::DrawText(canvas, font2, x, y2, myWHITE, &bg_color, "ModuleAir", letter_spacing);
|
||||||
|
|
||||||
|
usleep(2000000); // Display the welcome screen for 2 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Handle signals for graceful exit
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
|
||||||
|
RGBMatrix::Options defaults;
|
||||||
|
defaults.hardware_mapping = "moduleair_pinout";
|
||||||
|
defaults.rows = 64;
|
||||||
|
defaults.cols = 128;
|
||||||
|
defaults.chain_length = 1;
|
||||||
|
defaults.parallel = 1;
|
||||||
|
defaults.row_address_type = 3;
|
||||||
|
defaults.show_refresh_rate = true;
|
||||||
|
defaults.brightness = 100;
|
||||||
|
defaults.pwm_bits = 1;
|
||||||
|
//defaults.panel_type = "FM6126A";
|
||||||
|
defaults.disable_hardware_pulsing = false;
|
||||||
|
|
||||||
|
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
|
||||||
|
if (canvas == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Display welcome screen once
|
||||||
|
draw_welcome_screen(canvas);
|
||||||
|
|
||||||
|
// Sleep briefly to allow for updates
|
||||||
|
usleep(10000000); // 10 s
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
canvas->Clear();
|
||||||
|
delete canvas;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
34
sqlite/read_config.py
Normal file
34
sqlite/read_config.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
'''
|
||||||
|
____ ___ _ _ _
|
||||||
|
/ ___| / _ \| | (_) |_ ___
|
||||||
|
\___ \| | | | | | | __/ _ \
|
||||||
|
___) | |_| | |___| | || __/
|
||||||
|
|____/ \__\_\_____|_|\__\___|
|
||||||
|
|
||||||
|
Script to read data from a sqlite database
|
||||||
|
/usr/bin/python3 /var/www/moduleair_pro_4g/sqlite/read_config.py
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Connect to the SQLite database
|
||||||
|
conn = sqlite3.connect("/var/www/moduleair_pro_4g/sqlite/sensors.db")
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|
||||||
|
query = f"SELECT * FROM config_table"
|
||||||
|
cursor.execute(query)
|
||||||
|
|
||||||
|
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
rows.reverse() # Reverse the order in Python (to get ascending order)
|
||||||
|
|
||||||
|
|
||||||
|
# Display the results
|
||||||
|
for row in rows:
|
||||||
|
print(row)
|
||||||
|
|
||||||
|
# Close the database connection
|
||||||
|
conn.close()
|
||||||
140
update_firmware.sh
Normal file
140
update_firmware.sh
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# moduleair Pro 4G - Comprehensive Update Script
|
||||||
|
# This script performs a complete system update including git pull,
|
||||||
|
# config initialization, and service management
|
||||||
|
# Non-interactive version for WebUI
|
||||||
|
|
||||||
|
#to test:
|
||||||
|
# sudo chmod +x /var/www/moduleair_pro_4g/update_firmware.sh
|
||||||
|
# sudo /var/www/moduleair_pro_4g/update_firmware.sh
|
||||||
|
|
||||||
|
echo "======================================"
|
||||||
|
echo "ModuleAir Pro 4G - Firmware Update"
|
||||||
|
echo "======================================"
|
||||||
|
echo "Started at: $(date)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
cd /var/www/moduleair_pro_4g
|
||||||
|
|
||||||
|
# Ensure this script is executable
|
||||||
|
chmod +x /var/www/moduleair_pro_4g/update_firmware.sh
|
||||||
|
|
||||||
|
# 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..."
|
||||||
|
|
||||||
|
# Disable filemode to prevent permission issues
|
||||||
|
git -C /var/www/moduleair_pro_4g config core.fileMode false
|
||||||
|
check_status "Git fileMode disabled"
|
||||||
|
|
||||||
|
# Fetch latest changes
|
||||||
|
git fetch origin
|
||||||
|
check_status "Git fetch"
|
||||||
|
|
||||||
|
# Show current branch
|
||||||
|
print_status "Current branch: $(git branch --show-current)"
|
||||||
|
|
||||||
|
# Check for local changes
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
print_status "Warning: Local changes detected, stashing..."
|
||||||
|
git stash push -m "Auto-stash before update $(date)"
|
||||||
|
check_status "Git stash"
|
||||||
|
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/moduleair_pro_4g/sqlite/set_config_smart.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/moduleair_pro_4g/update_firmware.sh
|
||||||
|
sudo chmod 755 /var/www/moduleair_pro_4g/sqlite/*.py
|
||||||
|
sudo chmod 755 /var/www/moduleair_pro_4g/NPM/*.py
|
||||||
|
sudo chmod 755 /var/www/moduleair_pro_4g/BME280/*.py
|
||||||
|
sudo chmod 755 /var/www/moduleair_pro_4g/SARA/*.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=(
|
||||||
|
"moduleair-npm-data.timer"
|
||||||
|
"moduleair-sara-data.timer"
|
||||||
|
"moduleair-bme280-data.timer"
|
||||||
|
)
|
||||||
|
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
if systemctl list-unit-files | grep -q "$service"; then
|
||||||
|
# Check if service is enabled before restarting
|
||||||
|
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
|
||||||
|
print_status "Restarting enabled service: $service"
|
||||||
|
sudo systemctl restart "$service"
|
||||||
|
if systemctl is-active --quiet "$service"; then
|
||||||
|
print_status "✓ $service is running"
|
||||||
|
else
|
||||||
|
print_status "⚠ $service failed to start"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_status "ℹ Service $service is disabled, skipping restart"
|
||||||
|
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/moduleair_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/moduleair_pro_4g/logs -name "*.log" -size +10M -exec truncate -s 0 {} \;
|
||||||
|
check_status "Log cleanup"
|
||||||
|
|
||||||
|
print_status ""
|
||||||
|
print_status "======================================"
|
||||||
|
print_status "Update completed successfully!"
|
||||||
|
print_status "======================================"
|
||||||
|
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user