mirror of
				https://github.com/python-kasa/python-kasa.git
				synced 2025-10-31 12:41:54 +00:00 
			
		
		
		
	Add childprotection module (#1141)
When turned on, rotating the thermostat will not change the target temperature.
This commit is contained in:
		| @@ -127,6 +127,9 @@ class Module(ABC): | ||||
|     WaterleakSensor: Final[ModuleName[smart.WaterleakSensor]] = ModuleName( | ||||
|         "WaterleakSensor" | ||||
|     ) | ||||
|     ChildProtection: Final[ModuleName[smart.ChildProtection]] = ModuleName( | ||||
|         "ChildProtection" | ||||
|     ) | ||||
|     TriggerLogs: Final[ModuleName[smart.TriggerLogs]] = ModuleName("TriggerLogs") | ||||
|  | ||||
|     # SMARTCAMERA only modules | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from .autooff import AutoOff | ||||
| from .batterysensor import BatterySensor | ||||
| from .brightness import Brightness | ||||
| from .childdevice import ChildDevice | ||||
| from .childprotection import ChildProtection | ||||
| from .cloud import Cloud | ||||
| from .color import Color | ||||
| from .colortemperature import ColorTemperature | ||||
| @@ -40,6 +41,7 @@ __all__ = [ | ||||
|     "HumiditySensor", | ||||
|     "TemperatureSensor", | ||||
|     "TemperatureControl", | ||||
|     "ChildProtection", | ||||
|     "ReportMode", | ||||
|     "AutoOff", | ||||
|     "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"] | ||||
|         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): | ||||
|         method = request_dict["method"] | ||||
|  | ||||
| @@ -437,7 +447,7 @@ class FakeSmartTransport(BaseTransport): | ||||
|         if method == "control_child": | ||||
|             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 in info: | ||||
|                 result = copy.deepcopy(info[method]) | ||||
| @@ -518,6 +528,8 @@ class FakeSmartTransport(BaseTransport): | ||||
|             return self._edit_preset_rules(info, params) | ||||
|         elif method == "set_on_off_gradually_info": | ||||
|             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_": | ||||
|             target_method = f"get_{method[4:]}" | ||||
|             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 | ||||
		Reference in New Issue
	
	Block a user
	 Teemu R.
					Teemu R.