Files
nebuleair_pro_4g/wifi/power_save.py
PaulVua 5777b35770 Add WiFi and HDMI power saving features for remote sensors
Implements power saving optimizations to extend battery life on solar-powered remote air quality sensors:

- WiFi Power Saving: Disable WiFi 10 minutes after boot to save ~100-200mA
  - Configurable via web UI checkbox in admin panel
  - WiFi automatically re-enables after reboot for 10-minute configuration window
  - Systemd timer (nebuleair-wifi-powersave.timer) manages automatic disable
  - New wifi/power_save.py script checks database config and disables WiFi via nmcli

- HDMI Disable: Added hdmi_blanking=2 to boot config to save ~20-30mA
  - Automatically configured during installation

- Database: Added wifi_power_saving boolean config (default: disabled)
  - Uses INSERT OR IGNORE for safe updates to existing installations

- UI: Added checkbox control in admin.html for WiFi power saving
  - Includes helpful description of power savings and behavior

- Services: Updated setup_services.sh and update_firmware.sh to manage new timer

Total power savings: ~120-230mA when WiFi power saving enabled

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-13 12:05:21 +01:00

129 lines
3.8 KiB
Python
Executable File

#!/usr/bin/env python3
r'''
__ ___ ___ ___ ____
\ \ / (_) _(_) | | _ \ _____ _____ _ __
\ \ /\ / /| | | | | | | |_) / _ \ \ /\ / / _ \ '__|
\ V V / | | | | | | | __/ (_) \ V V / __/ |
\_/\_/ |_|_| |_|_| |_| \___/ \_/\_/ \___|_|
____
/ ___| __ ___ _____
\___ \ / _` \ \ / / _ \
___) | (_| |\ V / __/
|____/ \__,_| \_/ \___|
WiFi Power Saving Script
Disables WiFi after 10 minutes of boot if wifi_power_saving is enabled in config.
Saves ~100-200mA of power consumption.
Usage:
/usr/bin/python3 /var/www/nebuleair_pro_4g/wifi/power_save.py
'''
import sqlite3
import subprocess
import sys
import datetime
# Paths
DB_PATH = "/var/www/nebuleair_pro_4g/sqlite/sensors.db"
LOG_PATH = "/var/www/nebuleair_pro_4g/logs/app.log"
def log_message(message):
"""Write message to log file with timestamp"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] [WiFi Power Save] {message}\n"
try:
with open(LOG_PATH, "a") as log_file:
log_file.write(log_entry)
except Exception as e:
print(f"Failed to write to log: {e}", file=sys.stderr)
print(log_entry.strip())
def get_config_value(key):
"""Read configuration value from database"""
try:
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("SELECT value FROM config_table WHERE key = ?", (key,))
result = cursor.fetchone()
conn.close()
return result[0] if result else None
except sqlite3.Error as e:
log_message(f"Database error: {e}")
return None
def is_wifi_enabled():
"""Check if WiFi radio is currently enabled"""
try:
result = subprocess.run(
["nmcli", "radio", "wifi"],
capture_output=True,
text=True,
timeout=10
)
return result.stdout.strip() == "enabled"
except Exception as e:
log_message(f"Failed to check WiFi status: {e}")
return None
def disable_wifi():
"""Disable WiFi radio using nmcli"""
try:
result = subprocess.run(
["sudo", "nmcli", "radio", "wifi", "off"],
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
log_message("WiFi disabled successfully - saving ~100-200mA power")
return True
else:
log_message(f"Failed to disable WiFi: {result.stderr}")
return False
except subprocess.TimeoutExpired:
log_message("Timeout while trying to disable WiFi")
return False
except Exception as e:
log_message(f"Error disabling WiFi: {e}")
return False
def main():
"""Main function"""
log_message("WiFi power save script started")
# Check if wifi_power_saving is enabled in config
wifi_power_saving = get_config_value("wifi_power_saving")
if wifi_power_saving is None:
log_message("wifi_power_saving config not found - skipping (run migration or set_config.py)")
return 0
if wifi_power_saving == "0":
log_message("WiFi power saving is disabled in config - WiFi will remain enabled")
return 0
log_message("WiFi power saving is enabled in config")
# Check current WiFi status
wifi_enabled = is_wifi_enabled()
if wifi_enabled is None:
log_message("Could not determine WiFi status - aborting")
return 1
if not wifi_enabled:
log_message("WiFi is already disabled - nothing to do")
return 0
# Disable WiFi
log_message("Disabling WiFi after 10-minute configuration window...")
if disable_wifi():
log_message("WiFi power save completed successfully")
return 0
else:
log_message("WiFi power save failed")
return 1
if __name__ == "__main__":
sys.exit(main())