FEAT-DEEPSLEEP: Display-Refresh-Timer + CPU-Throttle + Admin Power-Tab

DeepSleep-Modus fuer das ePaper-Display:
- dashboard.py: _is_deepsleep(), _next_wake(), _seconds_until_wake(),
                _apply_cpu_throttle() (Pi auf 600MHz im Sleep)
- 60s sleep-chunks in main loop statt festes Intervall
- render_full() mit deepsleep_active + next_wake params
- ZZZ-Indikator + CPU-Throttle-Badge wenn schlafend
- load_config() auto-filled 'power' defaults aus POWER_DEFAULTS

Admin UI (templates/power.html + admin.py):
- /power (GET+POST): DeepSleep-Toggle, Wake-Times, CPU-Throttle-Settings
- /api/power (JSON): live status polling fuer Sidebar-Badge
- /api/wake (POST): manuelles Display-Aufwecken aus dem Admin
- templates/index.html: Energie-Sidebar-Link + Live-Status-Badge
  (Zz / wach / an / aus) via refreshPowerStatus()
This commit is contained in:
Patrik
2026-08-31 00:47:46 +02:00
parent 5a83826aa5
commit 5de086e1b1
4 changed files with 472 additions and 15 deletions
+118
View File
@@ -690,6 +690,124 @@ def api_net_forget():
return jsonify({"ok": rc == 0, "message": out or err})
# ============================================================================
# Power / DeepSleep — Energie-Tab
# ============================================================================
@app.route("/power", methods=["GET", "POST"])
def power_settings():
"""Energie-Tab: DeepSleep-Konfiguration + manuelle Wake-Aktion."""
a = require_auth()
if a: return a
cfg = dashboard_mod.load_config()
power = cfg.get("power", {})
if request.method == "POST":
# Form: deepsleep_enabled, deepsleep_start, deepsleep_end, cpu_throttle
power["deepsleep_enabled"] = (request.form.get("deepsleep_enabled") == "on")
start = request.form.get("deepsleep_start", "23:30").strip()
end = request.form.get("deepsleep_end", "06:30").strip()
# Validierung HH:MM
import re
if re.match(r"^\d{1,2}:\d{2}$", start):
power["deepsleep_start"] = start
if re.match(r"^\d{1,2}:\d{2}$", end):
power["deepsleep_end"] = end
power["cpu_throttle"] = (request.form.get("cpu_throttle") == "on")
power["wifi_off"] = (request.form.get("wifi_off") == "on")
cfg["power"] = power
dashboard_mod.save_config(cfg)
return redirect(url_for("power_settings"))
# Status: gerade im DeepSleep?
from datetime import datetime
now = datetime.now()
is_sleep = False
next_wake = None
if power.get("deepsleep_enabled", False):
try:
def _to_min(s):
h, m = s.split(":")
return int(h) * 60 + int(m)
now_min = now.hour * 60 + now.minute
s = _to_min(power.get("deepsleep_start", "23:00"))
e = _to_min(power.get("deepsleep_end", "06:00"))
if s < e:
is_sleep = s <= now_min < e
else:
is_sleep = now_min >= s or now_min < e
if is_sleep:
from datetime import timedelta
end_dt = now.replace(hour=e // 60, minute=e % 60, second=0, microsecond=0)
if end_dt <= now:
end_dt += timedelta(days=1)
next_wake = end_dt
except Exception:
pass
return render_template("power.html",
power=power,
is_deepsleep=is_sleep,
next_wake=next_wake.strftime("%H:%M") if next_wake else None,
now=now.strftime("%H:%M"))
@app.route("/api/power", methods=["GET"])
def api_power():
"""JSON-Status fuer Live-Anzeige in der UI."""
a = require_auth()
if a: return a
cfg = dashboard_mod.load_config()
power = cfg.get("power", {})
from datetime import datetime, timedelta
now = datetime.now()
is_sleep = False
next_wake = None
if power.get("deepsleep_enabled", False):
try:
def _to_min(s):
h, m = s.split(":")
return int(h) * 60 + int(m)
now_min = now.hour * 60 + now.minute
s = _to_min(power.get("deepsleep_start", "23:00"))
e = _to_min(power.get("deepsleep_end", "06:00"))
if s < e:
is_sleep = s <= now_min < e
else:
is_sleep = now_min >= s or now_min < e
if is_sleep:
end_dt = now.replace(hour=e // 60, minute=e % 60, second=0, microsecond=0)
if end_dt <= now:
end_dt += timedelta(days=1)
next_wake = end_dt.strftime("%H:%M")
secs_left = int((end_dt - now).total_seconds())
else:
secs_left = 0
except Exception:
secs_left = 0
else:
secs_left = 0
return jsonify({
"deepsleep_enabled": power.get("deepsleep_enabled", False),
"deepsleep_start": power.get("deepsleep_start", "23:30"),
"deepsleep_end": power.get("deepsleep_end", "06:30"),
"cpu_throttle": power.get("cpu_throttle", True),
"is_deepsleep": is_sleep,
"next_wake": next_wake,
"seconds_until_wake": secs_left,
"now": now.strftime("%H:%M"),
})
@app.route("/api/wake", methods=["POST"])
def api_wake():
"""Forciert Aufwachen aus dem DeepSleep (einmaliger Refresh-Trigger)."""
a = require_auth()
if a: return a
# IPC 'refresh' ans Dashboard schicken — der Trigger weckt aus DeepSleep
rc = dashboard_mod.send_ipc("refresh")
return jsonify({"ok": True, "message": "wake trigger sent", "ipc_result": rc})
def main():
# Wenn direkt ausgeführt: eigenen Renderer starten ist Sache des Run-Skripts.
# Wir nehmen einfach Port 8080.