# Server Handoff: TV Power Coordination ## Status Server PR-1 is implemented and merged (Phase 1). ## Source of Truth - Contract: TV_POWER_INTENT_SERVER_CONTRACT_V1.md - Implementation: scheduler/scheduler.py and scheduler/db_utils.py - Validation checklist: TV_POWER_PHASE_1_CANARY_VALIDATION.md ## Active Phase 1 Scope - Topic: infoscreen/groups/{group_id}/power/intent - QoS: 1 - Retained: true - Scope: group-level only - Per-client intent/state topics: deferred to Phase 2 ## Publish Semantics (Implemented) - Semantic transition (`desired_state` or `reason` changed): new `intent_id` and immediate publish - Heartbeat (no semantic change): same `intent_id`, refreshed `issued_at` and `expires_at` - Scheduler startup: immediate publish before first poll wait - MQTT reconnect: immediate retained republish of cached intents ## Payload Contract (Phase 1) ```json { "schema_version": "1.0", "intent_id": "uuid4", "group_id": 12, "desired_state": "on", "reason": "active_event", "issued_at": "2026-04-01T06:00:03.496Z", "expires_at": "2026-04-01T06:01:33.496Z", "poll_interval_sec": 15, "active_event_ids": [148], "event_window_start": "2026-04-01T06:00:00Z", "event_window_end": "2026-04-01T07:00:00Z" } ``` Expiry rule: - expires_at = issued_at + max(3 x poll_interval_sec, 90 seconds) ## Operational Notes - Adjacent/overlapping events are merged into one active coverage window; no OFF blip at boundaries. - Feature flag defaults are safe for rollout: - POWER_INTENT_PUBLISH_ENABLED=false - POWER_INTENT_HEARTBEAT_ENABLED=true - POWER_INTENT_EXPIRY_MULTIPLIER=3 - POWER_INTENT_MIN_EXPIRY_SECONDS=90 - Keep this handoff concise and defer full details to the stable contract document. ## Phase 2 (Deferred) - Per-client override topic: infoscreen/{client_uuid}/power/intent - Client power state topic and acknowledgments - Listener persistence of client-level power state