morz-infoboard/docs/API-MQTT-VERTRAG.md
Jesko Anschütz 585cb83ed0 MQTT-Playlist-Push: Änderungen erreichen Client binnen 5 Sekunden
Backend published auf signage/screen/{slug}/playlist-changed nach
Playlist-Mutationen (2s Debounce). Agent subscribed und fetcht
Playlist sofort (3s Debounce). 60s-Polling bleibt als Fallback.

Neue Packages: mqttnotifier (Backend), mqttsubscriber (Agent)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:35:50 +01:00

136 lines
3.7 KiB
Markdown

# Info-Board Neu - MQTT-Vertrag
Vertrag zwischen Backend und Agent für die Echtzeit-Synchronisation von Playlist-Änderungen und Gerätebefehlen.
---
## Überblick
Das Messaging-System nutzt MQTT für:
- **Playlist-Mutations-Benachrichtigungen**: Backend → Agent
- **Device-Commands**: Backend → Agent (zukünftig: reload, restart_player, reboot, display_on/off)
- **Heartbeat & Status**: Agent → Backend (siehe `PLAYER-STATUS-HTTP.md`)
Alle Topics folgen dem Naming-Pattern: `signage/{component}/{screenSlug}/{event}`
---
## Topics
### Backend publishes
#### `signage/screen/{screenSlug}/playlist-changed`
**Publisher:** Backend
**Subscriber:** Agent
Wird nach jeder Mutation der Playlist gepublished (Add, Remove, Reorder, Enable/Disable Item).
**Payload:**
```json
{
"ts": 1711268440000
}
```
- `ts`: Unix-Zeitstempel in Millisekunden des Änderungsereignisses auf dem Backend
**Verhalten:**
- Backend debounced Änderungen über **2 Sekunden**
- Mehrere schnelle Mutationen werden zu einem Event zusammengefasst
- Garantiert mindestens ein Event pro logischer Änderung
**Agent-Reaktion:**
- Agent empfängt das Event
- Agent debounced die Verarbeitung über **3 Sekunden**
- Agent startet sofortiges Playlist-Fetch via HTTP `GET /api/v1/screens/{screenSlug}/playlist`
- Agent speichert die Playlist lokal und signalisiert dem Browser einen Reload
**Implementierung (Agent):**
```go
// Pseudocode
func OnPlaylistChanged(msg PlaylistChangedMessage) {
if debounceTimer.running {
debounceTimer.reset()
} else {
debounceTimer.start(3 * time.Second)
}
}
func onDebounceExpire() {
playlist := fetchPlaylistViaHTTP()
saveToLocalCache(playlist)
signalBrowserReload()
}
```
---
## Zukünftige Topics
Die folgenden Topics sind **geplant** für Phase 5 (Prototyping) und später:
### `signage/screen/{screenSlug}/device-command`
**Publisher:** Backend
**Subscriber:** Agent
Befehl-Queue für Device-Steuerung.
**Payload:**
```json
{
"cmd_id": "uuid",
"command": "reload|restart_player|reboot|display_on|display_off",
"ts": 1711268440000
}
```
**Agent-Reaktion:**
- Befehl ausführen
- ACK via HTTP POST zu `PUT /api/v1/screens/{screenSlug}/command-ack`
---
## Beispiel-Flow: Playlist-Update
```
Admin: Click "Speichern" in Playlist-UI
Backend: Playlist-Mutation in DB schreiben
Backend: `playlist-changed` mit ts=now nach 2s Debounce publifyen
Agent: Event empfangen, 3s Debounce starten
Agent: Nach 3s → HTTP GET /api/v1/screens/{slug}/playlist
Backend: Aktuelle Playlist zurückgeben
Agent: Lokal speichern, Browser signalisieren "reload"
Browser: Neuer Content geladen und abgespielt
```
---
## MQTT-Verbindungspezifikation
(Siehe `PLAYER-KONZEPT.md` und Provisioning-Variablen für Broker-URL, Authentifizierung und Retry-Logik)
- **Broker-Adresse:** Über Provisioning konfigurierbar (Standard: `tcp://backend:1883`)
- **Client-ID:** `{tenantSlug}/{screenSlug}` (eindeutig pro Screen)
- **Username/Password:** Device-spezifische Credentials (OAuth-ähnlich)
- **QoS-Level:** 1 (At-Least-Once für Critical-Events)
- **Retain:** nein (Event-Natur, nicht State)
- **Heartbeat:** Separat via HTTP (siehe `PLAYER-STATUS-HTTP.md`)
---
## Notizen für Implementierer
1. **Replay bei Reconnect:** Topics haben `retain: false`, daher entfallen keine Events bei Trennung. Der Agent synchronisiert sich nach Reconnect via regulärem Status-Endpoint.
2. **Ordering:** Mehrere Events zu einem Screen sind ordered; Ordering über Screen-Grenzen hinweg ist nicht garantiert.
3. **Fehlerbehandlung:** Fehlgeschlagene Playlisten-Fetches werden vom Agent nach Standard-Retry-Logik wiederholt.
4. **Version der Spec:** v1.0 (März 2026)