feat(station): optionaler Zeitschalter schaltet die Station selbst ab

"on"/"recovery on"/"migrate on" nehmen jetzt "--for <minuten>" (1-240): die
Station schaltet sich danach selbsttaetig ab und kehrt ins Heim-WLAN zurueck.
Das hilft, wenn man ueber WLAN angemeldet ist - "on" macht wlan0 zum
Zugangspunkt und reisst jede SSH-Sitzung ab.

Ausdruecklich optional: ohne "--for" entsteht kein Zeitschalter und alles
verhaelt sich exakt wie bisher. Dazu "renew [<minuten>]" (Uhr zurueckstellen),
"hold" (abbestellen, Station bleibt an) und eine Restzeit in "status"; "off"
raeumt einen laufenden Zeitschalter mit ab.

Umgesetzt als transienter systemd-Timer unter festem Unit-Namen (systemd-run),
Zustand in RUN_DIR/autooff.json. Vor dem Abschalten wird per "ss" geprueft, ob
noch eine Verbindung auf Port 80 aus dem Stationsnetz offen ist - dann wird um
rund eine Minute verschoben, hoechstens zehnmal (Begruendung im Kommentar).
Das Panel zeigt den Zeitschalter als eigene Zeile, aber nur wenn einer laeuft.
This commit is contained in:
Michael Sauer
2026-09-14 19:39:42 +02:00
parent f105a0eb69
commit 1f01b29b8e
5 changed files with 841 additions and 17 deletions
+17
View File
@@ -281,6 +281,23 @@ def blocks(state: dict) -> list[tuple[str, str]]:
# ohne erkannte Lampe steht die SSID ohnehin als grosse Zeile da.
out.append((f"SSID: {ssid}", ROLE_DETAIL))
# Der optionale Zeitschalter, in BEIDEN Modi und vor den reinen Zaehlerzeilen: ein
# Zeitschalter, den man am Panel nicht sieht, ist schlimmer als keiner - man glaubt,
# die Station laeuft, und sie ist laengst aus. Fehlt er (der Regelfall, denn er
# entsteht nur mit "--for"), fehlt die Zeile ersatzlos.
#
# Er steht damit VOR "Ausgeliefert"/"zuletzt ausgeliefert": im vollsten Zustand
# passen fuenf Beiwerkzeilen aufs Panel (siehe BOTTOM_MARGIN_PX), die sechste faellt
# unten heraus. Von diesen beiden Auskuenften ist "wann geht die Station aus"
# diejenige, nach der man ueberhaupt aufs Panel sieht - der Zaehler der letzten
# Auslieferung steht auch noch in "ninlight-station status".
autooff_minutes = state.get("autooff_minutes")
if isinstance(autooff_minutes, int) and not isinstance(autooff_minutes, bool):
if autooff_minutes > 0:
out.append(("Auto-Aus in {} min".format(autooff_minutes), ROLE_DETAIL))
else:
out.append(("Auto-Aus steht an", ROLE_DETAIL))
served = state.get("served")
out.append((f"Ausgeliefert: {served if isinstance(served, int) else 0}", ROLE_DETAIL))