feat(station): Automatikmodus als eigener Menueeintrag am Taster
Das Tastermenue bot bisher nur "Migration starten" an, fest verdrahtet
auf die Horchphase; den Automatikmodus gab es nur ueber die
Kommandozeile - also ausgerechnet dort nicht, wo man ihn braucht, denn
"on" macht wlan0 zum Zugangspunkt und reisst jede SSH-Sitzung ab.
Jetzt zwei getrennte, namentlich unterscheidbare Eintraege:
"Migration: horchen" (--listen) zuerst, danach "Migration: AUTOMATIK"
(ohne --listen). Der Automatikeintrag bekommt eine andere, deutlichere
Rueckfrage ("ACHTUNG" statt "RUECKFRAGE", plus zwei Warnzeilen), weil
der Migrations-Zugangspunkt ein oeffentlich bekanntes Passwort hat und
im Automatikmodus jede alte Klyqa-Lampe in Funkreichweite ungefragt
migriert wird, auch fremde. Die Bedienhinweise stehen weiterhin und
zuletzt.
This commit is contained in:
@@ -32,12 +32,17 @@ ninlight-station), nie ueber nachgebaute Logik: nur das CLI kennt die Profilumsc
|
||||
die Nachpruefungen ("es darf nie mehr als ein Stationsprofil aktiv sein") und den
|
||||
Zeitschalter.
|
||||
|
||||
SICHERHEITSREGEL: "Migration starten" startet ueber das Menue IMMER in der Horchphase
|
||||
("migrate on --listen"), nie automatisch. Der Migrations-Zugangspunkt hat ein oeffentlich
|
||||
bekanntes Passwort (es steht in der alten Klyqa-Firmware); ein Tastendruck, der ihn im
|
||||
Automatikmodus hochfaehrt, wuerde jede alte Klyqa-Lampe in Funkreichweite ungefragt
|
||||
migrieren, auch fremde. Die bequeme Geste muss die sichere sein - wer den Automatikmodus
|
||||
will, nimmt die Kommandozeile, dort ist die Absicht eindeutig.
|
||||
SICHERHEITSREGEL: die beiden Migrationseintraege sind GETRENNT und namentlich
|
||||
unterscheidbar. "Migration: horchen" ruft "migrate on --listen" - der Zugangspunkt steht,
|
||||
aber es geht nichts raus, bis der Bediener eine erkannte Lampe freigibt; das ist der
|
||||
erste und naheliegende Eintrag. "Migration: AUTOMATIK" ruft "migrate on" ohne "--listen"
|
||||
und bekommt eine ANDERE, deutlichere Rueckfrage (siehe action_migrate_auto()): der
|
||||
Migrations-Zugangspunkt hat ein oeffentlich bekanntes Passwort (es steht in der alten
|
||||
Klyqa-Firmware), und im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite
|
||||
ungefragt migriert, auch fremde. Die bequeme Geste bleibt damit die sichere, ohne dass
|
||||
der Automatikmodus - fuer eine ganze Kiste eigener Lampen am Stueck - nur noch ueber die
|
||||
Kommandozeile erreichbar waere, die im Zugangspunktbetrieb gerade nicht zur Verfuegung
|
||||
steht.
|
||||
|
||||
Der Taster ist NIRGENDS Voraussetzung: fehlt gpiozero, fehlt der Taster oder ist GPIO23
|
||||
belegt, schreibt main() GENAU EINE Protokollzeile und beendet sich mit sys.exit(0) -
|
||||
@@ -115,15 +120,26 @@ class Action:
|
||||
"""Was ein Menueeintrag bzw. die Haltegeste ausloest.
|
||||
|
||||
`confirm` = braucht eine Rueckfrage. Zusage: JEDE veraendernde Auswahl bekommt eine
|
||||
("Anzeige auffrischen" veraendert nichts und braucht keine)."""
|
||||
("Anzeige auffrischen" veraendert nichts und braucht keine).
|
||||
|
||||
`confirm_title`/`confirm_details` machen die Rueckfrage bei Bedarf DEUTLICHER als die
|
||||
uebliche. Die Vorgabe ("RUECKFRAGE" + die zwei Bedienhinweise) reicht fuer eine
|
||||
Auswahl, deren Tragweite im Eintragsnamen steht ("Station aus", "Herunterfahren").
|
||||
Sie reicht NICHT fuer den Automatikmodus: was dort passiert - jede alte Klyqa-Lampe
|
||||
in Funkreichweite wird ungefragt migriert, auch fremde - steht in keinem
|
||||
Eintragsnamen, und ein blosses "Wirklich?" fragt nach etwas, das der Bediener in
|
||||
diesem Moment gar nicht vor Augen hat."""
|
||||
|
||||
def __init__(self, label: str, argv=(), kind: str = "cli", confirm: bool = True,
|
||||
done_text: str | None = None) -> None:
|
||||
done_text: str | None = None, confirm_title: str = "RUECKFRAGE",
|
||||
confirm_details=None) -> None:
|
||||
self.label = label
|
||||
self.argv = tuple(argv)
|
||||
self.kind = kind
|
||||
self.confirm = confirm
|
||||
self.done_text = done_text or label
|
||||
self.confirm_title = confirm_title
|
||||
self.confirm_details = list(confirm_details) if confirm_details else None
|
||||
|
||||
def __repr__(self) -> str: # nur fuer Testausgaben
|
||||
return "Action({!r}, {!r}, kind={!r})".format(self.label, self.argv, self.kind)
|
||||
@@ -134,6 +150,34 @@ def action_shutdown() -> Action:
|
||||
done_text="Faehrt herunter")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
def action_migrate_auto() -> Action:
|
||||
"""Der Automatikmodus - fuer die eigene Kiste Lampen am Stueck, ohne jede einzeln
|
||||
freizugeben. Er steht hier, weil das Verabreden ueber die Kommandozeile genau dann
|
||||
nicht geht, wenn man ihn braucht: "on" macht wlan0 zum Zugangspunkt und reisst jede
|
||||
SSH-Sitzung ueber WLAN ab.
|
||||
|
||||
Seine Rueckfrage ist ABSICHTLICH eine andere als die der uebrigen Eintraege. Der
|
||||
Migrations-Zugangspunkt hat ein oeffentlich bekanntes Passwort (es steht in der alten
|
||||
Klyqa-Firmware), und im Automatikmodus wird jede alte Klyqa-Lampe in Funkreichweite
|
||||
ungefragt migriert - auch die des Nachbarn, der davon nichts weiss und dessen Lampe
|
||||
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"])
|
||||
|
||||
|
||||
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."""
|
||||
@@ -150,10 +194,11 @@ def menu_actions(state: dict) -> list[Action]:
|
||||
if not running:
|
||||
return [
|
||||
Action("Rettung starten", ("recovery", "on"), done_text="Rettung laeuft"),
|
||||
# IMMER --listen (siehe Modul-Docstring, Sicherheitsregel). Wer den
|
||||
# Automatikmodus will, nimmt die Kommandozeile.
|
||||
Action("Migration starten", ("migrate", "on", "--listen"),
|
||||
done_text="Migration: horcht"),
|
||||
# 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.
|
||||
action_migrate_listen(),
|
||||
action_migrate_auto(),
|
||||
action_shutdown(),
|
||||
]
|
||||
|
||||
@@ -486,8 +531,13 @@ class ButtonUI:
|
||||
return _view("menu", "MENUE", "> " + selected.label,
|
||||
[a.label for a in rest] + ["kurz: weiter, halten: waehlen"])
|
||||
if self.screen == "confirm" and self.pending is not None:
|
||||
return _view("confirm", "RUECKFRAGE", self.pending.label,
|
||||
["halten (1,5 s): ja", "kurz: nein"])
|
||||
# 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"]
|
||||
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, [])
|
||||
if self.result is not None:
|
||||
|
||||
Reference in New Issue
Block a user