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

### 3a. Provisionierung

Zweck:

- neue Screens im Admin-Backend anlegen und technisch ausrollen
- Provisionierungslaeufe starten, beobachten und auswerten

Beispiel-Endpunkte:

- `POST /api/v1/screens/provision`
- `GET /api/v1/provisioning-jobs`
- `GET /api/v1/provisioning-jobs/:jobId`
- `POST /api/v1/provisioning-jobs/:jobId/retry`
- `POST /api/v1/provisioning-jobs/:jobId/cancel`

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

### 5a. Globale Templates und Kampagnen

Zweck:

- globale monitoruebergreifende Templates verwalten
- Kampagnen konfigurieren, aktivieren und deaktivieren
- Screens gezielt global uebersteuern

Beispiel-Endpunkte:

- `GET /api/v1/templates`
- `POST /api/v1/templates`
- `GET /api/v1/templates/:templateId`
- `PATCH /api/v1/templates/:templateId`
- `POST /api/v1/templates/:templateId/scenes`
- `PATCH /api/v1/templates/:templateId/scenes/:sceneId`
- `POST /api/v1/campaigns`
- `PATCH /api/v1/campaigns/:campaignId`
- `POST /api/v1/campaigns/:campaignId/activate`
- `POST /api/v1/campaigns/:campaignId/deactivate`
- `PUT /api/v1/campaigns/:campaignId/assignments`

### 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/campaign`
- `GET /api/v1/player/media-manifest`
- `GET /api/v1/player/media/:mediaId/download`
- `POST /api/v1/player/status`
- `POST /api/v1/player/snapshot`

## Aktuelle Entwicklungsstufe

Bevor das volle Player-Sync- und MQTT-Modell umgesetzt ist, existiert fuer die Entwicklung bereits ein bewusst kleiner HTTP-Statuspfad.

Aktuell gilt:

- `POST /api/v1/player/status` ist der erste reale Backend-Agent-Pfad
- die Payload enthaelt zunaechst Health- und Laufzeitdaten aus dem Agenten
- das Backend validiert den Request und bestaetigt ihn mit `status=accepted`
- Persistenz, Authentifizierung und die spaetere Trennung zwischen HTTP-Status und MQTT-Heartbeat folgen in spaeteren Schritten

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

## Provisionierungs-API

### `POST /api/v1/screens/provision`

Legt einen neuen Screen an und startet einen Provisionierungsjob.

Erwartete Eingaben mindestens:

- `screen_slug` oder `display_id`
- `screen_name`
- `tenant_id` optional, falls direkt zugeordnet werden soll
- `target_ip`
- `target_port` optional, Default 22
- `remote_user`, Default `root`
- `auth_mode`
- `password` oder `private_key_ref` fuer die Erstverbindung
- optionale technische Parameter wie `rotation`, `resolution`, `fallback_dir`

Serverseitiger Ablauf:

1. Screen-Datensatz anlegen
2. Provisionierungsjob anlegen
3. Secret nur kurzlebig oder referenziert speichern
4. Worker/Jobrunner startet Ansible- oder SSH-gestuetzten Rollout
5. nach Erfolg meldet sich der Screen kuenftig regulaer ueber seinen Agenten

Antwort mindestens:

- `screen_id`
- `provisioning_job_id`
- `status`

### `GET /api/v1/provisioning-jobs/:jobId`

Liefert:

- Jobstatus
- aktuelle Stage
- Zeitstempel
- letzte Meldungen
- Fehlerursache bei Fehlschlag

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

Liefert die fuer den Screen aktuell relevante globale Uebersteuerung.

Liefert:

- `campaign_revision`
- `active_campaign` nullable

Wenn `active_campaign` gesetzt ist, enthaelt es mindestens:

- `campaign_id`
- `template_id`
- `name`
- `priority`
- `valid_from`
- `valid_until`
- `override_mode`
- `scenes[]`

Jede Scene enthaelt mindestens:

- `id`
- `type`
- `src`
- `duration_seconds`
- `load_timeout_seconds`
- `cache_policy`
- `on_error`
- `layout_json`

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

Die eigentliche Erstprovisionierung laeuft nicht ueber MQTT, sondern ueber einen serverseitigen Job mit SSH/Ansible.

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

Beispielablauf bei Aktivierung einer globalen Kampagne:

1. Admin aktiviert eine Kampagne
2. Server erhoeht `campaign_revision`
3. Server sendet Event oder Command `reload`
4. Player ruft `GET /api/v1/player/campaign` ab
5. Player synchronisiert kampagnenbezogene Medien
6. Player setzt den Inhaltsmodus auf `campaign`
7. Nach Deaktivierung oder Ablauf faellt der Player wieder auf `tenant_playlist` oder `fallback` zurueck

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