feat(station): Bedienung ueber einen Taster mit Panel-Rueckmeldung
Ein Taster auf GPIO23 (active low, 50 ms entprellt) bedient die Station am Geraet: kurz oeffnet das Menue bzw. blaettert, gehalten waehlt (1,5 s), bricht ab (3 s, feuert sofort) und faehrt herunter (10 s, nur ueber die sichtbare Abbruchstufe hinweg). Menue schliesst nach 20 s, Rueckfrage verfaellt nach 10 s als Abbruch; jede veraendernde Auswahl bekommt eine Rueckfrage. Noetig, weil "on"/"migrate on" wlan0 zum Zugangspunkt macht und damit jede SSH-Sitzung abreisst - bisher blieb dort nur warten oder Stecker ziehen. "Migration starten" startet ueber das Menue IMMER mit --listen: der Migrations-AP hat ein oeffentlich bekanntes Passwort, ein Tastendruck im Automatikmodus wuerde jede alte Klyqa-Lampe in Reichweite ungefragt migrieren. Der Tasterdienst fasst den SPI-Bus nicht an (sonst "GPIO busy"), sondern schreibt seinen Bedienzustand atomar mit 0644 nach /run/ninlight-button/ui.json - bewusst nicht ins RuntimeDirectory der Hauptunit, das systemd beim "off" aufraeumt. Der Anzeigedienst liest die Datei jede halbe Sekunde (der teure build_state()-Fork bleibt beim 5-Sekunden-Takt) und zeichnet sie als Teilaufbau, kostet dem Panel also keinen Vollaufbau je Tastendruck. Fehlt gpiozero oder der Taster, gibt es genau eine Protokollzeile und sys.exit(0) - dieselbe Zusage wie bei der Anzeige. Ausliefern/Einrichten folgt in einem eigenen Durchgang.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
[Unit]
|
||||
Description=ninlight Taster (Bedienung der Station ueber GPIO23)
|
||||
# Wie beim Anzeigedienst nur After=, NICHT Requires=/BindsTo=/Wants=: ein fehlender oder
|
||||
# abgestuerzter Taster darf server.py niemals mitreissen, und server.py muss starten
|
||||
# koennen, auch wenn dieser Dienst fehlt oder deaktiviert ist. KEIN
|
||||
# Wants=ninlight-station.service - das wuerde die absichtlich deaktivierte Station bei
|
||||
# jedem Boot mit hochziehen (Fund der Hardware-Abnahme, ausfuehrlich in
|
||||
# ninlight-display.service begruendet).
|
||||
After=ninlight-station.service
|
||||
# Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort werden sie mit
|
||||
# "Unknown key name" ignoriert, und die Deckelung unten greift dann nie).
|
||||
StartLimitIntervalSec=60
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=NINLIGHT_RUN_DIR=/run/ninlight-station
|
||||
Environment=NINLIGHT_UI_FILE=/run/ninlight-button/ui.json
|
||||
# "-"-Praefix VOR dem Dateipfad (nicht vor dem Schluesselnamen - so herum ist es ein
|
||||
# ungueltiger Schluessel und systemd verwirft die Zeile ganz): ohne die
|
||||
# Konfigurationsdatei startet der Taster trotzdem.
|
||||
EnvironmentFile=-/etc/ninlight-station.conf
|
||||
# Eigenes RuntimeDirectory, aus zwei Gruenden:
|
||||
#
|
||||
# 1. Hier liegt ui.json. Es darf NICHT in /run/ninlight-station liegen (dem
|
||||
# RuntimeDirectory der Hauptunit): systemd raeumt das beim regulaeren Stop von
|
||||
# ninlight-station auf - also ausgerechnet im Zustand "Station aus", in dem das Menue
|
||||
# ("Rettung starten"/"Migration starten") am dringendsten gebraucht wird. So
|
||||
# verschwindet ui.json genau dann, wenn dieser Dienst endet, und ein
|
||||
# stehengebliebenes Menue kann ihn nicht ueberdauern.
|
||||
# 2. lgpio (gpiozero's bevorzugte Pin-Factory) legt beim Start "$HOME/.lgd-nfy0" an.
|
||||
# Unter systemd ist HOME per Vorgabe LEER und das Arbeitsverzeichnis "/" - der Pfad
|
||||
# wird "//.lgd-nfy0" und scheitert. Am Geraet fuer den Anzeigedienst nachgemessen,
|
||||
# siehe dort; derselbe Stolperstein trifft jeden gpiozero-Dienst.
|
||||
RuntimeDirectory=ninlight-button
|
||||
RuntimeDirectoryMode=0755
|
||||
WorkingDirectory=/run/ninlight-button
|
||||
Environment=HOME=/run/ninlight-button
|
||||
ExecStart=/usr/bin/python3 /opt/ninlight-station/button/button.py
|
||||
|
||||
# button.py faengt einen fehlenden Taster (keine Bibliothek, kein Pin, Pin belegt) als
|
||||
# ButtonUnavailable ab, protokolliert eine Zeile und beendet sich mit sys.exit(0) - das
|
||||
# ist nie ein "Fehlschlag" im systemd-Sinne und loest kein Restart= aus. on-failure
|
||||
# (nicht always) deckelt damit genau das Richtige: echte Abstuerze werden neu versucht,
|
||||
# ein dauerhaft fehlender Taster nicht.
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# BEWUSST KEIN NoNewPrivileges=/ProtectSystem=strict/ProtectHome= hier - anders als beim
|
||||
# Anzeigedienst, und das ist kein Versehen:
|
||||
#
|
||||
# Dieser Dienst ruft /usr/local/bin/ninlight-station auf, und dessen einziger Zweck ist
|
||||
# es, NetworkManager-Profile umzuschalten, Units zu starten/stoppen und nach
|
||||
# /run/ninlight-station zu schreiben - alles ueber "sudo". Kindprozesse erben den
|
||||
# Namensraum und das no_new_privs-Flag dieser Unit: mit ProtectSystem=strict waere
|
||||
# /run/ninlight-station fuer das CLI schreibgeschuetzt (mode/control.json liessen sich
|
||||
# nicht mehr schreiben), und no_new_privs laesst kein setuid-Binary mehr zu (sudo). Eine
|
||||
# Absicherung, die genau die Aktionen unmoeglich macht, wegen derer es den Dienst gibt,
|
||||
# ist keine. Die Angriffsflaeche ist stattdessen eng gehalten: EIN Eingangspin, kein
|
||||
# Netzwerk, keine Eingabe ausser der Taste selbst, und ausgefuehrt wird ausschliesslich
|
||||
# die feste Liste von CLI-Unterbefehlen aus button.py.
|
||||
MemoryMax=64M
|
||||
|
||||
# Laeuft als root (wie Haupt- und Anzeigedienst) fuer GPIO23 und fuer das CLI - kein
|
||||
# SupplementaryGroups=gpio: auf reinem Debian gibt es die Gruppe oft gar nicht, und eine
|
||||
# fehlende Gruppe ist fuer systemd ein harter Startfehler (216/GROUP), ausgerechnet in
|
||||
# einer Unit, die nie stoeren darf.
|
||||
|
||||
# KEIN Zugriff auf SPI und keine Zeichenleitung: der Tasterdienst fasst den Panel-Bus
|
||||
# nicht an (zwei Prozesse darauf ergeben "lgpio.error: 'GPIO busy'"). Er schreibt seinen
|
||||
# Bedienzustand nach /run/ninlight-button/ui.json, der Anzeigedienst liest ihn und
|
||||
# zeichnet.
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user