"""Hardened adapter over the MIT-0 guooscar 1.2.0 device/upload methods. No UI, scheduler, renderer, environment lookup, or constructor cloud calls. Never retry a write; a timeout may have occurred after cloud acceptance. """ import threading import time from collections import deque from urllib.parse import quote import requests from pigway_frame_flow.errors import ErrorCode as E from pigway_frame_flow.errors import FlowError from vendor import guooscar_protocol BASE_URL = "https://api.kokonna.art/openapi" class SafeTransport: def __init__(self, key, session=None): self.key = key self.session = session or requests.Session() self.calls: deque[float] = deque() self.lock = threading.Lock() def request(self, endpoint, payload=None, write=False, binary=False): if not self.key: raise FlowError(E.CONFIG_REQUIRED, "KoKonna configuration required", 503) with self.lock: current = time.monotonic() while self.calls and current - self.calls[0] >= 60: self.calls.popleft() # Reserve capacity beneath the official 20/minute ceiling. if len(self.calls) >= 18: raise FlowError(E.RATE_LIMITED, "Device request budget exhausted", 429) self.calls.append(current) url = BASE_URL + endpoint try: response = self.session.request( "GET" if binary else "POST", url, headers={} if binary else { "Authorization": "Bearer " + self.key, "Content-Type": "application/json", }, json=None if binary else payload or {}, timeout=(5, 120 if endpoint == "/upload" else 30), allow_redirects=False, ) except requests.RequestException: raise FlowError( E.WRITE_RESULT_UNKNOWN if write else E.CLOUD_UNAVAILABLE, "Cloud write outcome is unknown" if write else "Device cloud is unavailable", 502, ) from None if response.status_code != 200: code = ( E.RATE_LIMITED if response.status_code == 429 else ( E.WRITE_RESULT_UNKNOWN if write and response.status_code >= 500 else E.CLOUD_UNAVAILABLE ) ) raise FlowError( code, "Device cloud request failed", 429 if code == E.RATE_LIMITED else 502, ) if binary: if len(response.content) > 20 * 1024 * 1024: raise FlowError(E.INVALID_REQUEST, "Cloud image exceeds size limit") return response.content try: value = response.json() if not isinstance(value, dict): raise ValueError() # Cloud errors may echo credentials: never return raw messages. if value.get("success") is False or ( "message" in value and "id" not in value ): raise ValueError() return value except ValueError: raise FlowError( E.WRITE_RESULT_UNKNOWN if write else E.CLOUD_UNAVAILABLE, "Invalid device cloud response", 502, ) from None class KokonnaClient: def __init__(self, key, write_enabled=False, session=None): self.transport = SafeTransport(key, session) self.write_enabled = write_enabled def guard(self): if not self.write_enabled: raise FlowError( E.WRITE_DISABLED, "Real device writes require explicit authorization", 403, ) def get_device(self): return guooscar_protocol.device(self.transport) def list_images(self): return self.transport.request("/listImages", {}) def upload(self, path, name): self.guard() value = guooscar_protocol.upload(self.transport, path.read_bytes(), name) if not isinstance(value.get("id"), int) or value["id"] <= 0: raise FlowError(E.WRITE_RESULT_UNKNOWN, "Upload ID not confirmed", 502) return value["id"] def display(self, image_id): self.guard() return self.transport.request( "/displayImageById", {"imageId": image_id}, write=True ) def delete(self, image_id): self.guard() return self.transport.request("/deleteImage", {"imageId": image_id}, write=True) def download(self, image_id): return self.transport.request( f"/image/{quote(self.transport.key, safe='')}/{image_id}", binary=True ) def display_by_name(self, name): self.guard() return self.transport.request( "/displayImageByName", {"imageName": name}, write=True )