Add double click module for smart buttons

This commit is contained in:
Steven B 2025-01-07 12:53:10 +00:00
parent 7b3dde9aa0
commit cb972c3a30
No known key found for this signature in database
GPG Key ID: 6D5B46B3679F2A43
6 changed files with 100 additions and 4 deletions

View File

@ -12,6 +12,7 @@ from .color import Color
from .colortemperature import ColorTemperature
from .contactsensor import ContactSensor
from .devicemodule import DeviceModule
from .doubleclick import DoubleClick
from .energy import Energy
from .fan import Fan
from .firmware import Firmware
@ -42,6 +43,7 @@ __all__ = [
"DeviceModule",
"ChildDevice",
"BatterySensor",
"DoubleClick",
"HumiditySensor",
"TemperatureSensor",
"TemperatureControl",

View File

@ -0,0 +1,42 @@
"""Module for double click enable."""
from __future__ import annotations
from ...feature import Feature
from ..smartmodule import SmartModule, allow_update_after
class DoubleClick(SmartModule):
"""Implementation of double click module."""
REQUIRED_COMPONENT = "double_click"
QUERY_GETTER_NAME = "get_double_click_info"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="double_click",
name="Double click",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
def query(self) -> dict:
"""Query to execute during the update cycle."""
return {self.QUERY_GETTER_NAME: {}}
@property
def enabled(self) -> bool:
"""Return current double click enabled status."""
return self.data["enable"]
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set double click enable."""
return await self.call("set_double_click_info", {"enable": enable})

View File

@ -183,7 +183,7 @@ class SmartDevice(Device):
"""Update the internal device info."""
self._info = self._try_get_response(info_resp, "get_device_info")
async def update(self, update_children: bool = False) -> None:
async def update(self, update_children: bool = True) -> None:
"""Update the device."""
if self.credentials is None and self.credentials_hash is None:
raise AuthenticationError("Tapo plug requires authentication.")
@ -207,7 +207,7 @@ class SmartDevice(Device):
# devices will always update children to prevent errors on module access.
# This needs to go after updating the internal state of the children so that
# child modules have access to their sysinfo.
if update_children or self.device_type != DeviceType.Hub:
if first_update or update_children or self.device_type != DeviceType.Hub:
for child in self._children.values():
if TYPE_CHECKING:
assert isinstance(child, SmartChildDevice)

View File

@ -4,12 +4,13 @@ from __future__ import annotations
import logging
from collections.abc import Awaitable, Callable, Coroutine
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar
from typing import TYPE_CHECKING, Any, Concatenate, Final, ParamSpec, TypeVar
from ..exceptions import DeviceError, KasaException, SmartErrorCode
from ..module import Module
from ..module import Module, ModuleName
if TYPE_CHECKING:
from . import modules
from .smartdevice import SmartDevice
_LOGGER = logging.getLogger(__name__)
@ -51,6 +52,8 @@ def raise_if_update_error(func: Callable[[_T], _R]) -> Callable[[_T], _R]:
class SmartModule(Module):
"""Base class for SMART modules."""
SmartDoubleClick: Final[ModuleName[modules.DoubleClick]] = ModuleName("DoubleClick")
NAME: str
#: Module is initialized, if the given component is available
REQUIRED_COMPONENT: str | None = None

View File

@ -167,6 +167,12 @@ class FakeSmartTransport(BaseTransport):
"setup_payload": "00:0000000-0000.00.000",
},
),
"get_double_click_info": (
"double_click",
{
"enable": False,
},
),
}
async def send(self, request: str):

View File

@ -0,0 +1,43 @@
"""Tests for smart double click module."""
from __future__ import annotations
from kasa import Device
from kasa.smartcam.smartcammodule import SmartModule
from ...device_fixtures import parametrize
doubleclick = parametrize(
"has double click", component_filter="double_click", protocol_filter={"SMART.CHILD"}
)
@doubleclick
async def test_doubleclick(dev: Device):
"""Test device double click."""
doubleclick = dev.modules.get(SmartModule.SmartDoubleClick)
assert doubleclick
dc_feat = dev.features.get("double_click")
assert dc_feat
original_enabled = doubleclick.enabled
try:
await doubleclick.set_enabled(not original_enabled)
await dev.update()
assert doubleclick.enabled is not original_enabled
assert dc_feat.value is not original_enabled
await doubleclick.set_enabled(original_enabled)
await dev.update()
assert doubleclick.enabled is original_enabled
assert dc_feat.value is original_enabled
await dc_feat.set_value(not original_enabled)
await dev.update()
assert doubleclick.enabled is not original_enabled
assert dc_feat.value is not original_enabled
finally:
await doubleclick.set_enabled(original_enabled)