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:
+375
-16
@@ -22,6 +22,56 @@ CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf}
|
||||
RUN_DIR=${NINLIGHT_RUN_DIR:-/run/ninlight-station}
|
||||
STATION_LIB=${NINLIGHT_STATION_LIB:-/opt/ninlight-station}
|
||||
|
||||
# --- Zeitschalter ("Auto-Aus") -----------------------------------------------------
|
||||
#
|
||||
# Rein OPTIONAL: nur "--for <minuten>" legt einen an. Ohne die Angabe passiert hier
|
||||
# nichts, und die Station verhaelt sich exakt wie vorher - der Regelfall ist der Pi am
|
||||
# Kabel, dem niemand den Zugangspunkt unter der laufenden Arbeit wegschalten soll.
|
||||
#
|
||||
# Wozu er dann gut ist: "on" macht wlan0 zum Zugangspunkt und reisst damit JEDE
|
||||
# SSH-Sitzung ueber WLAN ab. Wer den Pi nicht am Kabel hat, sperrt sich damit aus und
|
||||
# kommt nur noch ueber Tastatur und Monitor zurueck. Der Zeitschalter schaltet die
|
||||
# Station nach der angeforderten Zeit selbsttaetig ab (= zurueck ins Heim-WLAN).
|
||||
#
|
||||
# Umgesetzt mit einem transienten systemd-Timer unter FESTEM Unit-Namen (systemd-run
|
||||
# --unit=...): so sind "renew"/"hold"/"off" schlichte systemctl-Aufrufe auf einen
|
||||
# bekannten Namen, und es braucht keine zusaetzliche Unit-Datei im Repo.
|
||||
AUTOOFF_UNIT=${NINLIGHT_AUTOOFF_UNIT:-ninlight-station-autooff}
|
||||
# Eigene Zustandsdatei neben mode/control.json, im selben RUN_DIR und mit denselben
|
||||
# Rechten (0644): sie ist die Quelle fuer "status", fuer "renew" ohne Argument (welche
|
||||
# Dauer war zuletzt angefordert?) und fuer die Panelzeile - display.py liest sie. Sie ist
|
||||
# zugleich die Antwort auf "laeuft ueberhaupt einer?": jeder Weg, der den Timer anlegt
|
||||
# oder abraeumt, schreibt bzw. loescht sie in derselben Funktion.
|
||||
AUTOOFF_FILE="$RUN_DIR/autooff.json"
|
||||
# Abstand zwischen zwei Anlaeufen, wenn gerade ausgeliefert wird ("in etwa einer Minute
|
||||
# erneut versuchen"). Der Timer bringt ihn selbst mit (--on-unit-inactive), es wird also
|
||||
# nichts nachgelegt - siehe autooff_arm().
|
||||
AUTOOFF_RETRY_S=${NINLIGHT_AUTOOFF_RETRY_S:-60}
|
||||
# Obergrenze fuers Verschieben. 10 Anlaeufe x 1 min = rund 10 Minuten Aufschub.
|
||||
#
|
||||
# Begruendung der Zahl: eine Auslieferung ist ein einzelner HTTP-GET ueber ein halbes
|
||||
# Megabyte im eigenen WLAN - Sekunden, keine Minuten. Eine Verbindung, die nach zehn
|
||||
# Minuten noch offen steht, liefert nicht mehr aus, sie haengt (Lampe abgestuerzt,
|
||||
# Funkloch, halboffener Socket, den der Kernel noch als ESTABLISHED fuehrt). Ohne
|
||||
# Obergrenze wuerde genau so eine haengende Verbindung den Zeitschalter dauerhaft
|
||||
# aushebeln - und damit ausgerechnet den Fall herbeifuehren, gegen den er angefordert
|
||||
# wurde: der Nutzer sitzt ohne Kabel da und kommt nicht mehr an den Pi. Zehn Minuten
|
||||
# sind lang genug, dass keine echte Auslieferung abgeschnitten wird, und kurz genug,
|
||||
# dass der Zeitschalter seine Zusage noch einloest.
|
||||
AUTOOFF_MAX_DEFERRALS=${NINLIGHT_AUTOOFF_MAX_DEFERRALS:-10}
|
||||
# Das Netz des Zugangspunkts (server.py haengt auf 192.168.8.1:80) - daran wird erkannt,
|
||||
# ob gerade eine Lampe laedt, ohne dafuer etwas am Server zu aendern.
|
||||
AUTOOFF_STATION_NET=192.168.8.0/24
|
||||
# Sinnvolle Grenzen fuer --for/renew.
|
||||
AUTOOFF_MIN_MINUTES=1
|
||||
AUTOOFF_MAX_MINUTES=240
|
||||
|
||||
# Absoluter Pfad auf dieses Skript - der Timer ruft es spaeter ohne unser $PWD auf.
|
||||
case "$0" in
|
||||
/*) SELF=$0 ;;
|
||||
*) SELF=$(cd "$(dirname "$0")" && pwd)/$(basename "$0") ;;
|
||||
esac
|
||||
|
||||
if [ -r "$CONF" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$CONF"
|
||||
@@ -132,6 +182,180 @@ write_control() {
|
||||
sudo mv -f "$_tmp" "$RUN_DIR/control.json"
|
||||
}
|
||||
|
||||
# --- Zeitschalter: Zustandsdatei, Anlegen, Abraeumen, Pruefung -----------------------
|
||||
|
||||
# Prueft eine Minutenangabe, BEVOR irgendetwas eingeschaltet wird (Zusage: eine
|
||||
# ungueltige Angabe schaltet gar nichts an). Gibt 0 zurueck, wenn sie taugt.
|
||||
autooff_check_minutes() {
|
||||
case "${1:-}" in
|
||||
'' )
|
||||
echo "Fehler: '--for' braucht eine Angabe in Minuten ($AUTOOFF_MIN_MINUTES-$AUTOOFF_MAX_MINUTES)" >&2
|
||||
return 1
|
||||
;;
|
||||
*[!0-9]* )
|
||||
echo "Fehler: '$1' ist keine Minutenangabe - erlaubt sind ganze Zahlen von $AUTOOFF_MIN_MINUTES bis $AUTOOFF_MAX_MINUTES" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
if [ "$1" -lt "$AUTOOFF_MIN_MINUTES" ] || [ "$1" -gt "$AUTOOFF_MAX_MINUTES" ]; then
|
||||
echo "Fehler: $1 Minuten liegen ausserhalb von $AUTOOFF_MIN_MINUTES-$AUTOOFF_MAX_MINUTES" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Schreibt $AUTOOFF_FILE atomar mit 0644 - dasselbe Muster und dieselben Rechte wie
|
||||
# write_control(), damit display.py (laeuft als root) und das CLI (laeuft als normaler
|
||||
# Benutzer) sie beide lesen koennen.
|
||||
#
|
||||
# $1 = angeforderte Dauer in Minuten (bleibt ueber Verschiebungen hinweg unveraendert -
|
||||
# "renew" ohne Argument stellt genau darauf zurueck), $2 = Zeitpunkt des naechsten
|
||||
# Anlaufs als Unix-Zeit, $3 = Zahl der bisherigen Verschiebungen.
|
||||
autooff_write() {
|
||||
sudo mkdir -p "$RUN_DIR"
|
||||
_tmp="$RUN_DIR/.autooff-$$.json"
|
||||
_json=$(printf '{"minutes": %s, "deadline": %s, "deferrals": %s}' "$1" "$2" "$3")
|
||||
printf '%s' "$_json" | sudo tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1
|
||||
sudo mv -f "$_tmp" "$RUN_DIR/autooff.json"
|
||||
}
|
||||
|
||||
# Gibt "<minuten> <deadline> <verschiebungen>" aus, oder scheitert (Rueckgabewert 1),
|
||||
# wenn kein Zeitschalter vorgemerkt ist bzw. die Datei unbrauchbar ist.
|
||||
autooff_read() {
|
||||
[ -e "$AUTOOFF_FILE" ] || return 1
|
||||
python3 - "$AUTOOFF_FILE" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
with open(sys.argv[1]) as f:
|
||||
d = json.load(f)
|
||||
except (OSError, ValueError):
|
||||
sys.exit(1)
|
||||
try:
|
||||
print(int(d["minutes"]), int(d["deadline"]), int(d.get("deferrals", 0)))
|
||||
except (KeyError, TypeError, ValueError):
|
||||
sys.exit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
# Haelt NUR den Timer an, nie die gleichnamige .service-Einheit: wenn diese Funktion aus
|
||||
# dem gefeuerten Timer heraus laeuft (autooff_fire -> full_off), ist genau diese Einheit
|
||||
# der eigene Prozess - ein "systemctl stop $AUTOOFF_UNIT" ohne Endung loeste sich zur
|
||||
# .service auf und wuerde sich mitten im Abschalten selbst umbringen.
|
||||
autooff_stop_unit() {
|
||||
sudo systemctl stop "$AUTOOFF_UNIT.timer" >/dev/null 2>&1 || true
|
||||
# Damit ein spaeteres systemd-run denselben Namen wieder vergeben kann, auch wenn ein
|
||||
# frueherer Lauf im Zustand "failed" haengengeblieben ist.
|
||||
sudo systemctl reset-failed "$AUTOOFF_UNIT.timer" "$AUTOOFF_UNIT.service" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
# Timer anhalten UND die Vormerkung loeschen - "es laeuft keiner mehr".
|
||||
autooff_clear() {
|
||||
autooff_stop_unit
|
||||
sudo rm -f "$AUTOOFF_FILE"
|
||||
}
|
||||
|
||||
# Legt den Zeitschalter an (oder erneuert ihn).
|
||||
# $1 = angeforderte Dauer in Minuten, $2 = Verzoegerung des naechsten Anlaufs in
|
||||
# Sekunden, $3 = Zahl der bisherigen Verschiebungen.
|
||||
#
|
||||
# --on-unit-inactive sorgt dafuer, dass der Timer nach JEDEM Lauf der Aktion erneut
|
||||
# feuert. Das ist die Verschiebung "in etwa einer Minute" (siehe autooff_fire): der
|
||||
# gefeuerte Lauf kann sie nicht selbst nachlegen, weil er unter demselben Unit-Namen
|
||||
# laeuft und systemd-run einen belegten Namen ablehnt. Wird nicht verschoben, endet der
|
||||
# Lauf in full_off -> autooff_clear, und der Timer ist weg, bevor er ein zweites Mal
|
||||
# feuern koennte.
|
||||
autooff_arm() {
|
||||
_req=$1
|
||||
_delay=$2
|
||||
_deferrals=$3
|
||||
autooff_stop_unit
|
||||
set -- --unit="$AUTOOFF_UNIT" \
|
||||
--description="ninlight-station: Zeitschalter (Auto-Aus)" \
|
||||
--on-active="${_delay}s" \
|
||||
--on-unit-inactive="${AUTOOFF_RETRY_S}s"
|
||||
# Die Testumgebung (tests/test_cli.sh) laeuft gegen eine Sandbox statt gegen
|
||||
# /etc und /run; der spaeter gefeuerte Lauf haette davon sonst nichts. Im Betrieb
|
||||
# sind diese Variablen nicht gesetzt und es wird nichts durchgereicht.
|
||||
if [ -n "${NINLIGHT_STATION_CONF:-}" ]; then
|
||||
set -- "$@" "--setenv=NINLIGHT_STATION_CONF=$NINLIGHT_STATION_CONF"
|
||||
fi
|
||||
if [ -n "${NINLIGHT_RUN_DIR:-}" ]; then
|
||||
set -- "$@" "--setenv=NINLIGHT_RUN_DIR=$NINLIGHT_RUN_DIR"
|
||||
fi
|
||||
if [ -n "${NINLIGHT_STATION_LIB:-}" ]; then
|
||||
set -- "$@" "--setenv=NINLIGHT_STATION_LIB=$NINLIGHT_STATION_LIB"
|
||||
fi
|
||||
if ! sudo systemd-run "$@" "$SELF" __autooff >/dev/null; then
|
||||
echo "Fehler: Zeitschalter liess sich nicht anlegen (systemd-run '$AUTOOFF_UNIT') - die Station bleibt AN, 'ninlight-station off' nicht vergessen." >&2
|
||||
return 1
|
||||
fi
|
||||
autooff_write "$_req" "$(( $(date +%s) + _delay ))" "$_deferrals" || {
|
||||
echo "Fehler: $AUTOOFF_FILE konnte nicht geschrieben werden - Zeitschalter wieder abgeraeumt" >&2
|
||||
autooff_clear
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Laeuft gerade eine Auslieferung? Geprueft an den offenen TCP-Verbindungen auf Port 80
|
||||
# aus dem Stationsnetz - ohne jede Aenderung an server.py.
|
||||
#
|
||||
# "ss" liegt auf Debian in /usr/sbin; der gefeuerte Timer-Lauf bringt nicht zwingend
|
||||
# denselben $PATH mit wie eine Anmeldesitzung, deshalb die beiden festen Pfade als
|
||||
# Rueckfall.
|
||||
autooff_busy() {
|
||||
_ss=$(command -v ss 2>/dev/null || true)
|
||||
if [ -z "$_ss" ]; then
|
||||
for _cand in /usr/sbin/ss /sbin/ss; do
|
||||
if [ -x "$_cand" ]; then
|
||||
_ss=$_cand
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ -z "$_ss" ]; then
|
||||
# Nicht pruefbar ist nicht dasselbe wie "es laeuft etwas": waere das hier ein
|
||||
# "ja, beschaeftigt", haette ein fehlendes ss den Zeitschalter bis zur
|
||||
# Obergrenze ausgehebelt - und der Zeitschalter ist angefordert worden, WEIL der
|
||||
# Nutzer sonst nicht mehr an den Pi kommt.
|
||||
echo "Warnung: 'ss' nicht gefunden - laufende Auslieferungen konnten nicht geprueft werden" >&2
|
||||
return 1
|
||||
fi
|
||||
_conns=$("$_ss" -Htn state established "( sport = :80 )" dst "$AUTOOFF_STATION_NET" 2>/dev/null | grep -c . || true)
|
||||
[ "${_conns:-0}" -gt 0 ]
|
||||
}
|
||||
|
||||
# Die Aktion des Timers. Absichtlich ein eigener (im Hilfetext nicht gefuehrter)
|
||||
# Unterbefehl statt schlicht "off": das Verschieben bei laufender Auslieferung gehoert
|
||||
# NUR hierher. Wer von Hand "off" tippt, will sofort aus - und nicht, dass ihm das CLI
|
||||
# das Abschalten zehn Minuten lang verweigert. Ausgeschaltet wird am Ende trotzdem
|
||||
# genau das, was "off" tut (full_off), nicht ein zweiter, aehnlicher Weg.
|
||||
autooff_fire() {
|
||||
if ! _info=$(autooff_read); then
|
||||
# Niemand hat mehr einen Zeitschalter vorgemerkt (z.B. "hold" oder ein
|
||||
# zwischenzeitliches "off") - dann hier nichts abschalten, nur den eigenen Timer
|
||||
# abraeumen. Sonst faellt dieser Lauf in eine Station hinein, die laengst aus
|
||||
# oder aus einem anderen Grund wieder an ist.
|
||||
autooff_stop_unit
|
||||
exit 0
|
||||
fi
|
||||
# Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt.
|
||||
# shellcheck disable=SC2086
|
||||
set -- $_info
|
||||
_req=$1
|
||||
_deferrals=$3
|
||||
if autooff_busy; then
|
||||
if [ "$_deferrals" -lt "$AUTOOFF_MAX_DEFERRALS" ]; then
|
||||
_deferrals=$((_deferrals + 1))
|
||||
autooff_write "$_req" "$(( $(date +%s) + AUTOOFF_RETRY_S ))" "$_deferrals"
|
||||
echo "Zeitschalter verschoben ($_deferrals/$AUTOOFF_MAX_DEFERRALS): eine Auslieferung laeuft noch"
|
||||
exit 0
|
||||
fi
|
||||
echo "Warnung: $AUTOOFF_MAX_DEFERRALS Verschiebungen erreicht - es steht immer noch eine Verbindung offen, sie haengt vermutlich. Station wird jetzt trotzdem abgeschaltet." >&2
|
||||
fi
|
||||
full_off
|
||||
}
|
||||
|
||||
# Startet (neu) und wartet kurz auf Lebenszeichen, dann prueft es nach, dass der
|
||||
# eben gestartete Prozess wirklich den beabsichtigten Modus/die beabsichtigte
|
||||
# Phase sieht.
|
||||
@@ -213,7 +437,43 @@ PY
|
||||
fi
|
||||
}
|
||||
|
||||
# Liest "--for <minuten>" aus den Argumenten und legt die Dauer in $_for_minutes ab
|
||||
# (leer = kein Zeitschalter, die Vorgabe). Wird VOR jedem Einschalten aufgerufen, damit
|
||||
# eine ungueltige Angabe nichts anschaltet.
|
||||
# $1 = das Kommando fuer die Fehlermeldung, Rest = die Argumente.
|
||||
parse_for_option() {
|
||||
_for_minutes=""
|
||||
_for_seen=0
|
||||
shift
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--for)
|
||||
shift
|
||||
_for_minutes=${1:-}
|
||||
_for_seen=1
|
||||
autooff_check_minutes "$_for_minutes" || exit 2
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
[ $# -gt 0 ] && shift || true
|
||||
done
|
||||
[ "$_for_seen" -eq 1 ] || _for_minutes=""
|
||||
}
|
||||
|
||||
# Legt den angeforderten Zeitschalter an - NUR wenn "--for" dabei war. Ohne die Angabe
|
||||
# passiert hier nichts: das ist die Vorgabe und der Grund, warum der Pi am Kabel von
|
||||
# dieser Funktion nie etwas merkt.
|
||||
autooff_arm_if_requested() {
|
||||
[ -n "$_for_minutes" ] || return 0
|
||||
autooff_arm "$_for_minutes" "$((_for_minutes * 60))" 0 || exit 1
|
||||
echo "Zeitschalter: Auto-Aus in $_for_minutes min ('ninlight-station renew' verlaengert, 'hold' bestellt ab)"
|
||||
}
|
||||
|
||||
recovery_on() {
|
||||
parse_for_option recovery "$@"
|
||||
need_profile "$AP_CONNECTION"
|
||||
n=$(images | wc -l | tr -d ' ')
|
||||
[ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2
|
||||
@@ -229,17 +489,34 @@ recovery_on() {
|
||||
echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2
|
||||
fi
|
||||
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
|
||||
autooff_arm_if_requested
|
||||
}
|
||||
|
||||
migrate_on() {
|
||||
# $1 = "--listen" oder leer.
|
||||
# Argumente: "--listen" und/oder "--for <minuten>", beide optional und in beliebiger
|
||||
# Reihenfolge. Beides wird VOLLSTAENDIG geprueft, bevor irgendein Profil hochkommt.
|
||||
_phase=auto
|
||||
if [ "${1:-}" = "--listen" ]; then
|
||||
_phase=listen
|
||||
elif [ -n "${1:-}" ]; then
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
_for_minutes=""
|
||||
_for_seen=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--listen)
|
||||
_phase=listen
|
||||
;;
|
||||
--for)
|
||||
shift
|
||||
_for_minutes=${1:-}
|
||||
_for_seen=1
|
||||
autooff_check_minutes "$_for_minutes" || exit 2
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
[ $# -gt 0 ] && shift || true
|
||||
done
|
||||
[ "$_for_seen" -eq 1 ] || _for_minutes=""
|
||||
need_profile "$MIGRATE_AP_CONNECTION"
|
||||
n=$(migrate_images | wc -l | tr -d ' ')
|
||||
[ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR/migrate enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2
|
||||
@@ -255,6 +532,7 @@ migrate_on() {
|
||||
echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)"
|
||||
echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2
|
||||
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
|
||||
autooff_arm_if_requested
|
||||
}
|
||||
|
||||
# Ruft display.py mit einem Schalter auf. EPD_REVISION steht in $CONF und wird von
|
||||
@@ -348,6 +626,12 @@ display_off() {
|
||||
}
|
||||
|
||||
full_off() {
|
||||
# ZUERST: ein noch vorgemerkter Zeitschalter muss mit weg, sonst feuert er spaeter in
|
||||
# eine Station hinein, die laengst aus ist - oder, schlimmer, in eine, die inzwischen
|
||||
# jemand anders wieder eingeschaltet hat. Ganz vorne, damit auch ein spaeter
|
||||
# scheiterndes "off" (haengender NetworkManager, siehe ap_switch) ihn nicht
|
||||
# stehenlaesst.
|
||||
autooff_clear
|
||||
sudo systemctl stop ninlight-station 2>/dev/null || true
|
||||
# VOR ap_switch, nicht danach: ap_switch "" bricht bei einem haengenden
|
||||
# NetworkManager mit Fehler ab, und unter "set -e" kaeme danach nichts mehr. Genau
|
||||
@@ -420,6 +704,55 @@ PY
|
||||
echo "Freigegeben: $_target"
|
||||
}
|
||||
|
||||
# "renew [<minuten>]" - die Uhr zurueckstellen. Ohne Argument auf dieselbe Dauer, die
|
||||
# zuletzt angefordert wurde (nicht auf die Restzeit und auch nicht auf eine Vorgabe).
|
||||
#
|
||||
# Laeuft keiner, ist das ein Fehler und AUSDRUECKLICH kein stillschweigendes Neuanlegen:
|
||||
# "renew" auf einem Pi am Kabel wuerde sonst genau den Zeitschalter herbeifuehren, den
|
||||
# der Nutzer nie wollte.
|
||||
renew_cmd() {
|
||||
_arg=${1:-}
|
||||
if ! _info=$(autooff_read); then
|
||||
echo "Fehler: kein Zeitschalter aktiv - 'renew' stellt nur einen laufenden zurueck. Einen neuen gibt es nur mit 'ninlight-station on --for <minuten>'." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt.
|
||||
# shellcheck disable=SC2086
|
||||
set -- $_info
|
||||
_minutes=$1
|
||||
if [ -n "$_arg" ]; then
|
||||
autooff_check_minutes "$_arg" || exit 2
|
||||
_minutes=$_arg
|
||||
fi
|
||||
autooff_arm "$_minutes" "$((_minutes * 60))" 0 || exit 1
|
||||
echo "Zeitschalter zurueckgestellt: Auto-Aus in $_minutes min"
|
||||
}
|
||||
|
||||
# "hold" - den Zeitschalter abbestellen. Die Station bleibt an, bis jemand "off" sagt.
|
||||
hold_cmd() {
|
||||
if ! autooff_read >/dev/null; then
|
||||
echo "Fehler: kein Zeitschalter aktiv - es gibt nichts abzubestellen." >&2
|
||||
exit 1
|
||||
fi
|
||||
autooff_clear
|
||||
echo "Zeitschalter abbestellt - die Station bleibt an, bis 'ninlight-station off'."
|
||||
}
|
||||
|
||||
# Restzeit fuer "status", oder leer, wenn kein Zeitschalter laeuft (dann steht in der
|
||||
# Ausgabe gar nichts davon - eine Zeile "kein Zeitschalter aktiv" kostet nur Platz).
|
||||
autooff_status_line() {
|
||||
_info=$(autooff_read) || return 0
|
||||
# Absichtliches Wortsplitting: autooff_read() liefert drei Zahlen, durch Leerzeichen getrennt.
|
||||
# shellcheck disable=SC2086
|
||||
set -- $_info
|
||||
_rest=$(( ($2 - $(date +%s) + 59) / 60 ))
|
||||
if [ "$_rest" -gt 0 ]; then
|
||||
printf 'Auto-Aus: in %s min (angefordert: %s min)\n' "$_rest" "$1"
|
||||
else
|
||||
printf 'Auto-Aus: steht an (angefordert: %s min)\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
status_cmd() {
|
||||
# "systemctl is-active" schreibt den Zustand ("inactive", "failed", ...) auf stdout und
|
||||
# liefert dabei einen Rueckgabewert ungleich 0, sobald der Dienst nicht "active" ist -
|
||||
@@ -513,6 +846,7 @@ PY
|
||||
printf 'Phase : %s\n' "$_phase"
|
||||
printf 'Erkannt: %s\n' "$_erkannt"
|
||||
printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)"
|
||||
autooff_status_line
|
||||
printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)"
|
||||
printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')"
|
||||
echo "Images :"
|
||||
@@ -547,8 +881,16 @@ display_cmd() {
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm [<target>]|listen|display {test|clear|off}|off|status" >&2
|
||||
echo "Aufruf: ninlight-station on [--for <minuten>]|recovery {on [--for <minuten>]|off}|migrate {on [--listen] [--for <minuten>]|off}|arm [<target>]|listen|renew [<minuten>]|hold|display {test|clear|off}|off|status" >&2
|
||||
echo "" >&2
|
||||
echo " --for <minuten> Zeitschalter: die Station schaltet sich nach <minuten> selbst" >&2
|
||||
echo " wieder aus (zurueck ins Heim-WLAN). OPTIONAL - ohne '--for'" >&2
|
||||
echo " bleibt sie an, bis jemand 'off' sagt. Nuetzlich, wenn du ueber" >&2
|
||||
echo " WLAN angemeldet bist: 'on' macht wlan0 zum Zugangspunkt und" >&2
|
||||
echo " reisst die SSH-Sitzung ab. Erlaubt: 1-240." >&2
|
||||
echo " renew [<min>] Uhr zurueckstellen (ohne Angabe auf die zuletzt angeforderte" >&2
|
||||
echo " Dauer). Laeuft keiner, ist das ein Fehler." >&2
|
||||
echo " hold Zeitschalter abbestellen, Station bleibt an." >&2
|
||||
echo " display test Testbild aufs Panel zeichnen. Das ist der erste Handgriff bei der" >&2
|
||||
echo " Hardware-Abnahme und die EINZIGE Moeglichkeit, die Panel-Revision" >&2
|
||||
echo " zu bestimmen: die steht am Geraet nirgends. Bleibt das Bild leer," >&2
|
||||
@@ -562,32 +904,49 @@ usage() {
|
||||
echo " display off Anzeigedienst stoppen und das Panel leeren (macht 'off' mit)." >&2
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
_cmd=${1:-}
|
||||
[ $# -gt 0 ] && shift || true
|
||||
case "$_cmd" in
|
||||
on)
|
||||
recovery_on
|
||||
recovery_on "$@"
|
||||
;;
|
||||
recovery)
|
||||
case "${2:-}" in
|
||||
on) recovery_on ;;
|
||||
_sub=${1:-}
|
||||
[ $# -gt 0 ] && shift || true
|
||||
case "$_sub" in
|
||||
on) recovery_on "$@" ;;
|
||||
off) full_off ;;
|
||||
*) usage; exit 2 ;;
|
||||
esac
|
||||
;;
|
||||
migrate)
|
||||
case "${2:-}" in
|
||||
on) migrate_on "${3:-}" ;;
|
||||
_sub=${1:-}
|
||||
[ $# -gt 0 ] && shift || true
|
||||
case "$_sub" in
|
||||
on) migrate_on "$@" ;;
|
||||
off) full_off ;;
|
||||
*) usage; exit 2 ;;
|
||||
esac
|
||||
;;
|
||||
arm)
|
||||
arm_cmd "${2:-}"
|
||||
arm_cmd "${1:-}"
|
||||
;;
|
||||
listen)
|
||||
listen_cmd
|
||||
;;
|
||||
renew)
|
||||
renew_cmd "${1:-}"
|
||||
;;
|
||||
hold)
|
||||
hold_cmd
|
||||
;;
|
||||
__autooff)
|
||||
# Nicht im Hilfetext: das ist die Aktion des Zeitschalters, kein Handgriff fuer
|
||||
# Menschen (siehe autooff_fire).
|
||||
autooff_fire
|
||||
;;
|
||||
display)
|
||||
display_cmd "${2:-}"
|
||||
display_cmd "${1:-}"
|
||||
;;
|
||||
off)
|
||||
full_off
|
||||
|
||||
Reference in New Issue
Block a user