"""Read-only official widget RPC; separate from the device OpenAPI credential.""" import json import time from pathlib import Path from pigway_frame_flow.errors import ErrorCode as E from pigway_frame_flow.errors import FlowError from pigway_frame_flow.integrations.kokonna_official.rpc import OfficialRPC from pigway_frame_flow.models import Widget, WidgetState class WidgetReader: def __init__(self, token="", robot_id=None, mock=False): self.rpc = OfficialRPC(token) self._robot_id = robot_id self.mock = mock self.mock_selection = 188 self._cached = None self._cached_at = 0.0 def read(self) -> WidgetState: if ( self.rpc.authorized and self._cached is not None and time.monotonic() - self._cached_at < 60 ): return self._cached snapshot = json.loads( Path(__file__).with_name("widget_catalog.json").read_text() ) catalog = [ Widget(**{k: row[k] for k in ("id", "name", "tags", "arg_keys")}) for row in snapshot["catalog"] ] if self.mock: return WidgetState( source="mock", configured=True, catalog=catalog, current=next((x for x in catalog if x.id == self.mock_selection), None), status="OK" if self.mock_selection else "NO_WIDGET", current_verified=True, ) if not self.rpc.authorized or self._robot_id is None: return WidgetState( source="snapshot", configured=False, catalog=catalog, observed_at=snapshot["observed_at"], status="DEVICE_REQUIRED" if self.rpc.authorized else "AUTH_REQUIRED", ) try: data = self.rpc.widgets(self._robot_id) rows = data["rows"] catalog = [ Widget( id=row["id"], name=row["name"], tags=row.get("tags") or [], arg_keys=list((row.get("args") or {}).keys()) if isinstance(row.get("args"), dict) else [], ) for row in rows ] selected = data.get("frameCoverRobot") or {} cover = selected.get("frameCover") or {} current_id = cover.get("id", selected.get("coverId")) current = next((row for row in catalog if row.id == current_id), None) robot = data.get("robot") or {} no_widget = ( "frameCoverRobot" in data and data["frameCoverRobot"] is None and "frameCoverRobotId" in robot and robot["frameCoverRobotId"] is None ) result = WidgetState( source="live", configured=True, catalog=catalog, current=current, current_verified=current is not None or no_widget, status="OK" if current else "NO_WIDGET" if no_widget else "CURRENT_UNVERIFIED", ) except FlowError: raise except ( ValueError, KeyError, TypeError, AttributeError, ): raise FlowError( E.CLOUD_UNAVAILABLE, "Official widget query unavailable", 502 ) from None self._cached, self._cached_at = result, time.monotonic() return result