mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-05-16 19:41:09 +00:00
Add childprotection module (#1141)
When turned on, rotating the thermostat will not change the target temperature.
This commit is contained in:
parent
77b654a9aa
commit
0360107e3f
@ -127,6 +127,9 @@ class Module(ABC):
|
|||||||
WaterleakSensor: Final[ModuleName[smart.WaterleakSensor]] = ModuleName(
|
WaterleakSensor: Final[ModuleName[smart.WaterleakSensor]] = ModuleName(
|
||||||
"WaterleakSensor"
|
"WaterleakSensor"
|
||||||
)
|
)
|
||||||
|
ChildProtection: Final[ModuleName[smart.ChildProtection]] = ModuleName(
|
||||||
|
"ChildProtection"
|
||||||
|
)
|
||||||
TriggerLogs: Final[ModuleName[smart.TriggerLogs]] = ModuleName("TriggerLogs")
|
TriggerLogs: Final[ModuleName[smart.TriggerLogs]] = ModuleName("TriggerLogs")
|
||||||
|
|
||||||
# SMARTCAMERA only modules
|
# SMARTCAMERA only modules
|
||||||
|
@ -6,6 +6,7 @@ from .autooff import AutoOff
|
|||||||
from .batterysensor import BatterySensor
|
from .batterysensor import BatterySensor
|
||||||
from .brightness import Brightness
|
from .brightness import Brightness
|
||||||
from .childdevice import ChildDevice
|
from .childdevice import ChildDevice
|
||||||
|
from .childprotection import ChildProtection
|
||||||
from .cloud import Cloud
|
from .cloud import Cloud
|
||||||
from .color import Color
|
from .color import Color
|
||||||
from .colortemperature import ColorTemperature
|
from .colortemperature import ColorTemperature
|
||||||
@ -40,6 +41,7 @@ __all__ = [
|
|||||||
"HumiditySensor",
|
"HumiditySensor",
|
||||||
"TemperatureSensor",
|
"TemperatureSensor",
|
||||||
"TemperatureControl",
|
"TemperatureControl",
|
||||||
|
"ChildProtection",
|
||||||
"ReportMode",
|
"ReportMode",
|
||||||
"AutoOff",
|
"AutoOff",
|
||||||
"Led",
|
"Led",
|
||||||
|
41
kasa/smart/modules/childprotection.py
Normal file
41
kasa/smart/modules/childprotection.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""Child lock module."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from ...feature import Feature
|
||||||
|
from ..smartmodule import SmartModule
|
||||||
|
|
||||||
|
|
||||||
|
class ChildProtection(SmartModule):
|
||||||
|
"""Implementation for child_protection."""
|
||||||
|
|
||||||
|
REQUIRED_COMPONENT = "child_protection"
|
||||||
|
QUERY_GETTER_NAME = "get_child_protection"
|
||||||
|
|
||||||
|
def _initialize_features(self):
|
||||||
|
"""Initialize features after the initial update."""
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
id="child_lock",
|
||||||
|
name="Child lock",
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self) -> bool:
|
||||||
|
"""Return True if child protection is enabled."""
|
||||||
|
return self.data["child_protection"]
|
||||||
|
|
||||||
|
async def set_enabled(self, enabled: bool) -> dict:
|
||||||
|
"""Set child protection."""
|
||||||
|
return await self.call("set_child_protection", {"enable": enabled})
|
@ -430,6 +430,16 @@ class FakeSmartTransport(BaseTransport):
|
|||||||
info["get_preset_rules"]["states"][params["index"]] = params["state"]
|
info["get_preset_rules"]["states"][params["index"]] = params["state"]
|
||||||
return {"error_code": 0}
|
return {"error_code": 0}
|
||||||
|
|
||||||
|
def _update_sysinfo_key(self, info: dict, key: str, value: str) -> dict:
|
||||||
|
"""Update a single key in the main system info.
|
||||||
|
|
||||||
|
This is used to implement child device setters that change the main sysinfo state.
|
||||||
|
"""
|
||||||
|
sys_info = info.get("get_device_info", info)
|
||||||
|
sys_info[key] = value
|
||||||
|
|
||||||
|
return {"error_code": 0}
|
||||||
|
|
||||||
async def _send_request(self, request_dict: dict):
|
async def _send_request(self, request_dict: dict):
|
||||||
method = request_dict["method"]
|
method = request_dict["method"]
|
||||||
|
|
||||||
@ -437,7 +447,7 @@ class FakeSmartTransport(BaseTransport):
|
|||||||
if method == "control_child":
|
if method == "control_child":
|
||||||
return await self._handle_control_child(request_dict["params"])
|
return await self._handle_control_child(request_dict["params"])
|
||||||
|
|
||||||
params = request_dict.get("params")
|
params = request_dict.get("params", {})
|
||||||
if method == "component_nego" or method[:4] == "get_":
|
if method == "component_nego" or method[:4] == "get_":
|
||||||
if method in info:
|
if method in info:
|
||||||
result = copy.deepcopy(info[method])
|
result = copy.deepcopy(info[method])
|
||||||
@ -518,6 +528,8 @@ class FakeSmartTransport(BaseTransport):
|
|||||||
return self._edit_preset_rules(info, params)
|
return self._edit_preset_rules(info, params)
|
||||||
elif method == "set_on_off_gradually_info":
|
elif method == "set_on_off_gradually_info":
|
||||||
return self._set_on_off_gradually_info(info, params)
|
return self._set_on_off_gradually_info(info, params)
|
||||||
|
elif method == "set_child_protection":
|
||||||
|
return self._update_sysinfo_key(info, "child_protection", params["enable"])
|
||||||
elif method[:4] == "set_":
|
elif method[:4] == "set_":
|
||||||
target_method = f"get_{method[4:]}"
|
target_method = f"get_{method[4:]}"
|
||||||
info[target_method].update(params)
|
info[target_method].update(params)
|
||||||
|
43
kasa/tests/smart/modules/test_childprotection.py
Normal file
43
kasa/tests/smart/modules/test_childprotection.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from kasa import Module
|
||||||
|
from kasa.smart.modules import ChildProtection
|
||||||
|
from kasa.tests.device_fixtures import parametrize
|
||||||
|
|
||||||
|
child_protection = parametrize(
|
||||||
|
"has child protection",
|
||||||
|
component_filter="child_protection",
|
||||||
|
protocol_filter={"SMART.CHILD"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@child_protection
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("feature", "prop_name", "type"),
|
||||||
|
[
|
||||||
|
("child_lock", "enabled", bool),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_features(dev, feature, prop_name, type):
|
||||||
|
"""Test that features are registered and work as expected."""
|
||||||
|
protect: ChildProtection = dev.modules[Module.ChildProtection]
|
||||||
|
assert protect is not None
|
||||||
|
|
||||||
|
prop = getattr(protect, prop_name)
|
||||||
|
assert isinstance(prop, type)
|
||||||
|
|
||||||
|
feat = protect._device.features[feature]
|
||||||
|
assert feat.value == prop
|
||||||
|
assert isinstance(feat.value, type)
|
||||||
|
|
||||||
|
|
||||||
|
@child_protection
|
||||||
|
async def test_enabled(dev):
|
||||||
|
"""Test the API."""
|
||||||
|
protect: ChildProtection = dev.modules[Module.ChildProtection]
|
||||||
|
assert protect is not None
|
||||||
|
|
||||||
|
assert isinstance(protect.enabled, bool)
|
||||||
|
await protect.set_enabled(False)
|
||||||
|
await dev.update()
|
||||||
|
assert protect.enabled is False
|
Loading…
x
Reference in New Issue
Block a user