"""Stable MQTT contracts shared by the Controller adapters.""" from __future__ import annotations from dataclasses import dataclass SLOT_IDS = tuple(range(1, 11)) @dataclass(frozen=True) class Topics: """MQTT topic names. Defaults preserve the established Player, Home Assistant, and Companion integrations. Player ``state`` is fixed by the Player Node contract. """ player_command: str = "youtube/player/cmd" player_status: str = "youtube/player/status" player_state: str = "youtube/player/state" player_telemetry: str = "youtube/player/telemetry" controller_command: str = "youtube/controller/cmd" ingest_progress: str = "youtube/ingest/progress" playback_base: str = "youtube/media/playback" router_mode_set: str = "audio/player/mode/set" router_mode_state: str = "audio/player/mode/state" controller_result: str | None = None def slot_base(self, slot: int) -> str: if slot not in SLOT_IDS: raise ValueError(f"slot must be in 1..10, got {slot!r}") return f"{self.playback_base}/{slot}" PLAYER_COMMAND_QOS = 1 PLAYER_COMMAND_RETAIN = False PLAYER_STATUS_QOS = 1 PLAYER_STATE_QOS = 0 PLAYER_TELEMETRY_QOS = 0 # Existing Controller-owned HA/Companion state was retained at QoS 0. LEGACY_STATE_QOS = 0 LEGACY_STATE_RETAIN = True # Router commands are ephemeral commands just like Player commands. ROUTER_COMMAND_QOS = 1 ROUTER_COMMAND_RETAIN = False