feat(station): Panel und CLI auf Englisch, IP-Zeile im Off-Bild
Die deutschen Texte sahen auf dem 2,13"-Panel merkwuerdig aus - jetzt sind alle Texte, die der Bediener am Geraet oder auf der Konsole sieht, englisch: Modus-, Phasen-, Bildsatz- und Abschlussbeschriftungen in render.py, die Bedientexte des Tasterdienstes und saemtliche Ausgaben von ninlight-station inklusive des Hilfetexts. Die Testbeschreibungen der Suiten bleiben deutsch - die liest nur, wer die Tests laufen laesst. Neu im Off-Bild: die Adresse, unter der der Pi gerade erreichbar ist. Ist die Station aus, ist der Pi ein gewoehnlicher Rechner im Heimnetz, und die einzige Auskunft, die man dann am Panel braucht, ist die fuer SSH. Die Adresse kommt aus einem UDP-Socket, der nur verbunden und nie benutzt wird (kein Datenverkehr, kein DNS, kein Unterprozess in einer 5-s-Schleife) und liefert genau die Adresse der Standardroute. Ohne Netz sagt die Zeile ausdruecklich "no network": eine fehlende Zeile wuerde offen lassen, warum SSH gerade scheitert. Die Zeile gibt es NUR im Off-Bild - im Betrieb traegt wlan0 die Adresse des Zugangspunkts, die ohnehin auf jedem Statusbild steht.
This commit is contained in:
@@ -139,7 +139,7 @@ class Action:
|
||||
diesem Moment gar nicht vor Augen hat."""
|
||||
|
||||
def __init__(self, label: str, argv=(), kind: str = "cli", confirm: bool = True,
|
||||
done_text: str | None = None, confirm_title: str = "RUECKFRAGE",
|
||||
done_text: str | None = None, confirm_title: str = "CONFIRM",
|
||||
confirm_details=None) -> None:
|
||||
self.label = label
|
||||
self.argv = tuple(argv)
|
||||
@@ -154,16 +154,16 @@ class Action:
|
||||
|
||||
|
||||
def action_shutdown() -> Action:
|
||||
return Action("Herunterfahren", kind="shutdown", confirm=True,
|
||||
done_text="Faehrt herunter")
|
||||
return Action("Shut down", kind="shutdown", confirm=True,
|
||||
done_text="Shutting down")
|
||||
|
||||
|
||||
def action_migrate_listen() -> Action:
|
||||
"""Der naheliegende Weg in die Migration: Zugangspunkt an, aber es geht NICHTS raus,
|
||||
bis der Bediener eine erkannte Lampe freigibt ("arm"). Deshalb steht dieser Eintrag
|
||||
vor dem Automatikmodus und traegt die uebliche Rueckfrage."""
|
||||
return Action("Migration: horchen", ("migrate", "on", "--listen"),
|
||||
done_text="Migration: horcht")
|
||||
return Action("Migration: listen", ("migrate", "on", "--listen"),
|
||||
done_text="Migration: listening")
|
||||
|
||||
|
||||
def action_migrate_auto() -> Action:
|
||||
@@ -179,11 +179,11 @@ def action_migrate_auto() -> Action:
|
||||
danach nicht mehr mit seiner App spricht. Ein "Wirklich?" fragt nach etwas, das der
|
||||
Bediener in diesem Moment gar nicht vor Augen hat; deshalb steht die Folge in der
|
||||
Rueckfrage selbst."""
|
||||
return Action("Migration: AUTOMATIK", ("migrate", "on"),
|
||||
done_text="Migration: automatisch",
|
||||
confirm_title="ACHTUNG",
|
||||
confirm_details=["Flasht JEDE alte Klyqa-Lampe",
|
||||
"in Reichweite - auch fremde"])
|
||||
return Action("Migration: AUTOMATIC", ("migrate", "on"),
|
||||
done_text="Migration: automatic",
|
||||
confirm_title="WARNING",
|
||||
confirm_details=["Flashes EVERY old Klyqa lamp",
|
||||
"in range - other people's too"])
|
||||
|
||||
|
||||
def action_imageset(current: str) -> Action:
|
||||
@@ -196,19 +196,19 @@ def action_imageset(current: str) -> Action:
|
||||
Lampe, die sie erwischt."""
|
||||
target = "mit-wlan" if current != "mit-wlan" else "ohne-wlan"
|
||||
if target == "mit-wlan":
|
||||
return Action("Umschalten: mit WLAN", ("bildsatz", "mit-wlan"),
|
||||
done_text="Bildsatz: mit WLAN",
|
||||
confirm_title="ACHTUNG",
|
||||
confirm_details=["Jede migrierte Lampe bekommt",
|
||||
"dein WLAN-Passwort"])
|
||||
return Action("Umschalten: ohne WLAN", ("bildsatz", "ohne-wlan"),
|
||||
done_text="Bildsatz: ohne WLAN")
|
||||
return Action("Switch to: with Wi-Fi", ("bildsatz", "mit-wlan"),
|
||||
done_text="Image set: with Wi-Fi",
|
||||
confirm_title="WARNING",
|
||||
confirm_details=["Every migrated lamp receives",
|
||||
"your Wi-Fi password"])
|
||||
return Action("Switch to: no Wi-Fi", ("bildsatz", "ohne-wlan"),
|
||||
done_text="Image set: no Wi-Fi")
|
||||
|
||||
|
||||
def action_cancel() -> Action:
|
||||
"""Die Haltegeste in Ruhe: Station aus. Bewusst ohne Rueckfrage - sie IST die
|
||||
Rueckfrage (3 s halten), und sie ist der Notausgang, der ohne Umweg wirken muss."""
|
||||
return Action("Station aus", ("off",), confirm=False, done_text="Station aus")
|
||||
return Action("Station off", ("off",), confirm=False, done_text="Station off")
|
||||
|
||||
|
||||
def menu_actions(state: dict) -> list[Action]:
|
||||
@@ -221,7 +221,7 @@ def menu_actions(state: dict) -> list[Action]:
|
||||
|
||||
if not running:
|
||||
return [
|
||||
Action("Rettung starten", ("recovery", "on"), done_text="Rettung laeuft"),
|
||||
Action("Start recovery", ("recovery", "on"), done_text="Recovery running"),
|
||||
# Reihenfolge ist Absicht: der horchende Modus zuerst, weil er der
|
||||
# naheliegende und der harmlose ist - wer nur blaettert und irgendwo
|
||||
# loslaesst, landet nicht im Automatikmodus.
|
||||
@@ -237,13 +237,13 @@ def menu_actions(state: dict) -> list[Action]:
|
||||
if mode == "migration" and target:
|
||||
# Nur wenn last_seen wirklich ein Ziel kennt - ohne erkannte Lampe gibt es
|
||||
# nichts freizugeben, und "arm" ohne Ziel wuerde das CLI ohnehin abweisen.
|
||||
items.append(Action("Freigabe: {}".format(target), ("arm", str(target)),
|
||||
done_text="Freigegeben: {}".format(target)))
|
||||
items.append(Action("Station aus", ("off",), done_text="Station aus"))
|
||||
items.append(Action("Arm: {}".format(target), ("arm", str(target)),
|
||||
done_text="Armed: {}".format(target)))
|
||||
items.append(Action("Station off", ("off",), done_text="Station off"))
|
||||
items.append(action_imageset(imageset))
|
||||
# Veraendert nichts am Betrieb - einzige Auswahl ohne Rueckfrage.
|
||||
items.append(Action("Anzeige auffrischen", ("display", "test"), confirm=False,
|
||||
done_text="Anzeige aufgefrischt"))
|
||||
items.append(Action("Refresh display", ("display", "test"), confirm=False,
|
||||
done_text="Display refreshed"))
|
||||
items.append(action_shutdown())
|
||||
return items
|
||||
|
||||
@@ -561,32 +561,32 @@ class ButtonUI:
|
||||
and self.screen in ("idle", "busy")):
|
||||
held = now - self._press_t
|
||||
if self._hold_stage == 1:
|
||||
return _view("hold", "STATION AUS",
|
||||
"Herunterfahren in {} s".format(
|
||||
return _view("hold", "STATION OFF",
|
||||
"Shutting down in {} s".format(
|
||||
_countdown(SHUTDOWN_HOLD_S, held)),
|
||||
["Weiter halten - Loslassen bricht ab"])
|
||||
["Keep holding - release to cancel"])
|
||||
if held >= SHORT_PRESS_MAX_S:
|
||||
return _view("hold", "HALTEN",
|
||||
"Abbruch in {} s".format(_countdown(CANCEL_HOLD_S, held)),
|
||||
["Weiter halten - Loslassen bricht ab"])
|
||||
return _view("hold", "HOLD",
|
||||
"Cancel in {} s".format(_countdown(CANCEL_HOLD_S, held)),
|
||||
["Keep holding - release to cancel"])
|
||||
if self.screen == "menu" and self.items:
|
||||
selected = self.items[self.index]
|
||||
rest = self.items[self.index + 1:] + self.items[:self.index]
|
||||
return _view("menu", "MENUE", "> " + selected.label,
|
||||
[a.label for a in rest] + ["kurz: weiter, halten: waehlen"])
|
||||
return _view("menu", "MENU", "> " + selected.label,
|
||||
[a.label for a in rest] + ["tap: next, hold: select"])
|
||||
if self.screen == "confirm" and self.pending is not None:
|
||||
# Die Bedienhinweise stehen IMMER und IMMER ZULETZT - eine Rueckfrage, bei
|
||||
# der man raten muss, wie man ablehnt, ist keine. Eine eigene Warnung des
|
||||
# Eintrags (confirm_details) kommt davor, damit sie zuerst gelesen wird.
|
||||
details = list(self.pending.confirm_details or [])
|
||||
details += ["halten (1,5 s): ja", "kurz: nein"]
|
||||
details += ["hold (1.5 s): yes", "tap: no"]
|
||||
return _view("confirm", self.pending.confirm_title, self.pending.label,
|
||||
details)
|
||||
if self.screen == "busy" and self.busy is not None:
|
||||
return _view("busy", "BITTE WARTEN", self.busy.label, [])
|
||||
return _view("busy", "PLEASE WAIT", self.busy.label, [])
|
||||
if self.result is not None:
|
||||
text, ok = self.result
|
||||
return _view("result", "FERTIG" if ok else "FEHLER", text, [])
|
||||
return _view("result", "DONE" if ok else "ERROR", text, [])
|
||||
return {"view": "idle", "active": False}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user