Zum Hauptinhalt springen

Kundendisplay

Das Kundendisplay (de.profipos.kundendisplay, WPF / .NET 8, Fullscreen) spiegelt live den Warenkorb der gepaarten Kasse, zeigt einen großen Restbetrag während des Bezahlvorgangs und fährt im Leerlauf eine Werbe-Slideshow. Live-Build seit 13.06.2026: Jenkins #8 (Commit 6968655).

Modi (DisplayStateService)

ModusInhalt
WerbungSlideshow (Bild + Video) oder Idle-Standby („Willkommen“ / „Schönen Aufenthalt“)
WarenkorbLive-Positionsliste (Menge · Bezeichnung · Einzelpreis · Zwischensumme), Bon-Nr + Tisch, Summe + Rabatt
BezahlungNFC-Ellipse (Statusfarbe) + großer Restbetrag (Viewbox) + Hinweistext
PairingQR-Code (DeviceUuid) + Server-Info + Status

MQTT-Topic-Schema

Base: profipos/{mandant_uuid}/{standort_uuid}/anzeige/{kasse_uuid_or_device_uuid}/

SuffixDTOAktion
warenkorb_updateWarenkorbUpdateEventSpiegelt Positionsliste, schaltet auf Warenkorb-Modus
bezahlung_updateBezahlungUpdateEvent5 Status: warten / verarbeitung / erfolg / fehler / abgebrochen
session_closeSessionCloseEventZurück zu Werbung
werbung_setWerbungSetEventSetzt neue Slideshow-Medien
befehlBefehlEventCustom-Befehle (Reset, Refresh, …)
pair_eventPairEventPairing-Bestätigung mit MQTT-Creds + Kasse/Mandant/Standort

Vor Pairing abonniert der Client Wildcard profipos/+/+/anzeige/{deviceUuid}/+, nach Pairing wechselt er auf profipos/{mandant}/{standort}/anzeige/{kasse}/+.

Pairing-Flow

Siehe Pairing-Architektur. Kurzform:

  1. DeviceUuid aus %LOCALAPPDATA%\ProfiPOS\Kundendisplay\settings.json.
  2. UDP-Discovery (Magic PROFIPOS-DISCOVER-V1, Port 34567).
  3. Parallel: REST-Poll POST /api/v1/anzeige/pair (5 s) und MQTT-Sub pair_event. Wer zuerst kommt, gewinnt (TaskCompletionSource).
  4. SchreibePairingAsync → Settings → MqttClientService reconnect.

Services (Dependency-Injection)

  • Singleton: SettingsStore, ServerApiClient, ServerStatusService, MqttClientService, UdpDiscoveryService, PairingCoordinator, DisplayStateService, MediaCacheService, alle 5 VMs, MainWindow.
  • Hosted: ServerStatusService, MqttClientService, HeartbeatService.
  • HttpClientFactory: Named Clients server-api und media.

Heartbeat

HeartbeatService (BackgroundService, 30 s) → POST /api/v1/heartbeat/anzeige mit {device_uuid, modus, uptime_sek, version}. 404 wird leise toleriert (Endpoint optional).

MediaCache

  • Cache-Dir: %LOCALAPPDATA%\ProfiPOS\Kundendisplay\media
  • Filename = SHA256(Url) — Hash-Verify bei jedem Treffer
  • FIFO bei MaxCacheMb (Default 500 MB) — ältestes File löschen
  • Atomic write .tmp → move

Window-Spezifika

  • WindowStyle=None, ResizeMode=NoResize, WindowState=Maximized.
  • MonitorIndex aus Settings positioniert auf gewähltem Monitor.
  • ESC schließt App (Failsafe-Exit).
  • Cursor.None nur in Werbung + Pairing.
  • Statusleiste 38 px am unteren Rand: Server + MQTT + Modus + AppVersion.

Settings

// %LOCALAPPDATA%\ProfiPOS\Kundendisplay\settings.json
{
"Mqtt": { "Host": "10.1.1.20", "Port": 1883, "User": "...", "Pass": "...", "KeepAlive": 30 },
"ServerApi": { "BaseUrl": "http://10.1.1.20:8080", "TimeoutSeconds": 15 },
"Anzeige": { "MonitorIndex": 1, "IdleSekunden": 30 },
"Werbung": { "MaxCacheMb": 500, "SlideSekunden": 8 },
"Discovery": { "Port": 34567, "MagicString": "PROFIPOS-DISCOVER-V1" },
"DeviceUuid": "...",
"KasseUuid": "...",
"MandantUuid": "...",
"StandortUuid": "..."
}

IstGepairt = KasseUuid + MandantUuid + StandortUuid sind alle nicht leer.

Verwandte Themen