#!/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 '' "$APACHE_MAIN_CONF"; then
if ! sed -n '//,/<\/Directory>/p' "$APACHE_MAIN_CONF" | grep -q "AllowOverride All"; then
sed -i '//,/<\/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 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