""" Agate Client - Main interface for AGATE device operations. """ from __future__ import annotations from typing import Any, Optional from .bluetooth import BluetoothManager from .wifi import HotspotProfile, WifiBackend, WifiManager class AgateClient: """Main client for FranklinWH AGATE device operations.""" def __init__(self, device_id: Optional[str] = None, wifi_backend: Optional[WifiBackend] = None) -> None: self.device_id = device_id self.bluetooth_manager: Optional[BluetoothManager] = None self.wifi_manager = WifiManager(backend=wifi_backend) # -- device hotspot (local connect) ------------------------------------- def join_hotspot(self, sn: str, **kwargs: Any) -> HotspotProfile: """Join the device SoftAP (derives creds, joins, verifies).""" return self.wifi_manager.join_device_hotspot(sn, **kwargs) def leave_hotspot(self) -> None: """Disconnect from the device hotspot.""" self.wifi_manager.leave_device_hotspot() def attach_local(self, sn: str, *, verify_join: bool = True, settle_timeout: float = 30.0, poll_interval: float = 1.0, **local_kwargs: Any): """Join the device hotspot, then return a ``LocalDeviceClient`` aimed at the device gateway (``https://10.100.1.1:18000`` by default). ``verify_join`` gates the association check (TLS verification of the device API is a separate concern — pass ``verify=`` in ``local_kwargs``). Raises :class:`~franklinwh_agate_client.wifi.HotspotJoinError` if the association never verifies. (``LocalDeviceClient`` imports ``requests``; install it before calling this on a cloud-less host.) Extra keyword arguments are passed through to ``LocalDeviceClient`` (``port``, ``scheme``, ``verify``, ``socket_port``, ``key_radix``, ``timeout``, ``extra_headers``, ``session``). """ profile = self.wifi_manager.join_device_hotspot( sn, verify=verify_join, settle_timeout=settle_timeout, poll_interval=poll_interval, ) from .device_local import LocalDeviceClient return LocalDeviceClient(sn=sn, host=profile.gateway_ip, **local_kwargs) # -- cloud / stub operations (pending RE) ------------------------------- def query_agate_info(self, device_id: Optional[str] = None): """Query AGATE device information.""" # Reverse engineered operation: queryAgateInfo pass def query_device_info_http(self, device_id: Optional[str] = None): """Query device info via HTTP.""" # Reverse engineered operation: queryDeviceInfoHttp pass def query_modes_by_http(self, device_id: Optional[str] = None): """Query operating modes via HTTP.""" # Reverse engineered operation: queryModesByHttp pass