mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 11:13:34 +00:00
Pass raw components to SmartChildDevice init (#1363)
Clean up and consolidate the processing of raw component query responses and simplify the code paths for creating smartcam child devices when supported.
This commit is contained in:
parent
8cb5c2e180
commit
f8a46f74cd
@ -9,7 +9,7 @@ from typing import Any
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..protocols.smartprotocol import SmartProtocol, _ChildProtocolWrapper
|
||||
from .smartdevice import SmartDevice
|
||||
from .smartdevice import ComponentsRaw, SmartDevice
|
||||
from .smartmodule import SmartModule
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -37,7 +37,7 @@ class SmartChildDevice(SmartDevice):
|
||||
self,
|
||||
parent: SmartDevice,
|
||||
info: dict,
|
||||
component_info: dict,
|
||||
component_info_raw: ComponentsRaw,
|
||||
*,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: SmartProtocol | None = None,
|
||||
@ -47,7 +47,8 @@ class SmartChildDevice(SmartDevice):
|
||||
super().__init__(parent.host, config=parent.config, protocol=_protocol)
|
||||
self._parent = parent
|
||||
self._update_internal_state(info)
|
||||
self._components = component_info
|
||||
self._components_raw = component_info_raw
|
||||
self._components = self._parse_components(self._components_raw)
|
||||
|
||||
async def update(self, update_children: bool = True) -> None:
|
||||
"""Update child module info.
|
||||
@ -84,7 +85,7 @@ class SmartChildDevice(SmartDevice):
|
||||
cls,
|
||||
parent: SmartDevice,
|
||||
child_info: dict,
|
||||
child_components: dict,
|
||||
child_components_raw: ComponentsRaw,
|
||||
protocol: SmartProtocol | None = None,
|
||||
*,
|
||||
last_update: dict | None = None,
|
||||
@ -97,7 +98,7 @@ class SmartChildDevice(SmartDevice):
|
||||
derived from the parent.
|
||||
"""
|
||||
child: SmartChildDevice = cls(
|
||||
parent, child_info, child_components, protocol=protocol
|
||||
parent, child_info, child_components_raw, protocol=protocol
|
||||
)
|
||||
if last_update:
|
||||
child._last_update = last_update
|
||||
|
@ -7,7 +7,7 @@ import logging
|
||||
import time
|
||||
from collections.abc import Mapping, Sequence
|
||||
from datetime import UTC, datetime, timedelta, tzinfo
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from typing import TYPE_CHECKING, Any, TypeAlias, cast
|
||||
|
||||
from ..device import Device, WifiNetwork, _DeviceInfo
|
||||
from ..device_type import DeviceType
|
||||
@ -40,6 +40,8 @@ _LOGGER = logging.getLogger(__name__)
|
||||
# same issue, homekit perhaps?
|
||||
NON_HUB_PARENT_ONLY_MODULES = [DeviceModule, Time, Firmware, Cloud]
|
||||
|
||||
ComponentsRaw: TypeAlias = dict[str, list[dict[str, int | str]]]
|
||||
|
||||
|
||||
# Device must go last as the other interfaces also inherit Device
|
||||
# and python needs a consistent method resolution order.
|
||||
@ -61,7 +63,7 @@ class SmartDevice(Device):
|
||||
)
|
||||
super().__init__(host=host, config=config, protocol=_protocol)
|
||||
self.protocol: SmartProtocol
|
||||
self._components_raw: dict[str, Any] | None = None
|
||||
self._components_raw: ComponentsRaw | None = None
|
||||
self._components: dict[str, int] = {}
|
||||
self._state_information: dict[str, Any] = {}
|
||||
self._modules: dict[str | ModuleName[Module], SmartModule] = {}
|
||||
@ -82,10 +84,8 @@ class SmartDevice(Device):
|
||||
self.internal_state.update(resp)
|
||||
|
||||
children = self.internal_state["get_child_device_list"]["child_device_list"]
|
||||
children_components = {
|
||||
child["device_id"]: {
|
||||
comp["id"]: int(comp["ver_code"]) for comp in child["component_list"]
|
||||
}
|
||||
children_components_raw = {
|
||||
child["device_id"]: child
|
||||
for child in self.internal_state["get_child_device_component_list"][
|
||||
"child_component_list"
|
||||
]
|
||||
@ -96,7 +96,7 @@ class SmartDevice(Device):
|
||||
child_info["device_id"]: await SmartChildDevice.create(
|
||||
parent=self,
|
||||
child_info=child_info,
|
||||
child_components=children_components[child_info["device_id"]],
|
||||
child_components_raw=children_components_raw[child_info["device_id"]],
|
||||
)
|
||||
for child_info in children
|
||||
}
|
||||
@ -131,6 +131,13 @@ class SmartDevice(Device):
|
||||
f"{request} not found in {responses} for device {self.host}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_components(components_raw: ComponentsRaw) -> dict[str, int]:
|
||||
return {
|
||||
str(comp["id"]): int(comp["ver_code"])
|
||||
for comp in components_raw["component_list"]
|
||||
}
|
||||
|
||||
async def _negotiate(self) -> None:
|
||||
"""Perform initialization.
|
||||
|
||||
@ -151,12 +158,9 @@ class SmartDevice(Device):
|
||||
self._info = self._try_get_response(resp, "get_device_info")
|
||||
|
||||
# Create our internal presentation of available components
|
||||
self._components_raw = cast(dict, resp["component_nego"])
|
||||
self._components_raw = cast(ComponentsRaw, resp["component_nego"])
|
||||
|
||||
self._components = {
|
||||
comp["id"]: int(comp["ver_code"])
|
||||
for comp in self._components_raw["component_list"]
|
||||
}
|
||||
self._components = self._parse_components(self._components_raw)
|
||||
|
||||
if "child_device" in self._components and not self.children:
|
||||
await self._initialize_children()
|
||||
|
@ -3,13 +3,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from ..device import _DeviceInfo
|
||||
from ..device_type import DeviceType
|
||||
from ..module import Module
|
||||
from ..protocols.smartcamprotocol import _ChildCameraProtocolWrapper
|
||||
from ..smart import SmartChildDevice, SmartDevice
|
||||
from ..smart.smartdevice import ComponentsRaw
|
||||
from .modules import ChildDevice, DeviceModule
|
||||
from .smartcammodule import SmartCamModule
|
||||
|
||||
@ -78,7 +79,7 @@ class SmartCamDevice(SmartDevice):
|
||||
self._children[child_id]._update_internal_state(info)
|
||||
|
||||
async def _initialize_smart_child(
|
||||
self, info: dict, child_components: dict
|
||||
self, info: dict, child_components_raw: ComponentsRaw
|
||||
) -> SmartDevice:
|
||||
"""Initialize a smart child device attached to a smartcam device."""
|
||||
child_id = info["device_id"]
|
||||
@ -93,7 +94,7 @@ class SmartCamDevice(SmartDevice):
|
||||
return await SmartChildDevice.create(
|
||||
parent=self,
|
||||
child_info=info,
|
||||
child_components=child_components,
|
||||
child_components_raw=child_components_raw,
|
||||
protocol=child_protocol,
|
||||
last_update=initial_response,
|
||||
)
|
||||
@ -108,17 +109,8 @@ class SmartCamDevice(SmartDevice):
|
||||
self.internal_state.update(resp)
|
||||
|
||||
smart_children_components = {
|
||||
child["device_id"]: {
|
||||
comp["id"]: int(comp["ver_code"]) for comp in component_list
|
||||
}
|
||||
child["device_id"]: child
|
||||
for child in resp["getChildDeviceComponentList"]["child_component_list"]
|
||||
if (component_list := child.get("component_list"))
|
||||
# Child camera devices will have a different component schema so only
|
||||
# extract smart values.
|
||||
and (first_comp := next(iter(component_list), None))
|
||||
and isinstance(first_comp, dict)
|
||||
and "id" in first_comp
|
||||
and "ver_code" in first_comp
|
||||
}
|
||||
children = {}
|
||||
for info in resp["getChildDeviceList"]["child_device_list"]:
|
||||
@ -172,6 +164,13 @@ class SmartCamDevice(SmartDevice):
|
||||
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def _parse_components(components_raw: ComponentsRaw) -> dict[str, int]:
|
||||
return {
|
||||
str(comp["name"]): int(comp["version"])
|
||||
for comp in components_raw["app_component_list"]
|
||||
}
|
||||
|
||||
async def _negotiate(self) -> None:
|
||||
"""Perform initialization.
|
||||
|
||||
@ -186,12 +185,10 @@ class SmartCamDevice(SmartDevice):
|
||||
self._last_update.update(resp)
|
||||
self._update_internal_info(resp)
|
||||
|
||||
self._components = {
|
||||
comp["name"]: int(comp["version"])
|
||||
for comp in resp["getAppComponentList"]["app_component"][
|
||||
"app_component_list"
|
||||
]
|
||||
}
|
||||
self._components_raw = cast(
|
||||
ComponentsRaw, resp["getAppComponentList"]["app_component"]
|
||||
)
|
||||
self._components = self._parse_components(self._components_raw)
|
||||
|
||||
if "childControl" in self._components and not self.children:
|
||||
await self._initialize_children()
|
||||
|
@ -145,7 +145,9 @@ async def test_child_device_type_unknown(caplog):
|
||||
super().__init__(
|
||||
SmartDevice("127.0.0.1"),
|
||||
{"device_id": "1", "category": "foobar"},
|
||||
{"device", 1},
|
||||
{
|
||||
"component_list": [{"id": "device", "ver_code": 1}],
|
||||
},
|
||||
)
|
||||
|
||||
assert DummyDevice().device_type is DeviceType.Unknown
|
||||
|
@ -86,7 +86,11 @@ async def test_device_class_ctors(device_class_name_obj):
|
||||
if issubclass(klass, SmartChildDevice):
|
||||
parent = SmartDevice(host, config=config)
|
||||
dev = klass(
|
||||
parent, {"dummy": "info", "device_id": "dummy"}, {"dummy": "components"}
|
||||
parent,
|
||||
{"dummy": "info", "device_id": "dummy"},
|
||||
{
|
||||
"component_list": [{"id": "device", "ver_code": 1}],
|
||||
},
|
||||
)
|
||||
else:
|
||||
dev = klass(host, config=config)
|
||||
@ -106,7 +110,11 @@ async def test_device_class_repr(device_class_name_obj):
|
||||
if issubclass(klass, SmartChildDevice):
|
||||
parent = SmartDevice(host, config=config)
|
||||
dev = klass(
|
||||
parent, {"dummy": "info", "device_id": "dummy"}, {"dummy": "components"}
|
||||
parent,
|
||||
{"dummy": "info", "device_id": "dummy"},
|
||||
{
|
||||
"component_list": [{"id": "device", "ver_code": 1}],
|
||||
},
|
||||
)
|
||||
else:
|
||||
dev = klass(host, config=config)
|
||||
|
Loading…
Reference in New Issue
Block a user