shrink root README into a landing page with a docs map and focused contributor guidance add TV_POWER_RUNBOOK as the canonical TV power rollout and canary runbook add CHANGELOG and move project history out of README-style docs refactor src README into a developer-focused guide (architecture, runtime files, MQTT, debugging) prune redundant older HDMI docs and keep a canonical HDMI_CEC_SETUP path update copilot instructions to a high-signal policy format with strict anti-shadow-README design rules align references across docs to current files, scripts, and TV power behavior
84 lines
2.7 KiB
Markdown
84 lines
2.7 KiB
Markdown
# Server Handoff: TV Power Coordination
|
|
|
|
## Purpose
|
|
Implement server-side MQTT power intent publishing so clients can keep TVs on across adjacent events and power off safely after schedules end.
|
|
|
|
## Source of Truth
|
|
- Shared full plan: TV_POWER_COORDINATION_TASKLIST.md
|
|
|
|
## Scope (Server Team)
|
|
- Scheduler-to-intent mapping
|
|
- MQTT publishing semantics (retain, QoS, expiry)
|
|
- Conflict handling (group vs client)
|
|
- Observability for intent lifecycle
|
|
|
|
## MQTT Contract (Server Responsibilities)
|
|
|
|
### Topics
|
|
- Primary (per-client): infoscreen/{client_id}/power/intent
|
|
- Optional (group-level): infoscreen/groups/{group_id}/power/intent
|
|
|
|
### Delivery Semantics
|
|
- QoS: 1
|
|
- retained: true
|
|
- Always publish UTC timestamps (ISO 8601 with Z)
|
|
|
|
### Intent Payload (v1)
|
|
```json
|
|
{
|
|
"schema_version": "1.0",
|
|
"intent_id": "uuid-or-monotonic-id",
|
|
"issued_at": "2026-03-31T12:00:00Z",
|
|
"expires_at": "2026-03-31T12:10:00Z",
|
|
"target": {
|
|
"client_id": "optional-if-group-topic",
|
|
"group_id": "optional"
|
|
},
|
|
"power": {
|
|
"desired_state": "on",
|
|
"reason": "event_window_active",
|
|
"grace_seconds": 30
|
|
},
|
|
"event_window": {
|
|
"start": "2026-03-31T12:00:00Z",
|
|
"end": "2026-03-31T13:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Required Behavior
|
|
|
|
### Adjacent/Overlapping Events
|
|
- Never publish an intermediate off intent when windows are contiguous/overlapping.
|
|
- Maintain continuous desired_state=on coverage across adjacent windows.
|
|
|
|
### Reconnect/Restart
|
|
- On scheduler restart, republish effective retained intent.
|
|
- On event edits/cancellations, replace retained intent with a fresh intent_id.
|
|
|
|
### Conflict Policy
|
|
- If both group and client intent exist: per-client overrides group.
|
|
|
|
### Expiry Safety
|
|
- expires_at must be set for every intent.
|
|
- Server should avoid publishing already-expired intents.
|
|
|
|
## Implementation Tasks
|
|
1. Add scheduler mapping layer that computes effective desired_state per client timeline.
|
|
2. Add intent publisher with retained QoS1 delivery.
|
|
3. Generate unique intent_id for each semantic transition.
|
|
4. Emit issued_at/expires_at and event_window consistently in UTC.
|
|
5. Add group-vs-client precedence logic.
|
|
6. Add logs/metrics for publish success, retained payload age, and transition count.
|
|
7. Add integration tests for adjacent events and reconnect replay.
|
|
|
|
## Acceptance Criteria
|
|
1. Adjacent events do not create OFF gap intents.
|
|
2. Fresh client receives retained intent after reconnect and gets correct desired state.
|
|
3. Intent payloads are schema-valid, UTC-formatted, and include expiry.
|
|
4. Publish logs and metrics allow intent timeline reconstruction.
|
|
|
|
## Operational Notes
|
|
- Keep intent publishing idempotent and deterministic.
|
|
- Preserve backward compatibility while clients run in hybrid mode.
|