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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user