feat(station): Panel zeigt Rettungs-Bildsatz, Lebenszeichen und klarere LAN-Zeile

display.py/_read_imageset() prueft jetzt das zum Modus passende Verzeichnispaar
(rescue-* im Rettungsmodus, migrate-* in der Migration) - vorher pruefte die
Fusszeile immer nur den Migrationssatz, auch im Rettungsmodus.

render.py haengt einen kleinen Punkt an die Servierzeile, solange
display.py:_recent_request() eine Anfrage der letzten ACTIVITY_WINDOW_S
Sekunden meldet - faehrt ueber den vorhandenen Textvergleich mit, kein
zusaetzlicher Zeichentakt fuer das E-Paper.

Aus-Zustand: "no network" hiess fuer einen Bediener wie "der Migrations-/
Rettungs-Zugangspunkt ist aus" - gemeint ist das Gegenteil, das Heimnetz.
Jetzt "LAN: <ip>" bzw. "LAN: not connected".

run_daemon() faengt jetzt jeden Fehler beim Erheben des Zustands ab
(_safe_read_state) statt die Schleife zu beenden - sonst friert das Panel
nach einem einzigen unerwarteten Lesefehler fuer immer auf dem letzten Bild
ein, ohne dass das am Geraet zu erkennen waere.
This commit is contained in:
Michael Sauer
2026-09-23 21:22:42 +02:00
parent ac8dfb1077
commit 6d0f79a6ac
3 changed files with 303 additions and 23 deletions
+21 -2
View File
@@ -316,9 +316,17 @@ def blocks(state: dict) -> list[tuple[str, str]]:
# die Zeile weg statt eine Adresse zu erfinden. Ist das Feld da, aber leer, sagt
# die Zeile ausdruecklich, dass kein Netz da ist: "keine Zeile" und "kein Netz"
# sind zwei verschiedene Auskuenfte, und die zweite erklaert, warum SSH scheitert.
#
# Beschriftung "LAN" statt eines nackten "IP"/"no network": Rueckmeldung eines
# Bedieners (2026-09-23) las "no network" so, als sei damit der Migrations-/
# Rettungs-Zugangspunkt gemeint (der ja tatsaechlich aus ist, die Station ist
# ausgeschaltet). Gemeint ist hier aber das GEGENTEIL - das gewoehnliche Heimnetz,
# in dem der Pi als normaler Rechner haengt, sobald die Station aus ist (siehe
# Kommentar oben). "LAN" grenzt das eindeutig vom Zugangspunkt-Netz ab, das auf
# jedem ANDEREN Statusbild als SSID steht.
ip = state.get("ip")
if isinstance(ip, str):
out.append((f"IP {ip}" if ip else "no network", ROLE_DETAIL))
out.append((f"LAN: {ip}" if ip else "LAN: not connected", ROLE_DETAIL))
return out + footer
if mode not in ("recovery", "migration"):
@@ -424,7 +432,18 @@ def blocks(state: dict) -> list[tuple[str, str]]:
out.append(("Auto-off pending", ROLE_DETAIL))
served = state.get("served")
out.append((f"Served: {served if isinstance(served, int) else 0}", ROLE_DETAIL))
served_line = f"Served: {served if isinstance(served, int) else 0}"
# Lebenszeichen: ein kleiner Punkt, sobald IRGENDEINE Anfrage vor kurzem hereinkam
# (display.py:_recent_request() - auch eine erfolglose zaehlt). Angehaengt an eine
# bereits bestehende, immer vorhandene Zeile statt an einer eigenen Stelle im Bild
# gezeichnet: er braucht dadurch keine eigene Zeichenlogik in compose() und keinen
# eigenen Platz im knappen Zeilenbudget, und er faehrt ueber denselben
# Textvergleich (RefreshPlanner.decide() in display.py) mit, der ueber ein
# Neuzeichnen entscheidet - kein zusaetzlicher Takt, kein zusaetzliches Wachhalten
# des E-Papers, siehe display.py:ACTIVITY_WINDOW_S fuer die volle Begruendung.
if state.get("activity"):
served_line += " •"
out.append((served_line, ROLE_DETAIL))
recent = state.get("recent")
if isinstance(recent, list) and recent: