# Versionierte Updates auf dem Raspberry Pi `tools/upload_to_pi.sh` überträgt per SSH/rsync eine neue Version und richtet bei jedem angewendeten Update den systemd-Autostart der WebUI ein. Ohne `--apply` zeigt es nur eine lokale Vorschau und verbindet sich nicht zum Pi. ## Auf dem Laptop im Projektordner ausführen Das folgende Beispiel verwendet den vom Nutzer bestätigten SSH-Benutzer, Hostnamen und Projektordner. Der absolute Home-Pfad muss auf dem Pi stimmen: ```bash bash tools/upload_to_pi.sh --user marcinator_2 --host raspi5wlan.local \ --remote-dir /home/marcinator_2/halloween --web-host 0.0.0.0 ``` Zum tatsächlichen Update denselben Befehl mit `--apply` ausführen: ```bash bash tools/upload_to_pi.sh --user marcinator_2 --host raspi5wlan.local \ --remote-dir /home/marcinator_2/halloween --web-host 0.0.0.0 --apply ``` `--web-host 0.0.0.0` ermöglicht den gewünschten Heimnetzzugriff auf `http://raspi5wlan.local:8000`. Die WebUI hat noch keinen Login; keine Router-Portfreigabe einrichten. Mit `--web-host 127.0.0.1` stattdessen den SSH-Tunnel aus `SOFTWARE.md` verwenden. Ohne Angabe bleibt die gespeicherte Wahl erhalten; beim ersten Update gilt Loopback. `--install-service` wird weiterhin akzeptiert, ist aber nicht mehr erforderlich. Autostart heißt: Der Webdienst startet beim Hochfahren des Pi. Ein Browser auf dem Laptop wird dadurch nicht automatisch geöffnet. ## Voraussetzungen - Laptop: `bash`, `python3`, `ssh`, `rsync`. - Pi: `bash`, `rsync`, `flock`, `runuser` (util-linux), `python3-venv`, `python3-picamera2`, systemd und nutzbares `sudo` für den normalen SSH-Benutzer. - Der Pi muss die Python-Pakete aus `requirements.txt` herunterladen können. Pro Version wird eine eigene `.venv` mit Systempaketzugriff angelegt; pip verwendet ausschließlich Wheels, keinen impliziten Quellbuild. - Genug Speicher für neue Version, Python-Umgebung und vollständige Sicherung von `data/`. Alte Versionen und Sicherungen werden nicht automatisch gelöscht. - SSH-Hostschlüssel am eigenen Gerät prüfen; die Prüfung bleibt eingeschaltet. - Eine mit Uvicorn manuell gestartete WebUI vor dem ersten Update mit `Strg+C` beenden. Ein bestehender, vom Projekt verwalteter Dienst wird selbst gestoppt. Eine fremde gleichnamige systemd-Unit wird abgelehnt. ## Passwortabfragen beim Update Alle Upload-Schritte nutzen nun **eine gemeinsame SSH-Verbindung**. Mit korrekter Passwortanmeldung ohne SSH-Schlüssel sind normalerweise insgesamt zwei Abfragen nötig: einmal das SSH-Passwort für die Verbindung, einmal das sudo-Passwort für den privilegierten Updateaufruf. Bei Schlüssel-/Agent-Anmeldung entfällt die SSH-Passwortabfrage; bei gültigem sudo-Zeitstempel kann auch diese entfallen. Ein erster Hostschlüssel-Dialog ist keine Passwortabfrage. Der Verbindungssocket liegt in einem privaten temporären Ordner (0700) unter `/tmp` auf dem Laptop. Ordneranlage, rsync und Aktivierung verwenden denselben Socket. Am Ende oder bei Fehlern wird die Master-Verbindung geschlossen und der Socketordner entfernt. Bei Verbindungsverlust fragen spätere Schritte nicht erneut nach einem Passwort (`BatchMode=yes`), sondern brechen bei fehlender Anmeldemöglichkeit ab. SSH-Konfiguration, Schlüssel und Passwörter werden nicht neu gespeichert oder geändert. Die Vorschau öffnet keine Verbindung. Das Uploadskript bereitet die neue Python-Umgebung als normaler Pi-Benutzer vor. Danach läuft die Umschaltung mit **einem einzigen sudo-Aufruf**: Unitprüfung, Dienststopp, Sicherung, Versionswechsel, Dienstneustart und gegebenenfalls Rückfall. Bei gültiger sudo-Anmeldung entfällt die Frage; sonst ist für diesen Aufruf eine Anmeldung nötig. Keine weitere Abfrage während Neustart oder Rückfall, auch bei kurzem oder deaktiviertem sudo-Zeitstempel. SSH-Anmeldung ist davon getrennt. Passwörter werden vom Skript weder gelesen noch gespeichert. Keine sudoers- Ausnahme, kein Passwort per Kommandozeile/Pipe und keine Änderung der sudo-Frist. Abhängigkeiten und Python-Prüfungen bleiben unprivilegiert; der privilegierte Teil startet Python und den Unit-Renderer mit `runuser` als ursprünglichem Pi-Benutzer. Auch der Webdienst behält diesen Benutzer. Die Updatesperre bleibt im wartenden unprivilegierten Elternprozess über den ganzen sudo-Aufruf gehalten. Eine abgelehnte sudo-Anmeldung lässt den aktiven Dienst und Datenbestand bestehen. ## Ablauf und Verzeichnisstruktur 1. Neue Dateien unter `releases/ZEITSTEMPEL-ZUFALL/` übertragen. 2. Mit einer Updatesperre parallele Aktivierung verhindern; neue Python-Umgebung vorbereiten und Importe/Syntax prüfen, während die alte Version weiterläuft. 3. Dienst stoppen; Daten einschließlich SQLite-Dateien und Einstellungen unter `backups/VERSION/data/` sichern. Keine laufende Datenbank kopieren. 4. `current` atomar auf die neue Version umschalten und die Unit aktualisieren. `HALLOWEEN_STATE_DIR` verweist immer auf den gemeinsamen Basisordner `data/`. 5. Autostart aktivieren, Dienst neu starten, `/api/status` bis zu 30 Versuche mit je einer Sekunde Pause abfragen und aktiven Dienststatus prüfen. 6. Bei einem Fehler während der Umschaltung Dienst stoppen, vorherige Version, Daten und Unit wiederherstellen und den alten Dienst samt HTTP-Antwort prüfen. Änderungen des fehlgeschlagenen Starts bleiben in `backups/VERSION/failed-data/`. Die bisherige flache Installation mit `run.py` und `.venv` im Basisordner bleibt beim ersten Update als Rückfallziel bestehen. Ohne vorherige Installation kann ein fehlgeschlagener Erststart nur die Daten und den Zustand ohne neuen Dienst wiederherstellen. Ein Fehlschlag liefert einen Fehlerstatus, auch wenn der Rückfall gelungen ist. Ein unvollständiger Rückfall wird ausdrücklich gemeldet. Abrupter Stromverlust/SIGKILL während des Updates ist nicht automatisch abgedeckt. `data/` wird niemals vom Laptop übertragen: Datenbank, Bilder, Modelle, Modus und Erkennungseinstellungen sowie `follow.json` mit Farbe, Richtungen, Nachführ- und Einrichtgrenzen bleiben auf dem Pi. `.git`, `.venv`, Vorschauen, Referenzen, `.env`, Schlüssel, Versionsordner und Sicherungen werden ebenfalls ausgeschlossen. Eigene Dateien außerhalb von `data/` bleiben im Basisordner, werden aber nicht automatisch in die neue Version übernommen. Das Skript löscht keine alten Pi-Dateien. Systempakete werden nicht geändert. Nach Start gelten die bisherigen Freigabe-/Resetbedingungen der Effektsteuerung. ## Nach einem WebUI-Update Vor dem Update die Kameraservos stoppen und ihre separate Versorgung abziehen. Nach erfolgreichem Upload die Browserseite mit **Strg+F5** neu laden. Auf der Kameraseite `/kamera` stehen die Bewegungs- und Einrichtgrenzen unter dem Bild. `halloween/camera.html` wird zusammen mit dem übrigen Projekt übertragen; es ist kein zusätzlicher Einzeldatei-Upload nötig. Beim Laden alter `follow.json`-Dateien bleiben die Nachführgrenzen erhalten; fehlende Einrichtgrenzen werden mit dem bisherigen Bereich 1100–1900 µs ergänzt. Ein Update erweitert keinen Bewegungsbereich und startet keine Kamerasitzung. Vor einem neuen Bewegungstest die gespeicherten Werte im WebUI prüfen. ## Auf dem Pi prüfen ```bash systemctl status halloween-grab.service systemctl is-enabled halloween-grab.service readlink -f ~/halloween/current sudo journalctl -u halloween-grab.service -n 80 --no-pager ``` Erkennung nach dem ersten Update bei Bedarf im aktiven Versionsordner einrichten: ```bash cd ~/halloween/current sudo apt update sudo apt install python3-opencv python3-numpy HALLOWEEN_STATE_DIR=/home/marcinator_2/halloween/data \ .venv/bin/python tools/install_detection_model.py sudo systemctl restart halloween-grab.service ``` OpenCV mindestens 4.10 und das echte Kamera-/Erkennungsverhalten gemäß `SOFTWARE.md` prüfen. Nachfolgende Versionsumgebungen nutzen dieselben Pi-Systempakete. ## Verifizierter Stand Lokale Tests simulieren Python-Installation, systemd und HTTP-Prüfung und prüfen Versionswechsel, Autostart-Unit, Datenbestand, Start-/HTTP-Fehler, Rückfall, Erstinstallation, Altinstallation und Ablehnung fremder Units/manueller Server. Sie bestätigen keine echte Pi-Installation, Kamera oder Neustartfestigkeit. Der Nutzer hat das echte Livebild und anschließend den erfolgreichen Updateaufruf bestätigt. Autostart nach einem tatsächlichen Pi-Neustart und automatischer Rückfall bei einem Fehler sind noch am Gerät zu prüfen. Die HTTP-Prüfung bestätigt die Status-API, nicht Bildqualität oder Aktoren.