From 3086aa8a20ad8ff26e127549c23cd19f9bea99e3 Mon Sep 17 00:00:00 2001 From: "Steven B." <51370195+sdb9696@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:21:12 +0000 Subject: [PATCH] Use component queries to select smartcamera modules (#1248) --- kasa/experimental/modules/childdevice.py | 1 + kasa/experimental/smartcamera.py | 52 ++++++++++++++++-------- kasa/experimental/smartcameramodule.py | 4 +- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/kasa/experimental/modules/childdevice.py b/kasa/experimental/modules/childdevice.py index 0168011d..81905fbf 100644 --- a/kasa/experimental/modules/childdevice.py +++ b/kasa/experimental/modules/childdevice.py @@ -7,6 +7,7 @@ from ..smartcameramodule import SmartCameraModule class ChildDevice(SmartCameraModule): """Implementation for child devices.""" + REQUIRED_COMPONENT = "childControl" NAME = "childdevice" QUERY_GETTER_NAME = "getChildDeviceList" # This module is unusual in that QUERY_MODULE_NAME in the response is not diff --git a/kasa/experimental/smartcamera.py b/kasa/experimental/smartcamera.py index 059bac8e..feadf87b 100644 --- a/kasa/experimental/smartcamera.py +++ b/kasa/experimental/smartcamera.py @@ -43,18 +43,16 @@ class SmartCamera(SmartDevice): for info in child_info["child_device_list"]: self._children[info["device_id"]]._update_internal_state(info) - async def _initialize_smart_child(self, info: dict) -> SmartDevice: + async def _initialize_smart_child( + self, info: dict, child_components: dict + ) -> SmartDevice: """Initialize a smart child device attached to a smartcamera.""" child_id = info["device_id"] child_protocol = _ChildCameraProtocolWrapper(child_id, self.protocol) try: initial_response = await child_protocol.query( - {"component_nego": None, "get_connect_cloud_state": None} + {"get_connect_cloud_state": None} ) - child_components = { - item["id"]: item["ver_code"] - for item in initial_response["component_nego"]["component_list"] - } except Exception as ex: _LOGGER.exception("Error initialising child %s: %s", child_id, ex) @@ -68,20 +66,28 @@ class SmartCamera(SmartDevice): async def _initialize_children(self) -> None: """Initialize children for hubs.""" - if not ( - child_info := self._try_get_response( - self._last_update, "getChildDeviceList", {} - ) - ): - return + child_info_query = { + "getChildDeviceList": {"childControl": {"start_index": 0}}, + "getChildDeviceComponentList": {"childControl": {"start_index": 0}}, + } + resp = await self.protocol.query(child_info_query) + self.internal_state.update(resp) + children_components = { + child["device_id"]: { + comp["id"]: int(comp["ver_code"]) for comp in child["component_list"] + } + for child in resp["getChildDeviceComponentList"]["child_component_list"] + } children = {} - for info in child_info["child_device_list"]: + for info in resp["getChildDeviceList"]["child_device_list"]: if ( category := info.get("category") ) and category in SmartChildDevice.CHILD_DEVICE_TYPE_MAP: child_id = info["device_id"] - children[child_id] = await self._initialize_smart_child(info) + children[child_id] = await self._initialize_smart_child( + info, children_components[child_id] + ) else: _LOGGER.debug("Child device type not supported: %s", info) @@ -90,6 +96,11 @@ class SmartCamera(SmartDevice): async def _initialize_modules(self) -> None: """Initialize modules based on component negotiation response.""" for mod in SmartCameraModule.REGISTERED_MODULES.values(): + if ( + mod.REQUIRED_COMPONENT + and mod.REQUIRED_COMPONENT not in self._components + ): + continue module = mod(self, mod._module_name()) if await module._check_supported(): self._modules[module.name] = module @@ -126,12 +137,21 @@ class SmartCamera(SmartDevice): """ initial_query = { "getDeviceInfo": {"device_info": {"name": ["basic_info", "info"]}}, - "getChildDeviceList": {"childControl": {"start_index": 0}}, + "getAppComponentList": {"app_component": {"name": "app_component_list"}}, } resp = await self.protocol.query(initial_query) self._last_update.update(resp) self._update_internal_info(resp) - await self._initialize_children() + + self._components = { + comp["name"]: int(comp["version"]) + for comp in resp["getAppComponentList"]["app_component"][ + "app_component_list" + ] + } + + if "childControl" in self._components and not self.children: + await self._initialize_children() def _map_info(self, device_info: dict) -> dict: basic_info = device_info["basic_info"] diff --git a/kasa/experimental/smartcameramodule.py b/kasa/experimental/smartcameramodule.py index bfb42fc0..217b69e7 100644 --- a/kasa/experimental/smartcameramodule.py +++ b/kasa/experimental/smartcameramodule.py @@ -74,7 +74,7 @@ class SmartCameraModule(SmartModule): if isinstance(query_resp, SmartErrorCode): raise DeviceError( f"Error accessing module data in {self._module}", - error_code=SmartErrorCode, + error_code=query_resp, ) if not query_resp: @@ -95,6 +95,6 @@ class SmartCameraModule(SmartModule): if isinstance(found[key], SmartErrorCode): raise DeviceError( f"Error accessing module data {key} in {self._module}", - error_code=SmartErrorCode, + error_code=found[key], ) return found