Nouvelle fonctionnalité permettant de mettre à jour le firmware sans connexion internet, via upload d'un fichier .zip depuis l'interface admin. Fichiers ajoutés: - update_firmware_from_file.sh (rsync + exclusions + chown + restart services) - .update-exclude (liste d'exclusions évolutive, versionnée) - html/.htaccess (limite upload PHP 50MB) Fichiers modifiés: - html/launcher.php (handler upload_firmware) - html/admin.html (UI upload + barre de progression) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
186 lines
5.3 KiB
Bash
186 lines
5.3 KiB
Bash
#!/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 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
|