mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-12-21 04:00:19 +00:00
Add core device, child and camera modules to smartcamera (#1193)
Co-authored-by: Teemu R. <tpr@iki.fi>
This commit is contained in:
@@ -2,16 +2,26 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..exceptions import SmartErrorCode
|
||||
from ..smart import SmartDevice
|
||||
from ..module import Module
|
||||
from ..smart import SmartChildDevice, SmartDevice
|
||||
from .modules.childdevice import ChildDevice
|
||||
from .modules.device import DeviceModule
|
||||
from .smartcameramodule import SmartCameraModule
|
||||
from .smartcameraprotocol import _ChildCameraProtocolWrapper
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SmartCamera(SmartDevice):
|
||||
"""Class for smart cameras."""
|
||||
|
||||
# Modules that are called as part of the init procedure on first update
|
||||
FIRST_UPDATE_MODULES = {DeviceModule, ChildDevice}
|
||||
|
||||
@staticmethod
|
||||
def _get_device_type_from_sysinfo(sysinfo: dict[str, Any]) -> DeviceType:
|
||||
"""Find type to be displayed as a supported device category."""
|
||||
@@ -20,17 +30,108 @@ class SmartCamera(SmartDevice):
|
||||
return DeviceType.Hub
|
||||
return DeviceType.Camera
|
||||
|
||||
async def update(self, update_children: bool = False):
|
||||
"""Update the device."""
|
||||
def _update_internal_info(self, info_resp: dict) -> None:
|
||||
"""Update the internal device info."""
|
||||
info = self._try_get_response(info_resp, "getDeviceInfo")
|
||||
self._info = self._map_info(info["device_info"])
|
||||
|
||||
def _update_children_info(self) -> None:
|
||||
"""Update the internal child device info from the parent info."""
|
||||
if child_info := self._try_get_response(
|
||||
self._last_update, "getChildDeviceList", {}
|
||||
):
|
||||
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:
|
||||
"""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}
|
||||
)
|
||||
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)
|
||||
|
||||
return await SmartChildDevice.create(
|
||||
parent=self,
|
||||
child_info=info,
|
||||
child_components=child_components,
|
||||
protocol=child_protocol,
|
||||
last_update=initial_response,
|
||||
)
|
||||
|
||||
async def _initialize_children(self) -> None:
|
||||
"""Initialize children for hubs."""
|
||||
if not (
|
||||
child_info := self._try_get_response(
|
||||
self._last_update, "getChildDeviceList", {}
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
children = {}
|
||||
for info in child_info["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)
|
||||
else:
|
||||
_LOGGER.debug("Child device type not supported: %s", info)
|
||||
|
||||
self._children = children
|
||||
|
||||
async def _initialize_modules(self) -> None:
|
||||
"""Initialize modules based on component negotiation response."""
|
||||
for mod in SmartCameraModule.REGISTERED_MODULES.values():
|
||||
module = mod(self, mod._module_name())
|
||||
if await module._check_supported():
|
||||
self._modules[module.name] = module
|
||||
|
||||
async def _initialize_features(self) -> None:
|
||||
"""Initialize device features."""
|
||||
for module in self.modules.values():
|
||||
module._initialize_features()
|
||||
for feat in module._module_features.values():
|
||||
self._add_feature(feat)
|
||||
|
||||
for child in self._children.values():
|
||||
await child._initialize_features()
|
||||
|
||||
async def _query_setter_helper(
|
||||
self, method: str, module: str, section: str, params: dict | None = None
|
||||
) -> dict:
|
||||
res = await self.protocol.query({method: {module: {section: params}}})
|
||||
|
||||
return res
|
||||
|
||||
async def _query_getter_helper(
|
||||
self, method: str, module: str, sections: str | list[str]
|
||||
) -> Any:
|
||||
res = await self.protocol.query({method: {module: {"name": sections}}})
|
||||
|
||||
return res
|
||||
|
||||
async def _negotiate(self) -> None:
|
||||
"""Perform initialization.
|
||||
|
||||
We fetch the device info and the available components as early as possible.
|
||||
If the device reports supporting child devices, they are also initialized.
|
||||
"""
|
||||
initial_query = {
|
||||
"getDeviceInfo": {"device_info": {"name": ["basic_info", "info"]}},
|
||||
"getLensMaskConfig": {"lens_mask": {"name": ["lens_mask_info"]}},
|
||||
"getChildDeviceList": {"childControl": {"start_index": 0}},
|
||||
}
|
||||
resp = await self.protocol.query(initial_query)
|
||||
self._last_update.update(resp)
|
||||
info = self._try_get_response(resp, "getDeviceInfo")
|
||||
self._info = self._map_info(info["device_info"])
|
||||
self._last_update = resp
|
||||
self._update_internal_info(resp)
|
||||
await self._initialize_children()
|
||||
|
||||
def _map_info(self, device_info: dict) -> dict:
|
||||
basic_info = device_info["basic_info"]
|
||||
@@ -48,25 +149,17 @@ class SmartCamera(SmartDevice):
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if the device is on."""
|
||||
if isinstance(self._last_update["getLensMaskConfig"], SmartErrorCode):
|
||||
return True
|
||||
return (
|
||||
self._last_update["getLensMaskConfig"]["lens_mask"]["lens_mask_info"][
|
||||
"enabled"
|
||||
]
|
||||
== "on"
|
||||
)
|
||||
if (camera := self.modules.get(Module.Camera)) and not camera.disabled:
|
||||
return camera.is_on
|
||||
|
||||
async def set_state(self, on: bool):
|
||||
return True
|
||||
|
||||
async def set_state(self, on: bool) -> dict:
|
||||
"""Set the device state."""
|
||||
if isinstance(self._last_update["getLensMaskConfig"], SmartErrorCode):
|
||||
return
|
||||
query = {
|
||||
"setLensMaskConfig": {
|
||||
"lens_mask": {"lens_mask_info": {"enabled": "on" if on else "off"}}
|
||||
},
|
||||
}
|
||||
return await self.protocol.query(query)
|
||||
if (camera := self.modules.get(Module.Camera)) and not camera.disabled:
|
||||
return await camera.set_state(on)
|
||||
|
||||
return {}
|
||||
|
||||
@property
|
||||
def device_type(self) -> DeviceType:
|
||||
@@ -82,6 +175,14 @@ class SmartCamera(SmartDevice):
|
||||
return self._info.get("alias")
|
||||
return None
|
||||
|
||||
async def set_alias(self, alias: str) -> dict:
|
||||
"""Set the device name (alias)."""
|
||||
return await self.protocol.query(
|
||||
{
|
||||
"setDeviceAlias": {"system": {"sys": {"dev_alias": alias}}},
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def hw_info(self) -> dict:
|
||||
"""Return hardware info for the device."""
|
||||
|
||||
Reference in New Issue
Block a user