Flask-Verwaltungsoberfläche für Hannah — Räume/Gruppen-, Satelliten-, User-, Settings- und Trigger-Verwaltung, plus eine Self-Service-Startseite für alle Bewohner (Passwort, Telegram-Verknüpfung, Wecker, Nachrichten). Spricht ausschließlich per gRPC mit Hannah Core — kein direkter DB-/Dateizugriff, Core bleibt alleiniger Owner aller Daten.
Extrahiert aus dem Hannah-Monorepo (webui/), siehe CHANGELOG.md. Architektur-/Protokoll-Hintergrund zu Hannah Core: CLAUDE.md.
- Räume & Gruppen — Räume read-only anzeigen, Gruppen anlegen/bearbeiten/löschen und Satelliten zuweisen
- Satelliten — einem Raum zuordnen, Anzeigenamen setzen
- Trigger — Wenn/Und/Außer-wenn/Dann-Regelbuilder (Bedingungstyp
state/time/phrase), löste die frühere separate Routinen-Verwaltung ab - Settings — No-Code-Editor für Core-Settings, Render-Typ (Text/Zeilen-Builder/Key-Value/rohes JSON) wird automatisch aus der Werteform abgeleitet
- BLE-Tags & Fahrzeuge — CRUD inkl. Owner-Zuweisung
- Nutzerverwaltung — User anlegen/bearbeiten/löschen, Trust-Level setzen, mit Residents verknüpfen
- Verlauf — eigene geloggte Interaktionen (Transkript, Kanal, Intent, Antworttext) mit Audio-Wiedergabe; Trust-Level 10 sieht wahlweise den Verlauf anderer User
- Nachrichten — passive Mailbox mit Antwort-Flow, Badge mit ungelesener Anzahl in der Navigation
- Self-Service (
/me) — eigenes Passwort ändern, Telegram-Konto verknüpfen/trennen (bevorzugt per Deep-Link zum Bot, sobald Core einen verbundenen Telegram-Adapter meldet; sonst über das Login Widget, WebUI verifiziert die Signatur selbst), Microsoft-Entra-Konto verknüpfen/trennen (OIDC-Login, Objekt-IDoidlandet alsentra-Account in Core), Wecker verwalten
Ausführliche Bedienungsanleitung je Seite: docs/usage.md.
Jeder Nutzer braucht einen Hannah-Core-Account; der Zugriff auf Admin-Seiten ist per Trust-Level gestaffelt (hannah_webui/extensions.py: TRUST_LEVELS). Self-Service-Seiten (/me, /messages) sind für jeden eingeloggten User offen.
| Seite | Route | Min. Trust-Level |
|---|---|---|
| Startseite / Self-Service | /me |
jeder eingeloggte User |
| Nachrichten | /messages |
jeder eingeloggte User |
| Verlauf (eigener) | /activity-log |
jeder eingeloggte User |
| Verlauf (fremder, per Filter) | /activity-log |
10 |
| Räume | /rooms |
3 |
| Satelliten | /satellites |
5 |
| Trigger anzeigen | /triggers |
5 |
| Trigger anlegen/bearbeiten/löschen | /triggers/... |
7 |
| Gruppen, Settings, BLE-Tags, Fahrzeuge, User | /groups, /settings, /ble-tags, /cars, /users |
10 |
Flask-App-Factory (create_app, hannah_webui/app.py) registriert einen Blueprint je Routen-Gruppe (hannah_webui/blueprints/), hängt den gRPC-Client als app.extensions["hannah"] ein und rendert bei nicht erreichbarer Core eine eigene Fehlerseite statt eines 500ers. Session-basiertes Login gegen Core's Login-RPC. Details, gRPC-Client-Methoden, Blueprint-Liste: CLAUDE.md.
- Eine erreichbare Hannah-Core-Instanz (gRPC) — siehe Hannah-Repo
- Python 3.11+ (nur für lokale Entwicklung/systemd-Deployment, nicht für Docker nötig)
python -m venv venv
venv/Scripts/pip install -r requirements.txt -r tests/requirements-test.txt
cp config.example.yaml config.yaml # secret_key + gRPC-Host anpassen
venv/Scripts/python main.pyTests:
pytest tests/ -vtests/ nutzt FakeHannahClient, einen In-Memory-Stand-in mit echten hannah_pb2-Messages — keine echte Hannah Core nötig.
config.yaml (siehe config.example.yaml) oder Env-Vars — welcher Weg gilt, hängt davon ab, ob am gestarteten Pfad eine config.yaml existiert:
host: "127.0.0.1"
port: 5000
# Signiert die Flask-Session-Cookie — muss über alle gunicorn-Worker und
# Neustarts hinweg stabil sein, sonst werden Nutzer zufällig ausgeloggt.
# Generieren mit: python3 -c "import secrets; print(secrets.token_hex(32))"
secret_key: "..."
# Telegram Login Widget (Account-Verknüpfung in /me) — Bot-Token via
# @BotFather, Domain muss dort per /setdomain auf diese WebUI-Instanz
# freigeschaltet sein.
telegram_bot_token: ""
telegram_bot_username: ""
# Microsoft Entra (Account-Verknüpfung in /me, OIDC-Login) — Single-Tenant-App-
# Registration mit Client-Secret, keine API-Permissions nötig. Redirect-URI (Typ
# "Web") dort eintragen: https://<webui-host>/me/entra/callback
entra_client_id: ""
entra_client_secret: ""
entra_tenant: "" # Tenant-ID (GUID)
grpc:
host: "127.0.0.1"
port: 50051
# Native TLS-Terminierung (#61), z.B. fürs Telegram-Login-Widget in /me (verlangt
# HTTPS). Bei leerem cert_file/key_file wird beim ersten Start ein selbstsigniertes
# Zertifikat generiert und dauerhaft persistiert (Neustarts erzeugen es nie neu).
tls:
enabled: false
cert_file: ""
key_file: ""Äquivalente Env-Vars (Docker-Pfad, kein config.yaml im Image): HANNAH_WEBUI_SECRET_KEY, HANNAH_WEBUI_TELEGRAM_BOT_TOKEN, HANNAH_WEBUI_TELEGRAM_BOT_USERNAME, HANNAH_WEBUI_ENTRA_CLIENT_ID, HANNAH_WEBUI_ENTRA_CLIENT_SECRET, HANNAH_WEBUI_ENTRA_TENANT, HANNAH_WEBUI_GRPC_HOST, HANNAH_WEBUI_GRPC_PORT, HANNAH_WEBUI_TLS_ENABLED, HANNAH_WEBUI_TLS_CERT_FILE, HANNAH_WEBUI_TLS_KEY_FILE. HANNAH_WEBUI_HOST/HANNAH_WEBUI_PORT existieren zwar auch, wirken aber nur bei main.py (lokaler Dev-Server) — der Docker-Entrypoint wsgi.py liest cfg.host/cfg.port gar nicht, die Bind-Adresse steckt fest in gunicorn.conf.py (0.0.0.0:5000, siehe unten).
Das selbstsignierte TLS-Zertifikat landet standardmäßig unter /var/lib/hannah-webui/tls/ (systemd) bzw. /data/tls/ (Docker, siehe docker-compose.example.yml für den nötigen Volume-Mount) — beides Pfade, die Updates/Neustarts überstehen. Ein eigenes Cert/Key lässt sich über cert_file/key_file bzw. die Env-Vars stattdessen fest hinterlegen.
Zwei unabhängige Wege, kein Auto-Update beim Container-Pfad:
curl -fsSL https://dev.kernstock.net/gessinger/voice/hannah-webui/-/raw/master/deploy/install.sh | sudo bashLädt das aktuellste Release vom Hannah Update Server (Channel webui-stable), richtet venv, System-User hannah und den systemd-Service ein. Config danach unter /etc/hannah-webui/config.yaml ablegen und starten:
sudo systemctl enable --now hannah-webuiErneuter Aufruf des Skripts aktualisiert auf die neueste Version; --uninstall entfernt den Service (Config bleibt erhalten). Im laufenden Betrieb hält AutoDeploy (hannah-autodeploy) die Installation automatisch aktuell: pollt den Update Server auf Channel webui-stable, tauscht Dateien, führt pip install -r requirements.txt aus und restartet den Service — install.sh ist nur für Erst-Install/manuelle Reinstalls nötig.
Bind-Adresse/Worker-Anzahl stehen in der .service-Unit, nicht in config.yaml (gunicorn bindet den Socket vor dem WSGI-App-Import).
Multi-Arch-Image (amd64/arm64), Konfiguration ausschließlich per Env-Vars:
docker build -t hannah-webui .
docker run -d \
-p 5000:5000 \
-e HANNAH_WEBUI_SECRET_KEY="..." \
-e HANNAH_WEBUI_GRPC_HOST=hannah-core \
-e HANNAH_WEBUI_GRPC_PORT=50051 \
hannah-webuiBeispiel-Compose-Datei: docker-compose.example.yml.
Aktueller Stand und Änderungshistorie: CHANGELOG.md. Offene Bugs/Features werden als GitLab Issues geführt, nicht hier dupliziert. Architektur-/Entwicklungs-Details: CLAUDE.md.