Files
nebuleair_pro_4g/update_firmware_from_file.sh
PaulVua 11ac2b184a v1.8.1: Alignement upload offline sur le flow online (UX + self-heal)
L'upload offline avait deux defauts vs l'update online:
- pas de self-heal des services (pas de Step 3c equivalent)
- ancienne UX synchrone (spinner sans feedback pendant 60-90s)

Maintenant:
- update_firmware_from_file.sh: nouveau Step 4c qui appelle
  setup_services.sh (alignement avec online)
- launcher.php upload_firmware: lance le script en background et
  reutilise le mecanisme log/done de l'update online
- admin.html uploadFirmware: apres l'upload du ZIP, bascule sur
  le meme systeme de polling/progress que l'online (avec mapping
  d'etapes specifique au script offline)
- Detection de fin par substring 'completed successfully!' (matche
  les 2 markers finaux differents)

Fix au passage: le bouton 'Upload & Install' restait bloque sur
'Installing...' apres succes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:00:10 +02:00

227 lines
7.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# NebuleAir Pro 4G - Update from uploaded ZIP file
# Usage: sudo ./update_firmware_from_file.sh /path/to/extracted/folder
echo "======================================"
echo "NebuleAir Pro 4G - Firmware Update (File Upload)"
echo "======================================"
echo "Started at: $(date)"
echo ""
# Set target directory
TARGET_DIR="/var/www/nebuleair_pro_4g"
EXCLUDE_FILE="$TARGET_DIR/.update-exclude"
# 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
}
# Validate arguments
if [ -z "$1" ]; then
print_status "✗ Error: No source directory provided"
print_status "Usage: $0 /path/to/extracted/folder"
exit 1
fi
SOURCE_DIR="$1"
if [ ! -d "$SOURCE_DIR" ]; then
print_status "✗ Error: Source directory does not exist: $SOURCE_DIR"
exit 1
fi
# Step 1: Validate source and compare versions
print_status "Step 1: Validating update package..."
if [ ! -f "$SOURCE_DIR/VERSION" ]; then
print_status "✗ Error: VERSION file not found in update package"
exit 1
fi
NEW_VERSION=$(cat "$SOURCE_DIR/VERSION" | tr -d '[:space:]')
OLD_VERSION="unknown"
if [ -f "$TARGET_DIR/VERSION" ]; then
OLD_VERSION=$(cat "$TARGET_DIR/VERSION" | tr -d '[:space:]')
fi
print_status "Current version: $OLD_VERSION"
print_status "New version: $NEW_VERSION"
# Step 2: Rsync with exclusions from .update-exclude
print_status ""
print_status "Step 2: Syncing files..."
# Build exclude args: use .update-exclude from the SOURCE (new version) if available,
# otherwise fall back to the one already installed
if [ -f "$SOURCE_DIR/.update-exclude" ]; then
EXCLUDE_FILE="$SOURCE_DIR/.update-exclude"
print_status "Using .update-exclude from update package"
elif [ -f "$TARGET_DIR/.update-exclude" ]; then
EXCLUDE_FILE="$TARGET_DIR/.update-exclude"
print_status "Using .update-exclude from current installation"
else
print_status "⚠ No .update-exclude file found, using built-in defaults"
# Fallback minimal exclusions
EXCLUDE_FILE=$(mktemp)
cat > "$EXCLUDE_FILE" <<'EXCL'
sqlite/sensors.db
sqlite/*.db-journal
sqlite/*.db-wal
logs/
.git/
config.json
deviceID.txt
wifi_list.csv
envea/data/
NPM/data/
*.lock
EXCL
fi
rsync -av --delete --exclude-from="$EXCLUDE_FILE" "$SOURCE_DIR/" "$TARGET_DIR/"
check_status "File sync (rsync)"
# Fix ownership and permissions
print_status "Fixing ownership..."
chown -R www-data:www-data "$TARGET_DIR/"
check_status "Ownership fix (chown)"
# Step 3: Update database configuration
print_status ""
print_status "Step 3: Updating database configuration..."
/usr/bin/python3 "$TARGET_DIR/sqlite/set_config.py"
check_status "Database configuration update"
# Step 4: Check and fix file permissions
print_status ""
print_status "Step 4: Checking file permissions..."
chmod +x "$TARGET_DIR/update_firmware.sh"
chmod +x "$TARGET_DIR/update_firmware_from_file.sh"
chmod 755 "$TARGET_DIR/sqlite/"*.py
chmod 755 "$TARGET_DIR/NPM/"*.py
chmod 755 "$TARGET_DIR/BME280/"*.py
chmod 755 "$TARGET_DIR/SARA/"*.py
chmod 755 "$TARGET_DIR/envea/"*.py
chmod 755 "$TARGET_DIR/MPPT/"*.py 2>/dev/null
chmod 755 "$TARGET_DIR/wifi/"*.py 2>/dev/null
check_status "File permissions update"
# Step 4b: Ensure Apache/PHP config allows file uploads (.htaccess + php.ini)
APACHE_MAIN_CONF="/etc/apache2/apache2.conf"
if grep -q '<Directory /var/www/>' "$APACHE_MAIN_CONF"; then
if ! sed -n '/<Directory \/var\/www\/>/,/<\/Directory>/p' "$APACHE_MAIN_CONF" | grep -q "AllowOverride All"; then
sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' "$APACHE_MAIN_CONF"
print_status "✓ AllowOverride All enabled for Apache"
APACHE_CHANGED=true
fi
fi
PHP_INI=$(php -r "echo php_ini_loaded_file();" 2>/dev/null)
if [ -n "$PHP_INI" ]; then
CURRENT_UPLOAD=$(grep -oP 'upload_max_filesize = \K\d+' "$PHP_INI" 2>/dev/null)
if [ -n "$CURRENT_UPLOAD" ] && [ "$CURRENT_UPLOAD" -lt 50 ]; then
sed -i 's/upload_max_filesize = .*/upload_max_filesize = 50M/' "$PHP_INI"
sed -i 's/post_max_size = .*/post_max_size = 55M/' "$PHP_INI"
print_status "✓ PHP upload limits set to 50M"
APACHE_CHANGED=true
fi
fi
if [ "${APACHE_CHANGED:-false}" = true ]; then
systemctl reload apache2 2>/dev/null
print_status "✓ Apache reloaded"
fi
# Step 4c: Reconcile systemd services with the repo (self-heal)
# Same logic as update_firmware.sh: re-run setup_services.sh so any missing,
# masked, or newly-added service in the canonical setup script is installed.
# git config core.fileMode=false strips +x, so chmod first.
print_status ""
print_status "Step 4c: Reconciling systemd services with setup_services.sh..."
SETUP_SERVICES="$TARGET_DIR/services/setup_services.sh"
if [ -f "$SETUP_SERVICES" ]; then
chmod +x "$SETUP_SERVICES"
"$SETUP_SERVICES"
check_status "Setup services reconciliation"
else
print_status "⚠ setup_services.sh not found, skipping"
fi
# Step 5: Restart critical services
print_status ""
print_status "Step 5: Managing system services..."
services=(
"nebuleair-npm-data.timer"
"nebuleair-envea-data.timer"
"nebuleair-sara-data.timer"
"nebuleair-bme280-data.timer"
"nebuleair-mppt-data.timer"
"nebuleair-noise-data.timer"
"nebuleair-wifi-powersave.timer"
)
for service in "${services[@]}"; do
if systemctl list-unit-files | grep -q "$service"; then
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
print_status "Restarting enabled service: $service"
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 6: System health check
print_status ""
print_status "Step 6: System health check..."
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
if [ -f "$TARGET_DIR/sqlite/sensors.db" ]; then
print_status "✓ Database file exists"
else
print_status "⚠ Warning: Database file not found"
fi
# Cleanup logs > 10MB
find "$TARGET_DIR/logs" -name "*.log" -size +10M -exec truncate -s 0 {} \;
check_status "Log cleanup"
# Step 7: Cleanup temporary files
print_status ""
print_status "Step 7: Cleaning up temporary files..."
rm -rf /tmp/nebuleair_update/
check_status "Temp cleanup"
print_status ""
print_status "======================================"
print_status "Update from $OLD_VERSION to $NEW_VERSION completed successfully!"
print_status "======================================"
exit 0