# 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
- globale Admin-Templates zur monitoruebergreifenden Orchestrierung
- 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

### Sprach- und Laufzeitstrategie

- `Go` ist die bevorzugte Sprache fuer systemnahe und portable Komponenten
- insbesondere gilt das fuer den `player-agent` und nach Moeglichkeit auch fuer zentrale Backend-Komponenten
- Ziel ist eine geringe Zahl externer Laufzeitabhaengigkeiten und moeglichst einfache Verteilung per Ansible
- fuer Browser-Oberflaechen bleibt JavaScript/TypeScript im Frontend natuerlich zulaessig
- falls einzelne Server-Bausteine spaeter aus pragmatischen Gruenden anders umgesetzt werden, soll das begruendet und dokumentiert werden

## 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
- globale Templates erstellen, testen und aktivieren
- temporaere Kampagnen fuer mehrere oder alle Monitore einschalten und wieder abschalten
- neue Monitore initial provisionieren und betriebsbereit ausrollen

### Globale Template-Orchestrierung

Die Admin-Seite bekommt zusaetzlich einen globalen Orchestrierungsbereich.

Ziele:

- grossformatige monitoruebergreifende Botschaften wie `SCHOENE FERIEN` oder `MORZ-INFO`
- saisonale oder veranstaltungsbezogene globale Motive, z. B. Weihnachten
- schnelles temporäres Ueberstimmen des Firmencontents
- Rueckkehr zum normalen Firmencontent auf Knopfdruck

Grundprinzip:

- globale Templates sind administrative Kampagnen mit hoeherer Prioritaet als tenantbezogene Inhalte
- sie koennen auf einzelne Monitore, Monitorgruppen oder alle Monitore angewendet werden
- sie sind zeitlich planbar oder manuell aktivierbar
- nach Deaktivierung faellt der Bildschirm automatisch auf seine normale tenantbezogene Playlist zurueck

### Automatische Screen-Provisionierung

Die Admin-Oberflaeche soll neue Bildschirme nicht nur fachlich anlegen, sondern technisch in Betrieb nehmen koennen.

Zielablauf:

- Admin gibt `display_id`, Ziel-IP und initiale Root-Zugangsdaten an
- das System stellt per SSH eine Erstverbindung her
- ein Deploy-Key oder dedizierter Verwaltungsschluessel wird hinterlegt
- noetige Pakete und Komponenten werden installiert
- Konfiguration fuer den neuen Screen wird ausgerollt
- der Player wird gestartet und meldet sich selbststaendig am Server an

Der Vorgang soll moeglichst als gefuehrter Provisionierungs-Workflow im Admin-Backend verfuegbar sein.

Wichtige Randbedingungen:

- Root-Passwoerter duerfen nicht dauerhaft im Klartext gespeichert werden
- Provisionierungsdaten sind nur fuer Admins sichtbar
- jeder Provisionierungslauf muss nachvollziehbar protokolliert werden
- die eigentliche Ausbringung soll auf einem automatisierbaren Ansible- oder SSH-Workflow basieren, nicht auf ad-hoc-Shellmagie im Browser

## Datenmodell

### Zentrale Entitaeten

- `tenant`
- `screen`
- `user`
- `media_asset`
- `playlist`
- `playlist_item`
- `display_template`
- `template_scene`
- `template_assignment`
- `campaign`
- `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

### Prioritaetsregel fuer globale Templates

Die Inhaltsauswahl pro Screen folgt in v1 einer klaren Prioritaetskette:

1. aktive globale Kampagne bzw. aktives Admin-Template
2. normale tenantbezogene Playlist des Monitors
3. lokaler Fallback aus dem konfigurierten Verzeichnis

Das verhindert spaeteres Anflanschen und macht die Uebersteuerung von Beginn an explizit.

Wichtige Regeln:

- globale Templates sind immer explizit aktiv oder inaktiv
- sie koennen `valid_from` und `valid_until` besitzen
- sie koennen manuell sofort deaktiviert werden
- nach Ablauf oder Deaktivierung setzt der Screen automatisch beim normalen Inhalt fort

## 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

Die Vorschau soll auch sichtbar machen, ob ein Screen gerade:

- tenantbezogenen Normalbetrieb zeigt
- von einer globalen Kampagne uebersteuert wird
- im Fallback-Modus laeuft

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

Technische Vorgabe:

- Implementierung bevorzugt in `Go`
- Auslieferung moeglichst als einzelnes Binary

### `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

Zusaetzliche fachliche Server-Faehigkeiten:

- Verwaltung globaler Templates
- Konfiguration monitoruebergreifender Layouts und Szenen
- Aktivierung, Deaktivierung und Zeitplanung globaler Kampagnen
- technische Provisionierung neuer Screens ueber einen gefuehrten Admin-Workflow

Technische Vorgabe:

- Backend bevorzugt in `Go`, sofern die Entwicklungsdynamik nicht klar dagegen spricht
- Frontends als Web-Anwendungen mit ueblichem Browser-Stack

### 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 in `Go` oder eine gleichwertig 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`
- `signage_provision`

### Provisionierungsmodell

Die Erstinstallation eines neuen Screens soll auf Ansible aufsetzen.

Vorgesehener Ablauf:

1. Screen im Admin-Backend anlegen
2. einmalige technische Zugangsdaten fuer die Erstverbindung angeben
3. Backend startet einen Provisionierungsjob
4. der Job nutzt SSH/Ansible fuer Basis-Setup, Key-Deployment, Paketinstallation und Konfiguration
5. nach erfolgreicher Provisionierung verwendet der Screen nur noch den hinterlegten Verwaltungsschluessel und seine technische Geraeteidentitaet

Damit wird die GUI zur komfortablen Steuerschicht ueber einem reproduzierbaren Automatisierungsprozess.

### 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

Parallel von Anfang an mitdenken:

- globale Template-Orchestrierung darf kein Nachbau sein, sondern muss im Inhalts- und Prioritaetsmodell direkt vorgesehen werden

## 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/`
