feat(mqtt): finalize dashboard screenshot payload v2 and trigger flow

- switch dashboard payload to grouped schema v2.0 in simclient
- support immediate event-triggered screenshot sends via meta.json signaling
- update README and copilot instructions to document v2 payload and trigger behavior
- update migration checklist to reflect completed client/server rollout
This commit is contained in:
RobbStarkAustria
2026-03-30 17:53:58 +02:00
parent 77db2bc565
commit 25cf4e3322
4 changed files with 85 additions and 70 deletions

View File

@@ -12,6 +12,9 @@
-**Keep screenshot consent notice in docs** when describing dashboard screenshot feature
-**Event-start/event-stop screenshots must preserve metadata** - See SCREENSHOT_MQTT_FIX.md for critical race condition that was fixed
-**Screenshot updates must keep `latest.jpg` and `meta.json` in sync** (simclient prefers `latest.jpg`)
-**Dashboard payload uses grouped v2 schema** (`message/content/runtime/metadata`, `schema_version="2.0"`)
-**Event-triggered screenshots**: `display_manager` arms a `threading.Timer` after start/stop, captures, writes `meta.json` with `send_immediately=true`; simclient fires within ≤1s
-**Payload assembly is centralized** in `_build_dashboard_payload()` — do not build dashboard JSON at call sites
### Key Files & Locations
- **Display logic**: `src/display_manager.py` (controls presentations/video/web)
@@ -488,31 +491,49 @@ The screenshot capture and transmission system has been implemented with separat
- **Rotation**: Keeps max N files (default 20), deletes older
- **Timing**: Production captures when display process is active (unless `SCREENSHOT_ALWAYS=1`); development allows periodic idle captures to keep dashboard fresh
- **Reliability**: Stale/invalid pending trigger metadata is ignored automatically to avoid lock-up of periodic updates
- **Event-triggered captures**: `_trigger_event_screenshot(type, delay)` arms a one-shot `threading.Timer` after event start/stop; timer is cancelled and replaced on rapid event switches; default delays: presentation=4s, video=2s, web=5s (env-configurable)
- **IPC signal file** (`screenshots/meta.json`): written atomically by `display_manager` after each capture; contains `type`, `captured_at`, `file`, `send_immediately`; `send_immediately=true` for event-triggered, `false` for periodic
### Transmission Strategy (simclient.py)
- **Source**: Prefers `screenshots/latest.jpg` if present, falls back to newest timestamped file
- **Topic**: `infoscreen/{client_id}/dashboard`
- **Format**: JSON with base64-encoded image data
- **Payload Structure**:
- **Format**: JSON with base64-encoded image data, grouped v2 schema
- **Schema version**: `"2.0"` (legacy flat fields removed; all fields grouped)
- **Payload builder**: `_build_dashboard_payload()` in `simclient.py` — single source of truth
- **Payload Structure** (v2):
```json
{
"timestamp": "ISO datetime",
"client_id": "UUID",
"status": "alive",
"screenshot": {
"filename": "latest.jpg",
"data": "base64...",
"timestamp": "ISO datetime",
"size": 12345
"message": { "client_id": "UUID", "status": "alive" },
"content": {
"screenshot": {
"filename": "latest.jpg",
"data": "base64...",
"timestamp": "ISO datetime",
"size": 12345
}
},
"system_info": {
"hostname": "...",
"ip": "...",
"uptime": 123456.78
"runtime": {
"system_info": { "hostname": "...", "ip": "...", "uptime": 123456.78 },
"process_health": { "event_type": "...", "process_status": "...", ... }
},
"metadata": {
"schema_version": "2.0",
"producer": "simclient",
"published_at": "ISO datetime",
"capture": {
"type": "periodic | event_start | event_stop",
"captured_at": "ISO datetime",
"age_s": 0.9,
"triggered": false,
"send_immediately": false
},
"transport": { "topic": "infoscreen/.../dashboard", "qos": 0, "publisher": "simclient" }
}
}
```
- **Logging**: Logs publish success/failure with file size for monitoring
- **Capture types**: `periodic` (interval-based), `event_start` (N seconds after event launch), `event_stop` (1s after process killed)
- **Triggered send**: `display_manager` sets `send_immediately=true` in `meta.json`; simclient 1-second tick detects and fires within ≤1s
- **Logging**: `Dashboard published: schema=2.0 type=<type> screenshot=<file> (<bytes>) age=<s>`
### Scalability Considerations
- **Client-side resize/compress**: Reduces bandwidth and broker load (recommended for 50+ clients)