"""Daemon-managed device pairing for the face-tracking and music application.""" from __future__ import annotations import json import os import urllib.error import urllib.request from collections.abc import Mapping class DaemonDeviceManager: """Ask the Runtime to pair and report status; never connect to the device directly.""" def __init__(self, status_url: str, *, timeout: float = 0.8) -> None: self._status_url = status_url.rstrip("/") self._timeout = timeout @classmethod def from_environment(cls) -> "DaemonDeviceManager": status_url = os.environ.get("WATCHER_APP_DEVICE_STATUS_URL", "").strip() if not status_url: raise RuntimeError("Daemon device status URL is unavailable") return cls(status_url) def status(self) -> Mapping[str, object]: return self._device_from_response(self._request(self._status_url, method="GET")) def pair(self, pairing_code: str) -> Mapping[str, object]: if len(pairing_code) != 6 or not pairing_code.isdigit(): raise RuntimeError("Pairing code must contain six digits") return self._device_from_response( self._request( f"{self._status_url}/pair", method="POST", body={"pairing_code": pairing_code, "target_mode": "python_sdk"}, ) ) @staticmethod def _device_from_response(payload: Mapping[str, object]) -> Mapping[str, object]: device = payload.get("device") if not isinstance(device, dict): raise RuntimeError("Daemon returned an invalid device response") return device def _request( self, url: str, *, method: str, body: Mapping[str, object] | None = None, ) -> Mapping[str, object]: data = json.dumps(body).encode("utf-8") if body is not None else None request = urllib.request.Request( url, data=data, headers={"Content-Type": "application/json"} if data else {}, method=method, ) try: with urllib.request.urlopen(request, timeout=self._timeout) as response: payload = json.loads(response.read().decode("utf-8")) except (urllib.error.URLError, urllib.error.HTTPError, UnicodeError, json.JSONDecodeError) as error: raise RuntimeError("Daemon device request failed") from error if not isinstance(payload, dict): raise RuntimeError("Daemon response is invalid") return payload