"""Hardware detection and heartbeat payload construction for the streamer.""" from __future__ import annotations from ria_toolkit_oss.sdr import detect_available from .config import AgentConfig def available_devices() -> list[str]: """Return a sorted list of device names whose driver modules import cleanly.""" return sorted(detect_available().keys()) def heartbeat_payload( status: str = "idle", app_id: str | None = None, *, cfg: AgentConfig | None = None, sessions: dict | None = None, ) -> dict: """Build the JSON body of a periodic heartbeat frame. *cfg* drives the ``capabilities`` list and the ``tx_enabled`` flag. If not supplied, the heartbeat advertises RX-only with ``tx_enabled=False`` — matching the pre-TX shape. """ c = cfg or AgentConfig() capabilities = ["rx"] if c.tx_enabled: capabilities.append("tx") payload: dict = { "type": "heartbeat", "hardware": available_devices(), "status": status, "capabilities": capabilities, "tx_enabled": bool(c.tx_enabled), } # Surface configured interlock values so the hub can pre-filter UI controls # before sending a tx_start that would be rejected. Only included when TX # is opted in AND the operator set a cap. if c.tx_enabled: if c.tx_max_gain_db is not None: payload["tx_max_gain_db"] = float(c.tx_max_gain_db) if c.tx_max_duration_s is not None: payload["tx_max_duration_s"] = float(c.tx_max_duration_s) if c.tx_allowed_freq_ranges: payload["tx_allowed_freq_ranges"] = [ [float(lo), float(hi)] for lo, hi in c.tx_allowed_freq_ranges ] if app_id: payload["app_id"] = app_id if sessions: payload["sessions"] = sessions return payload