Files
pigway-frame-flow/pigway_frame_flow/integrations/kokonna_official/widgets.py
T

103 lines
3.6 KiB
Python
Raw Normal View History

"""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