mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-05-30 21:51:24 +00:00
Add double click module for smart buttons
This commit is contained in:
parent
7b3dde9aa0
commit
cb972c3a30
@ -12,6 +12,7 @@ from .color import Color
|
|||||||
from .colortemperature import ColorTemperature
|
from .colortemperature import ColorTemperature
|
||||||
from .contactsensor import ContactSensor
|
from .contactsensor import ContactSensor
|
||||||
from .devicemodule import DeviceModule
|
from .devicemodule import DeviceModule
|
||||||
|
from .doubleclick import DoubleClick
|
||||||
from .energy import Energy
|
from .energy import Energy
|
||||||
from .fan import Fan
|
from .fan import Fan
|
||||||
from .firmware import Firmware
|
from .firmware import Firmware
|
||||||
@ -42,6 +43,7 @@ __all__ = [
|
|||||||
"DeviceModule",
|
"DeviceModule",
|
||||||
"ChildDevice",
|
"ChildDevice",
|
||||||
"BatterySensor",
|
"BatterySensor",
|
||||||
|
"DoubleClick",
|
||||||
"HumiditySensor",
|
"HumiditySensor",
|
||||||
"TemperatureSensor",
|
"TemperatureSensor",
|
||||||
"TemperatureControl",
|
"TemperatureControl",
|
||||||
|
42
kasa/smart/modules/doubleclick.py
Normal file
42
kasa/smart/modules/doubleclick.py
Normal 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})
|
@ -183,7 +183,7 @@ class SmartDevice(Device):
|
|||||||
"""Update the internal device info."""
|
"""Update the internal device info."""
|
||||||
self._info = self._try_get_response(info_resp, "get_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."""
|
"""Update the device."""
|
||||||
if self.credentials is None and self.credentials_hash is None:
|
if self.credentials is None and self.credentials_hash is None:
|
||||||
raise AuthenticationError("Tapo plug requires authentication.")
|
raise AuthenticationError("Tapo plug requires authentication.")
|
||||||
@ -207,7 +207,7 @@ class SmartDevice(Device):
|
|||||||
# devices will always update children to prevent errors on module access.
|
# 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
|
# This needs to go after updating the internal state of the children so that
|
||||||
# child modules have access to their sysinfo.
|
# 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():
|
for child in self._children.values():
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
assert isinstance(child, SmartChildDevice)
|
assert isinstance(child, SmartChildDevice)
|
||||||
|
@ -4,12 +4,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections.abc import Awaitable, Callable, Coroutine
|
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 ..exceptions import DeviceError, KasaException, SmartErrorCode
|
||||||
from ..module import Module
|
from ..module import Module, ModuleName
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from . import modules
|
||||||
from .smartdevice import SmartDevice
|
from .smartdevice import SmartDevice
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -51,6 +52,8 @@ def raise_if_update_error(func: Callable[[_T], _R]) -> Callable[[_T], _R]:
|
|||||||
class SmartModule(Module):
|
class SmartModule(Module):
|
||||||
"""Base class for SMART modules."""
|
"""Base class for SMART modules."""
|
||||||
|
|
||||||
|
SmartDoubleClick: Final[ModuleName[modules.DoubleClick]] = ModuleName("DoubleClick")
|
||||||
|
|
||||||
NAME: str
|
NAME: str
|
||||||
#: Module is initialized, if the given component is available
|
#: Module is initialized, if the given component is available
|
||||||
REQUIRED_COMPONENT: str | None = None
|
REQUIRED_COMPONENT: str | None = None
|
||||||
|
@ -167,6 +167,12 @@ class FakeSmartTransport(BaseTransport):
|
|||||||
"setup_payload": "00:0000000-0000.00.000",
|
"setup_payload": "00:0000000-0000.00.000",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
"get_double_click_info": (
|
||||||
|
"double_click",
|
||||||
|
{
|
||||||
|
"enable": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def send(self, request: str):
|
async def send(self, request: str):
|
||||||
|
43
tests/smart/modules/test_doubleclick.py
Normal file
43
tests/smart/modules/test_doubleclick.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user