Lege Planungsgrundlage fuer neues Info-Board an

This commit is contained in:
Jesko Anschütz 2026-03-22 12:46:34 +01:00
commit de15ee2e63
5 changed files with 1387 additions and 0 deletions

424
API-MQTT-VERTRAG.md Normal file
View file

@ -0,0 +1,424 @@
# Info-Board Neu - API- und MQTT-Vertrag
## Ziel
Dieses Dokument beschreibt die technische Arbeitsteilung zwischen HTTPS-API und MQTT.
Grundregel:
- HTTPS fuer Daten, Uploads, Konfiguration und Authentifizierung
- MQTT fuer Ereignisse, Heartbeats, Status und Fernsteuerung
## Architekturprinzip
Der Player arbeitet autonom aus lokalem Zustand.
Das bedeutet:
- die API liefert Konfiguration, Playlist und Medieninformationen
- der Player cached diese Daten lokal
- MQTT informiert ueber Aenderungen und traegt leichte Steuerkommandos
- bei Ausfall der Verbindung laeuft der Player mit dem letzten gueltigen Stand weiter
## HTTPS-API
### Authentifizierung
Die Web-Oberflaechen nutzen benutzerbezogene Sessions oder Tokens.
Die Player nutzen ein geraetebezogenes Token.
Empfohlene Aufteilung:
- Browser-UI: Cookie-Session oder JWT
- Player: statischer oder rotierbarer API-Token je Screen
## API-Bereiche
### 1. Auth
Zweck:
- Login
- Logout
- Session-Pruefung
Beispiel-Endpunkte:
- `POST /api/v1/auth/login`
- `POST /api/v1/auth/logout`
- `GET /api/v1/auth/me`
### 2. Tenants und Benutzer
Zweck:
- Verwaltung von Firmen und Benutzern
Beispiel-Endpunkte:
- `GET /api/v1/tenants`
- `POST /api/v1/tenants`
- `GET /api/v1/tenants/:tenantId/users`
- `POST /api/v1/tenants/:tenantId/users`
### 3. Screens
Zweck:
- technische und fachliche Verwaltung der Monitore
Beispiel-Endpunkte:
- `GET /api/v1/screens`
- `GET /api/v1/screens/:screenId`
- `PATCH /api/v1/screens/:screenId`
- `GET /api/v1/screens/:screenId/status`
- `GET /api/v1/screens/:screenId/snapshots/latest`
### 4. Medien
Zweck:
- Uploads
- Listenansicht
- Loeschen
- Metadatenbearbeitung
Beispiel-Endpunkte:
- `GET /api/v1/media`
- `POST /api/v1/media/upload`
- `POST /api/v1/media/import-url`
- `PATCH /api/v1/media/:mediaId`
- `DELETE /api/v1/media/:mediaId`
### 5. Playlists
Zweck:
- Playlist lesen und bearbeiten
- Reihenfolge und Zeitfenster verwalten
Beispiel-Endpunkte:
- `GET /api/v1/screens/:screenId/playlist`
- `PUT /api/v1/screens/:screenId/playlist`
- `POST /api/v1/screens/:screenId/playlist/items`
- `PATCH /api/v1/screens/:screenId/playlist/items/:itemId`
- `DELETE /api/v1/screens/:screenId/playlist/items/:itemId`
### 6. Player-Sync
Zweck:
- geraeteseitiger Abruf von Konfiguration und Daten
- differenzieller Sync ueber Revisionen
Beispiel-Endpunkte:
- `POST /api/v1/player/register`
- `GET /api/v1/player/config`
- `GET /api/v1/player/playlist`
- `GET /api/v1/player/media-manifest`
- `GET /api/v1/player/media/:mediaId/download`
- `POST /api/v1/player/status`
- `POST /api/v1/player/snapshot`
### 7. Admin-Kommandos
Zweck:
- Ausloesen von Fernaktionen
Beispiel-Endpunkte:
- `POST /api/v1/screens/:screenId/commands/reload`
- `POST /api/v1/screens/:screenId/commands/restart-player`
- `POST /api/v1/screens/:screenId/commands/reboot`
- `POST /api/v1/screens/:screenId/commands/display-on`
- `POST /api/v1/screens/:screenId/commands/display-off`
## API-Antworten fuer den Player
### `GET /api/v1/player/config`
Liefert:
- `screen_id`
- `screen_slug`
- `config_revision`
- `player_settings`
- `fallback_dir`
- `snapshot_interval_seconds`
- `offline_overlay_enabled`
- `rotation`
### `GET /api/v1/player/playlist`
Liefert:
- `playlist_id`
- `playlist_revision`
- `default_duration_seconds`
- `fallback_enabled`
- `items[]`
Jedes Item enthaelt mindestens:
- `id`
- `type`
- `src`
- `duration_seconds`
- `load_timeout_seconds`
- `cache_policy`
- `on_error`
- `retry_count`
- `valid_from`
- `valid_until`
- `enabled`
### `GET /api/v1/player/media-manifest`
Liefert:
- `media_revision`
- `assets[]`
Jeder Asset-Eintrag enthaelt mindestens:
- `id`
- `type`
- `source_kind`
- `download_url` nullable
- `original_url` nullable
- `checksum`
- `size_bytes`
- `mime_type`
- `cache_required`
## MQTT-Verwendung
MQTT dient nur der schnellen, leichten Signal- und Statuskommunikation.
Nicht ueber MQTT transportieren:
- Datei-Uploads
- grosse JSON-Payloads fuer komplette Medienlisten
- eigentliche Mediendateien
## MQTT-Topics
### Heartbeat
- `signage/screen/<screen-id>/heartbeat`
Payload-Beispiel:
```json
{
"screen_id": "info01",
"ts": "2026-03-22T12:00:00Z",
"online": true,
"server_connected": true,
"mqtt_connected": true,
"player_version": "0.1.0",
"uptime_seconds": 86400
}
```
### Status
- `signage/screen/<screen-id>/status`
Payload-Beispiel:
```json
{
"screen_id": "info01",
"ts": "2026-03-22T12:00:00Z",
"current_item_id": "pli_123",
"current_item_type": "image",
"current_item_label": "Begruessung",
"current_item_started_at": "2026-03-22T11:59:45Z",
"current_item_duration_seconds": 20,
"overlay_state": "online",
"cache_state": "ok",
"error_code": null,
"error_message": null
}
```
### Events
- `signage/screen/<screen-id>/event`
Typische Events:
- `playlist_changed`
- `sync_completed`
- `sync_failed`
- `item_failed`
- `browser_restarted`
- `snapshot_uploaded`
### Commands
- `signage/screen/<screen-id>/command`
Payload-Beispiel:
```json
{
"command_id": "cmd_456",
"type": "reload",
"payload": {},
"requested_at": "2026-03-22T12:00:00Z"
}
```
### ACK
- `signage/screen/<screen-id>/ack`
Payload-Beispiel:
```json
{
"command_id": "cmd_456",
"screen_id": "info01",
"ts": "2026-03-22T12:00:02Z",
"result": "ok",
"message": "playlist reloaded"
}
```
## QoS- und Broker-Hinweise
Empfehlung:
- Heartbeat: QoS 0 oder 1
- Status: QoS 0 oder 1
- Commands: QoS 1
- ACK: QoS 1
Zusatzempfehlungen:
- Last-Will fuer Offline-Erkennung nutzen
- retained Messages nur sparsam und gezielt einsetzen
- kein Missbrauch von retained Commands
## Trennung von Pull und Push
### Pull ueber HTTPS
Der Player holt aktiv:
- Konfiguration
- Playlist
- Medienmanifest
- eigentliche Medien
### Push ueber MQTT
Der Server signalisiert aktiv:
- es gibt neue Konfiguration
- es gibt neue Playlist-Daten
- fuehre einen Befehl aus
Beispielablauf bei Playlist-Aenderung:
1. Benutzer speichert Playlist in der Web-UI
2. Server erhoeht `playlist_revision`
3. Server sendet Event oder Command `reload`
4. Player ruft `GET /api/v1/player/playlist` ab
5. Player synchronisiert fehlende Medien
6. Player bestaetigt erfolgreichen Reload
## Offline-Verhalten
### Wenn HTTPS ausfaellt
- Player nutzt letzte lokale Konfiguration
- Player nutzt letzte lokale Playlist
- Player nutzt lokalen Mediencache
- Overlay signalisiert Offline-Zustand
### Wenn MQTT ausfaellt
- Player laeuft normal weiter
- periodischer HTTPS-Sync erkennt spaetere Aenderungen trotzdem
- Admin-Kommandos werden ggf. verzoegert, aber nicht die Anzeige selbst
### Wenn beides ausfaellt
- Player bleibt autonom betriebsfaehig
- keine frischen Inhalte oder Befehle mehr
- bestehende Playlist/Fallback laufen weiter
## Web-Inhalte und Fehlerstrategie
Webseiten sind der stoeranfaelligste Medientyp.
Regeln:
- Chromium zeigt immer nur die lokale `player-ui`
- `player-ui` bettet Web-Inhalte kontrolliert ein
- jedes Web-Item hat einen `load_timeout_seconds`
- bei Fehler gilt `on_error`
Typische Fehlerpfade:
- 404 oder DNS-Problem -> `skip` oder `fallback`
- langsamer Ladevorgang -> Timeout -> `retry` oder `skip`
- Renderer-Fehler -> lokaler Watchdog startet den Renderer neu
## Screenshot-Upload
Der Player erstellt Screenshots:
- bei Item-Wechsel
- zyklisch
- auf Admin-Anforderung
Ablauf:
1. Player erzeugt Bild lokal
2. Player sendet es per HTTPS an `POST /api/v1/player/snapshot`
3. Server speichert das Bild und aktualisiert die letzte Vorschau des Screens
## Sicherheitsgrundsaetze
- jeder Player hat eine eigene technische Identitaet
- API und MQTT werden authentifiziert
- Tenant-Benutzer sehen nur tenantbezogene Objekte
- Admin-Kommandos werden serverseitig protokolliert
- Screenshots und Statusdaten sind nur fuer berechtigte Nutzer sichtbar
## Minimale API-Frequenzen
Sinnvolle Startwerte:
- Heartbeat alle 30 Sekunden
- Status-Update bei Item-Wechsel und zusaetzlich alle 60 Sekunden
- Konfigurations-/Playlist-Poll als Fallback alle 5 Minuten
- Snapshot alle 60 Sekunden oder bei Item-Wechsel
## V1-Abgrenzung
Fuer Version 1 bewusst nicht zwingend enthalten:
- Live-Video-Stream der Monitore
- komplexe Kampagnenlogik mit Prioritaeten
- Multi-Screen-Synchronisation auf Frame-Ebene
- serverseitiges Video-Transcoding
## Empfohlene naechste Dokumente
Als direkte Folge sollten entstehen:
- technisches Player-Konzept
- Server-Komponentenplan
- Ansible-Deploymentplan
- Rechte- und Authentifizierungskonzept

391
DATENMODELL.md Normal file
View file

@ -0,0 +1,391 @@
# Info-Board Neu - Datenmodell
## Ziel
Das Datenmodell trennt klar zwischen:
- Mandanten und Benutzern
- Bildschirmen und ihrer technischen Laufzeitinformation
- Medien und Playlists
- Steuerbefehlen und Rueckmeldungen
Das Modell ist so ausgelegt, dass:
- jede Firma nur ihren eigenen Monitor bzw. Kanal pflegt
- die Administration alle Monitore zentral sehen und steuern kann
- Offline-Betrieb der Player moeglich bleibt
- Zeitsteuerung mit `valid_from` und `valid_until` sauber abbildbar ist
## Kernentitaeten
### `tenant`
Repraesentiert eine Firma bzw. einen abgeschotteten Inhaltsbereich.
Felder:
- `id`
- `slug`
- `name`
- `active`
- `created_at`
- `updated_at`
Hinweise:
- ein Tenant kann spaeter mehrere Screens haben, auch wenn zunaechst meist 1:1 gearbeitet wird
- alle Medien und Playlists sind einem Tenant zugeordnet
### `user`
Repraesentiert einen Benutzer der Firmen- oder Admin-Oberflaeche.
Felder:
- `id`
- `tenant_id` nullable fuer globale Admins
- `username`
- `email`
- `password_hash`
- `role` (`admin`, `tenant_user`)
- `active`
- `last_login_at`
- `created_at`
- `updated_at`
Regeln:
- `tenant_user` darf nur Daten des eigenen Tenants sehen und bearbeiten
- `admin` darf alle Tenants und alle Screens verwalten
### `screen`
Repraesentiert einen physischen Monitor bzw. einen Player-Client.
Felder:
- `id`
- `tenant_id`
- `slug`
- `name`
- `description`
- `location`
- `hardware_name`
- `enabled`
- `rotation` (`0`, `90`, `180`, `270`)
- `resolution_width`
- `resolution_height`
- `fallback_dir`
- `snapshot_interval_seconds`
- `offline_overlay_enabled`
- `created_at`
- `updated_at`
Hinweise:
- der `slug` dient als technische Kennung fuer Konfiguration, API und MQTT
- `fallback_dir` beschreibt die lokale oder synchronisierte Fallback-Quelle auf dem Client
### `screen_registration`
Optionale Trennung zwischen fachlichem Screen und technischer Registrierung.
Felder:
- `id`
- `screen_id`
- `device_uuid`
- `hostname`
- `api_token_hash`
- `mqtt_client_id`
- `last_seen_at`
- `last_ip`
- `player_version`
- `os_version`
- `created_at`
- `updated_at`
Zweck:
- Wiedererkennung eines konkreten Geraets
- sichere Kommunikation mit API und Broker
- technische Inventarisierung
### `media_asset`
Repraesentiert ein verwaltetes Medium.
Felder:
- `id`
- `tenant_id`
- `screen_id` nullable
- `title`
- `description`
- `type` (`image`, `video`, `pdf`, `web`)
- `source_kind` (`upload`, `remote_url`)
- `storage_path` nullable bei reinen Web-URLs
- `original_url` nullable
- `mime_type`
- `checksum`
- `size_bytes`
- `enabled`
- `created_by_user_id`
- `created_at`
- `updated_at`
Regeln:
- `upload` bedeutet: Datei liegt im Medien-Storage
- `remote_url` bedeutet: Quelle ist extern und wird vom Player oder Server gecacht
- `screen_id` kann optional gesetzt werden, wenn ein Medium nur fuer einen Monitor gedacht ist
### `playlist`
Repraesentiert eine logische Playlist eines Screens.
Felder:
- `id`
- `tenant_id`
- `screen_id`
- `name`
- `is_active`
- `default_duration_seconds`
- `fallback_enabled`
- `fallback_dir`
- `shuffle_enabled`
- `created_at`
- `updated_at`
Regeln:
- pro Screen gibt es in v1 genau eine aktive Playlist
- spaeter koennen mehrere Playlists mit Umschaltung moeglich werden
### `playlist_item`
Repraesentiert einen einzelnen Eintrag in einer Playlist.
Felder:
- `id`
- `playlist_id`
- `screen_id`
- `order_index`
- `media_asset_id` nullable
- `type` (`image`, `video`, `pdf`, `web`, `dir`)
- `src`
- `title`
- `duration_seconds`
- `load_timeout_seconds`
- `cache_policy` (`required`, `prefer_cache`, `no_cache`)
- `on_error` (`skip`, `retry`, `fallback`)
- `retry_count`
- `valid_from` nullable
- `valid_until` nullable
- `enabled`
- `created_at`
- `updated_at`
Regeln:
- `media_asset_id` wird bei verwalteten Medien gesetzt
- `src` enthaelt den effektiven Pfad oder die URL, damit der Player auch ohne Join-Struktur arbeiten kann
- `type=dir` erlaubt definierte Verzeichnisreferenzen innerhalb der Playlist
### `playlist_item_dir_rule`
Optionale Zusatzregel fuer `type=dir`.
Felder:
- `id`
- `playlist_item_id`
- `directory_path`
- `sort_mode` (`name_asc`, `name_desc`, `mtime_asc`, `mtime_desc`, `shuffle`)
- `per_item_duration_seconds`
- `recursive`
- `file_filter` nullable
Zweck:
- sauberere Modellierung fuer Verzeichnisbasierte Fallbacks oder Einschuebe
### `screen_status`
Repraesentiert den letzten bekannten Laufzeitstatus eines Players.
Felder:
- `screen_id`
- `online`
- `server_connected`
- `mqtt_connected`
- `last_heartbeat_at`
- `last_sync_at`
- `current_playlist_id`
- `current_playlist_item_id` nullable
- `current_item_type` nullable
- `current_item_label` nullable
- `current_item_started_at` nullable
- `current_item_duration_seconds` nullable
- `cache_state` (`ok`, `stale`, `missing`, `error`)
- `overlay_state` (`online`, `degraded`, `offline`)
- `error_code` nullable
- `error_message` nullable
- `player_version`
- `uptime_seconds`
- `free_disk_bytes` nullable
- `temperature_celsius` nullable
- `updated_at`
Hinweise:
- `screen_status` ist eine verdichtete Sicht fuer Dashboard und Ueberwachung
- historische Verlaeufe koennen spaeter separat gespeichert werden
### `screen_snapshot`
Repraesentiert eine Vorschauaufnahme des aktuellen Bildschirminhalts.
Felder:
- `id`
- `screen_id`
- `captured_at`
- `storage_path`
- `width`
- `height`
- `mime_type`
- `source` (`scheduled`, `item_change`, `manual`)
Regeln:
- in der UI wird typischerweise nur der letzte Snapshot angezeigt
- aeltere Snapshots koennen nach Zeit oder Anzahl aufgeraeumt werden
### `device_command`
Repraesentiert einen an einen Screen gesendeten Befehl.
Felder:
- `id`
- `screen_id`
- `command_type` (`reload`, `restart_player`, `reboot`, `display_on`, `display_off`, `refresh_snapshot`, `clear_cache`)
- `payload_json`
- `requested_by_user_id`
- `requested_at`
- `delivery_state` (`queued`, `sent`, `acknowledged`, `failed`, `expired`)
- `delivered_at` nullable
- `acknowledged_at` nullable
- `result_code` nullable
- `result_message` nullable
Zweck:
- nachvollziehbare Fernsteuerung mit Audit-Trail
### `sync_state`
Repraesentiert den Synchronisationsstand eines Screens.
Felder:
- `screen_id`
- `config_revision`
- `playlist_revision`
- `media_revision`
- `last_successful_sync_at`
- `last_failed_sync_at` nullable
- `last_error_message` nullable
Zweck:
- Erkennung, ob ein Player auf dem aktuellen Stand ist
## Beziehungen
- ein `tenant` hat viele `users`
- ein `tenant` hat viele `screens`
- ein `tenant` hat viele `media_assets`
- ein `screen` hat genau eine aktive `playlist` in v1
- eine `playlist` hat viele `playlist_items`
- ein `screen` hat genau einen aktuellen `screen_status`
- ein `screen` hat viele `screen_snapshots`
- ein `screen` hat viele `device_commands`
- ein `screen` hat genau einen aktuellen `sync_state`
## Zugriffsregeln
### Tenant-User
Ein `tenant_user` darf:
- nur den eigenen `tenant` sehen
- nur den eigenen `screen` bzw. die dem Tenant zugeordneten Screens sehen
- nur eigene Medien hochladen und pflegen
- nur eigene Playlists bearbeiten
- nur die Vorschau des eigenen Screens sehen
Ein `tenant_user` darf nicht:
- andere Tenants sehen
- globale Steuerkommandos senden
- andere Screens administrieren
### Admin
Ein `admin` darf:
- alle Tenants, Screens, Medien und Playlists sehen
- globale Steuerbefehle senden
- Vorschau und Status aller Screens sehen
- neue Screens und Benutzer anlegen
## Revisions- und Caching-Modell
Damit Offline-Betrieb einfach bleibt, arbeitet der Player nicht gegen beliebige Einzelobjekte, sondern gegen Revisionen.
Sinnvoll sind mindestens:
- `playlist_revision`
- `media_revision`
- `config_revision`
Der Player kann damit erkennen:
- ob neue Konfiguration vorliegt
- ob Medien nachgeladen werden muessen
- ob die lokale Kopie noch gueltig ist
## Beispiel fuer aktive Playlist-Auswertung
Ein `playlist_item` ist aktiv, wenn:
- `enabled = true`
- `valid_from` leer oder in der Vergangenheit liegt
- `valid_until` leer oder in der Zukunft liegt
Wenn kein aktives Item existiert:
- greift die globale Fallback-Regel des Screens oder der Playlist
## Beispiel fuer Admin-Steuerung
Wenn ein Admin `reload` ausloest:
1. Ein `device_command` wird gespeichert
2. der Befehl wird per MQTT an den Screen signalisiert
3. der Player fuehrt den Reload aus
4. der Player sendet ein ACK
5. `device_command.delivery_state` wird aktualisiert
## Offene spaetere Erweiterungen
- mehrere Screens pro Tenant in der Firmenoberflaeche
- Vorlagen und globale Playlists
- Historie der Statusdaten als Zeitreihe
- Geplante Kampagnen mit Prioritaeten
- serverseitige Konvertierung oder Transkodierung von Medien

416
PLAN.md Normal file
View file

@ -0,0 +1,416 @@
# Info-Board Neu - Architekturplan
## Zielbild
Das neue System ersetzt die bestehende `pictur`-/LXDE-basierte Loesung durch eine schlanke, zentral verwaltete Signage-Plattform.
Ziele:
- moeglichst wenig Ballast auf den Player-Clients
- Anzeige von Bildern, Videos, PDFs und Webseiten
- flexible Playlist pro Monitor
- Fallback auf sequentielle Anzeige aller Medien aus einem konfigurierten Verzeichnis
- mandantenfaehige Pflegeoberflaeche pro Monitor/Firma
- Admin-Oberflaeche fuer Gesamtsteuerung aller Monitore
- autonomer Offline-Betrieb bei Serverausfall
- einfache Verteilung per Ansible
- moeglichst geringe Abhaengigkeit vom Unterbau innerhalb Debian-/Raspberry-Pi-OS-Welt
## Basisentscheidungen
### Betriebssystem
- Player-Basis: aktuelles Raspberry Pi OS Lite auf Debian-13-Basis
- Server-Basis: Debian-artiges System, bevorzugt containerisiert
- keine volle Desktop-Umgebung auf den Playern
### Display-Stack
- fuer Version 1 X11 statt Wayland
- Grund: konservativer, besser vorhersehbar fuer Chromium-Kiosk, Screenshot-Erzeugung, Watchdogs und Raspberry-spezifische Display-Steuerung
- kein LXDE, kein LightDM als Komfortschicht, nur ein minimaler X11-Kiosk-Stack
### Container-Strategie
- Server: Docker Compose ist sinnvoll und empfohlen
- Player: kein Voll-Docker-Ansatz fuer den Grafikpfad in Version 1
- Player besser als native Dienste mit systemd + Chromium-Kiosk
## Gesamtarchitektur
Das System besteht aus zwei Hauptteilen:
1. zentraler Server fuer Verwaltung, API, Medien, Rechte und Steuerung
2. schlanke Player-Clients auf den Monitoren
### Server-Komponenten
- Reverse Proxy
- Backend API
- Admin-Oberflaeche
- mandantenbezogene Management-Oberflaeche
- PostgreSQL
- MQTT-Broker
- Dateispeicher fuer Uploads, Vorschaubilder und Screenshots
### Player-Komponenten
- `player-agent` als nativer systemd-Dienst
- `player-ui` als lokale Web-Anwendung
- Chromium im Kiosk-Modus, der nur die lokale `player-ui` rendert
- lokaler Cache fuer Playlist, Medien und Status
## Rollenmodell
### Monitor-/Firmen-Oberflaeche
Jede Firma sieht nur den ihr zugeordneten Monitor bzw. Kanal.
Funktionen:
- Medien hochladen
- Medien loeschen und ordnen
- Playlist pflegen
- Anzeigedauer je Eintrag festlegen
- `valid_from` und `valid_until` setzen
- lokale Vorschau des eigenen Monitors sehen
### Admin-Oberflaeche
Die Administration der Schule sieht und steuert die gesamte Anlage.
Funktionen:
- Inhalte aller Monitore verwalten
- Monitore global uebersichtlich sehen
- Vorschau/Screenshots aller Monitore sehen
- Reload des Players ausloesen
- Seite neu laden
- Dienste neu starten
- Monitore rebooten
- Monitore an- und ausschalten
## Datenmodell
### Zentrale Entitaeten
- `tenant`
- `screen`
- `user`
- `media_asset`
- `playlist`
- `playlist_item`
- `screen_status`
- `screen_snapshot`
- `device_command`
- `sync_state`
### Wichtige Felder
#### `screen`
- `id`
- `name`
- `tenant_id`
- `location`
- `rotation`
- `resolution`
- `enabled`
#### `media_asset`
- `id`
- `tenant_id`
- `type` (`image`, `video`, `pdf`, `web`)
- `source_kind` (`upload`, `remote_url`)
- `storage_path`
- `original_url`
- `checksum`
- `mime_type`
- `created_at`
#### `playlist_item`
- `id`
- `playlist_id`
- `screen_id`
- `order_index`
- `type`
- `src`
- `duration`
- `valid_from`
- `valid_until`
- `load_timeout`
- `cache_policy`
- `on_error`
- `enabled`
#### `screen_status`
- `screen_id`
- `online`
- `last_heartbeat`
- `current_item`
- `current_type`
- `current_since`
- `last_sync_at`
- `server_connected`
- `cache_state`
- `error_state`
#### `screen_snapshot`
- `screen_id`
- `captured_at`
- `image_path`
## Playlist-Konzept
### Unterstuetzte Typen
- Bild
- Video
- PDF
- Webseite
- Verzeichnisreferenz als Fallback-/Sammelquelle
### Pro Eintrag steuerbar
- Dauer
- Gueltigkeit ueber `valid_from` und `valid_until`
- Reihenfolge
- Timeout fuer das Laden
- Cache-Strategie
- Fehlerverhalten (`skip`, `retry`, `fallback`)
### Fallback-Regel
Wenn keine aktive Playlist vorhanden ist oder kein gueltiges Item aktiv ist, zeigt der Player alle Medien aus einem konfigurierten Fallback-Verzeichnis nacheinander an.
Eigenschaften:
- deterministische Reihenfolge, standardmaessig alphabetisch
- medientypgerechte Anzeigezeiten
- funktioniert komplett lokal aus dem Cache oder Dateisystem
## Offline- und Cache-Strategie
Offline-Betrieb ist Pflicht.
Der Player speichert lokal:
- die letzte gueltige Playlist
- alle noetigen Medien
- letzten bekannten Status
- letzte Server-/Sync-Metadaten
Bei Serverausfall:
- Wiedergabe laeuft lokal weiter
- ein kleines Overlay-Symbol zeigt den Offline-Zustand an
- Heartbeat wird lokal gepuffert oder beim Wiederverbinden aktualisiert
### Overlay-Zustaende
- gruen: online
- gelb: Verbindung instabil oder Daten veraltet
- rot: keine Verbindung zum Server
## Vermeidung von Chromium-Haengern
Chromium rendert niemals direkt externe Medien- oder Playlist-URLs.
Stattdessen:
- Chromium zeigt nur `http://127.0.0.1:<port>/player`
- die lokale `player-ui` kapselt alle Medien und Fehlerzustande
- externe Medien werden vorher geladen oder lokal gespiegelt, wo immer moeglich
Konsequenzen:
- keine haengenden 404-Fehlerseiten als Hauptansicht
- Timeouts pro Inhalt moeglich
- bei Fehlern: Skip oder Fallback statt Stehenbleiben
- lokaler Watchdog kann Anzeige und Browser neu initialisieren
## Vorschaukonzept
Eine Vorschau ist sinnvoll, aber nicht als permanenter Livestream.
Empfohlene Umsetzung:
- Screenshot bei Item-Wechsel
- zusaetzlicher Screenshot in Intervallen, z. B. alle 30 bis 60 Sekunden
- Anzeige des letzten Screenshots in Admin- und Firmen-Oberflaeche
- ergaenzt durch strukturierte Statusdaten
Statusdaten:
- aktuelles Item
- Typ
- Startzeit
- Restlaufzeit
- letzter Heartbeat
- letzter Sync
- Fehlerstatus
## Kommunikation
### HTTPS
Fuer:
- Login
- Uploads
- API-Aufrufe
- Playlist-Bearbeitung
- Download von Medien und Konfiguration
- Abruf von Screenshots und Status
### MQTT
Fuer:
- Heartbeat
- Statusmeldungen
- Kommandos
- Acknowledgements
- Hinweis auf Playlist-Aenderungen
### MQTT-Topics
- `signage/screen/<screen-id>/heartbeat`
- `signage/screen/<screen-id>/status`
- `signage/screen/<screen-id>/event`
- `signage/screen/<screen-id>/command`
- `signage/screen/<screen-id>/ack`
### Befehle
- `reload`
- `restart_player`
- `reboot`
- `display_on`
- `display_off`
- `refresh_snapshot`
- `clear_cache`
## Player-Aufbau
### `player-agent`
Aufgaben:
- Registrierung des Geraets
- HTTPS-Sync
- MQTT-Kommunikation
- Cache-Verwaltung
- Befehlsausfuehrung
- Screenshot-Erzeugung
- Ueberwachung lokaler Dienste
### `player-ui`
Aufgaben:
- lokale Wiedergabeoberflaeche
- Darstellung von Bild, Video, PDF und Web
- Auswertung von Zeitfenstern
- Umschalten zwischen Playlist und Fallback
- Offline-/Fehler-Overlay
## Server-Aufbau
### Kernkomponenten
- API-Backend
- Admin-Frontend
- Tenant-Frontend
- MQTT-Broker
- Datenbank
- Media-Storage
### Betriebsform
- bevorzugt per Docker Compose
- klare Trennung von persistenten Daten, Konfiguration und Deploy-Code
## Minimale Abhaengigkeiten auf dem Player
Das Ziel ist ein moeglichst kleiner Satz an Laufzeitabhaengigkeiten.
Bevorzugt:
- Raspberry Pi OS Lite
- Xorg-Minimalstack
- Chromium
- ein eigenes Player-Binary oder eine sehr schlanke Laufzeit
- systemd
Vermeiden:
- voller Desktop
- mehrere externe Viewer wie `feh`, `vlc`, `wmctrl`, `xdg-open`
- Python-/Node-Laufzeiten mit grossem Paketbaum direkt auf dem Client, wenn vermeidbar
## Ansible-Zielstruktur
### Rollen
- `signage_base`
- `signage_player`
- `signage_display`
- `signage_server`
### Konfigurationspfade auf dem Client
- `/etc/signage/config.yml`
- `/var/lib/signage/cache/`
- `/var/lib/signage/media/`
- `/var/lib/signage/state/`
- `/var/log/signage/`
## Migrationsstrategie
1. Architektur finalisieren
2. Datenmodell finalisieren
3. Minimalen Player-Prototyp bauen
4. Server-Minimalversion bauen
5. Referenz-Raspi aufsetzen
6. Offline-/Netzfehler-/Timeout-Szenarien testen
7. Management-Oberflaechen ausbauen
8. Ansible-Rollen erstellen
9. Einen Pilotmonitor migrieren
10. Restliche Monitore sukzessive migrieren
## Teststrategie
Zu testen sind mindestens:
- kurzer Netzverlust
- kompletter Serverausfall
- MQTT-Ausfall bei funktionierendem HTTPS
- HTTPS-Ausfall bei funktionierendem MQTT
- kaputte URL in Playlist
- defekte PDF-Datei
- grosses Video
- Wechsel von `valid_from`/`valid_until` waehrend des laufenden Betriebs
- Reload aus der Admin-Oberflaeche
- Dienstneustart aus der Admin-Oberflaeche
- Reboot aus der Admin-Oberflaeche
- Display an/aus
- Screenshot-Erzeugung unter Last
## Projektstruktur-Empfehlung
Ein separates Projektverzeichnis ist sinnvoll, um die neue Architektur sauber von der bestehenden Netboot-Struktur zu trennen.
Empfohlener Pfad:
- `/srv/docker/info-board-neu`
Dieses Verzeichnis sollte mindestens enthalten:
- `PLAN.md`
- `TODO.md`
- spaeter `compose/`, `ansible/`, `server/`, `player/`, `docs/`

24
README.md Normal file
View file

@ -0,0 +1,24 @@
# Info-Board Neu
Dieses Verzeichnis enthaelt die Planung und spaetere Umsetzung der neuen Info-Board-Plattform.
Die Trennung von `/srv/docker/infoboard-netboot` ist sinnvoll, damit:
- die bestehende produktive Netboot-Struktur unangetastet bleibt
- Planung, Prototypen und neue Deployments sauber getrennt sind
- Server-, Player- und Ansible-Artefakte nicht mit Altbestand vermischt werden
## Aktueller Stand
- Architekturplan: `PLAN.md`
- Umsetzungs-Todo: `TODO.md`
- Datenmodell: `DATENMODELL.md`
- API- und MQTT-Vertrag: `API-MQTT-VERTRAG.md`
## Empfohlene spaetere Struktur
- `docs/` fuer weitere Architektur- und Betriebsdokumente
- `server/` fuer Backend, Frontends und Compose-Dateien
- `player/` fuer Agent, UI und lokale Startlogik
- `ansible/` fuer Rollen, Inventories und Deployments
- `scripts/` fuer Hilfsskripte

132
TODO.md Normal file
View file

@ -0,0 +1,132 @@
# Info-Board Neu - Umsetzungs-Todo
## Phase 0 - Projektbasis
- [ ] Projektverzeichnisstruktur unter `/srv/docker/info-board-neu` festlegen
- [ ] Namenskonventionen fuer Server, Player, Rollen und Pakete definieren
- [ ] Dokumentationsstruktur fuer Architektur, Betrieb und Deployment anlegen
- [ ] Entscheidung fuer Server-Tech-Stack dokumentieren
- [ ] Entscheidung fuer Player-Implementierung dokumentieren
## Phase 1 - Fachliches Fundament
- [ ] Rollenmodell fuer `admin` und monitorgebundene Nutzer final festschreiben
- [ ] Datenmodell fuer `tenant`, `screen`, `user`, `media_asset`, `playlist`, `playlist_item`, `screen_status`, `screen_snapshot` definieren
- [ ] Playlist-Semantik mit `duration`, `valid_from`, `valid_until`, `load_timeout`, `cache_policy`, `on_error` spezifizieren
- [ ] Fallback-Regel fuer ungeplante oder leere Inhalte verbindlich definieren
- [ ] Statusmodell fuer Online/Offline/Degraded/Error definieren
- [ ] Kommandokatalog fuer Admin-Aktionen finalisieren
## Phase 2 - Technische Zielarchitektur
- [ ] Server-Komponentenliste finalisieren
- [ ] API-Schnittstellen grob definieren
- [ ] MQTT-Topic-Struktur finalisieren
- [ ] HTTPS- und MQTT-Aufgabentrennung dokumentieren
- [ ] Screenshot-/Vorschaustrategie spezifizieren
- [ ] Offline- und Cache-Strategie bis auf Dateiebene festlegen
- [ ] Sicherheitsmodell fuer Uploads, Login und Rechte pruefen
## Phase 3 - Player-Design
- [ ] Minimalen Paketbedarf fuer den Player auf Raspberry Pi OS Debian 13 ermitteln
- [ ] X11-Minimalkonzept fuer Chromium-Kiosk dokumentieren
- [ ] Startmechanismus fuer Chromium ohne Desktop-Umgebung definieren
- [ ] Verzeichnislayout auf dem Player festlegen
- [ ] `player-agent` fachlich zuschneiden
- [ ] `player-ui` fachlich zuschneiden
- [ ] Watchdog-Konzept fuer Browser und Agent definieren
- [ ] Offline-Overlay-Verhalten spezifizieren
- [ ] Fehlerbehandlung fuer Web-Inhalte und Timeouts ausarbeiten
- [ ] Display-Steuerung fuer An/Aus, Rotation und Neustart planen
## Phase 4 - Server-Design
- [ ] API-Backend fachlich schneiden
- [ ] Admin-Oberflaeche in Hauptbereiche aufteilen
- [ ] Firmen-/Monitor-Oberflaeche in Hauptbereiche aufteilen
- [ ] Storage-Konzept fuer Uploads, Cache-Dateien und Screenshots festlegen
- [ ] Authentifizierungskonzept festlegen
- [ ] Mandantentrennung im Datenmodell und in den APIs absichern
- [ ] Logging- und Monitoring-Konzept definieren
## Phase 5 - Prototyping
- [ ] Minimalen Server-Prototyp bauen
- [ ] Minimalen Player-Agent-Prototyp bauen
- [ ] Minimale Player-UI bauen
- [ ] Lokale Test-Playlist mit Bild, Video, PDF und Webseite anlegen
- [ ] Fallback-Verzeichnisbetrieb demonstrieren
- [ ] `valid_from`/`valid_until` im Prototyp pruefen
- [ ] Offline-Sync mit lokalem Cache pruefen
- [ ] MQTT-Kommandos `reload`, `restart_player`, `reboot`, `display_on`, `display_off` testweise durchspielen
## Phase 6 - Betriebsfaehigkeit
- [ ] Docker-Compose-Setup fuer den Server anlegen
- [ ] systemd-Units fuer den Player erstellen
- [ ] Chromium-Kiosk-Startskript erstellen
- [ ] Screenshot-Erzeugung auf dem Player integrieren
- [ ] Heartbeat- und Statusmeldungen integrieren
- [ ] Fehler- und Wiederanlaufverhalten verifizieren
## Phase 7 - Ansible-Automatisierung
- [ ] Rolle `signage_base` erstellen
- [ ] Rolle `signage_player` erstellen
- [ ] Rolle `signage_display` erstellen
- [ ] Rolle `signage_server` erstellen
- [ ] Inventar-/Variablenmodell fuer mehrere Monitore entwerfen
- [ ] Screen-spezifische Variablen wie `screen_id`, Rotation und Aufloesung abbilden
- [ ] Erstinstallation eines neuen Players automatisieren
- [ ] Update-Rollout eines bestehenden Players automatisieren
## Phase 8 - Pilotbetrieb
- [ ] Einen Referenzmonitor fuer den Pilotbetrieb auswaehlen
- [ ] Pilotmonitor neu aufsetzen
- [ ] Verbindung zum Zentralserver herstellen
- [ ] Upload- und Playlist-Pflege mit einem Testmandanten pruefen
- [ ] Admin-Funktionen am Pilotmonitor pruefen
- [ ] Offline-Betrieb real testen
- [ ] Browser-/Renderer-Stabilitaet ueber laengeren Zeitraum beobachten
## Phase 9 - Migration der Bestandsmonitore
- [ ] Migrationsreihenfolge fuer `info01` bis `info09` festlegen
- [ ] Rueckfallstrategie pro Monitor definieren
- [ ] Altinhalte in das neue Medienmodell ueberfuehren
- [ ] Playlists uebernehmen oder neu modellieren
- [ ] Monitore nacheinander umstellen
- [ ] Nach jeder Umstellung Stabilitaet und Fernsteuerung pruefen
## Phase 10 - Nacharbeiten
- [ ] Betriebsdokumentation schreiben
- [ ] Admin-Handbuch schreiben
- [ ] Kurzhandbuch fuer Firmen-Nutzer schreiben
- [ ] Backup- und Restore-Konzept dokumentieren
- [ ] Update- und Release-Prozess festlegen
- [ ] Langfristige Wayland-Neubewertung fuer spaetere Version vormerken
## Querschnittsthemen
- [ ] Datensicherung fuer Datenbank und Medien einplanen
- [ ] TLS-/Reverse-Proxy-Konzept festlegen
- [ ] Ressourcenverbrauch auf Raspberry Pi beobachten
- [ ] Verhalten bei grossen Videos optimieren
- [ ] Verhalten bei defekten externen Webseiten absichern
- [ ] Datenschutz- und Rechtefragen fuer Screenshots/Vorschauen pruefen
## Erste konkrete Abarbeitungsreihenfolge
- [ ] 1. Projektstruktur im neuen Verzeichnis vervollstaendigen
- [ ] 2. Datenmodell in eigener Datei ausformulieren
- [ ] 3. API- und MQTT-Vertrag definieren
- [ ] 4. Player-Minimalkonzept fuer Raspberry Pi OS Debian 13 festzurren
- [ ] 5. Server-Compose-Grundgeruest erstellen
- [ ] 6. Player-Prototyp mit lokalem Browser-Renderer bauen
- [ ] 7. Offline-Cache und Fallback robust machen
- [ ] 8. UIs fuer Admin und Firmen schrittweise aufbauen
- [ ] 9. Ansible-Rollen erstellen
- [ ] 10. Pilotmonitor migrieren