From 623d93da3ee3d1e2f0873c6570efcbcc1c14ab67 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 18 Feb 2024 22:19:30 +0100 Subject: [PATCH 1/3] Add brightness module --- kasa/smart/modules/__init__.py | 2 ++ kasa/smart/modules/brightness.py | 40 +++++++++++++++++++++++++++ kasa/smart/smartmodule.py | 11 ++++++-- kasa/tests/device_fixtures.py | 2 -- kasa/tests/test_feature_brightness.py | 18 +++++++++--- 5 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 kasa/smart/modules/brightness.py diff --git a/kasa/smart/modules/__init__.py b/kasa/smart/modules/__init__.py index 3e95dfe78..dc4e0cf5a 100644 --- a/kasa/smart/modules/__init__.py +++ b/kasa/smart/modules/__init__.py @@ -2,6 +2,7 @@ from .alarmmodule import AlarmModule from .autooffmodule import AutoOffModule from .battery import BatterySensor +from .brightness import Brightness from .childdevicemodule import ChildDeviceModule from .cloudmodule import CloudModule from .devicemodule import DeviceModule @@ -26,6 +27,7 @@ "ReportModule", "AutoOffModule", "LedModule", + "Brightness", "Firmware", "CloudModule", "LightTransitionModule", diff --git a/kasa/smart/modules/brightness.py b/kasa/smart/modules/brightness.py new file mode 100644 index 000000000..691ee94e1 --- /dev/null +++ b/kasa/smart/modules/brightness.py @@ -0,0 +1,40 @@ +"""Implementation of brightness module.""" +from typing import TYPE_CHECKING, Dict + +from ...feature import Feature +from ..smartmodule import SmartModule + +if TYPE_CHECKING: + from ..smartdevice import SmartDevice + + +class Brightness(SmartModule): + """Implementation of brightness module.""" + + REQUIRED_COMPONENT = "brightness" + + def __init__(self, device: "SmartDevice", module: str): + super().__init__(device, module) + self._add_feature( + Feature( + device, + "Brightness", + container=self, + attribute_getter="brightness", + attribute_setter="set_brightness", + ) + ) + + def query(self) -> Dict: + """Query to execute during the update cycle.""" + # Brightness is contained in the main device info response. + return {} + + @property + def brightness(self): + """Return current brightness.""" + return self.data["brightness"] + + async def set_brightness(self, brightness: int): + """Set the brightness.""" + return await self.call("set_device_info", {"brightness": brightness}) diff --git a/kasa/smart/smartmodule.py b/kasa/smart/smartmodule.py index e34f2260a..01a27360f 100644 --- a/kasa/smart/smartmodule.py +++ b/kasa/smart/smartmodule.py @@ -53,14 +53,19 @@ def call(self, method, params=None): def data(self): """Return response data for the module. - If module performs only a single query, the resulting response is unwrapped. + If the module performs only a single query, the resulting response is unwrapped. + If the module does not define a query, this property returns a reference + to the main "get_device_info" response. """ + dev = self._device q = self.query() + + if not q: + return dev.internal_state["get_device_info"] + q_keys = list(q.keys()) query_key = q_keys[0] - dev = self._device - # TODO: hacky way to check if update has been called. # The way this falls back to parent may not always be wanted. # Especially, devices can have their own firmware updates. diff --git a/kasa/tests/device_fixtures.py b/kasa/tests/device_fixtures.py index 73d171d23..8a1b643bd 100644 --- a/kasa/tests/device_fixtures.py +++ b/kasa/tests/device_fixtures.py @@ -249,8 +249,6 @@ def parametrize( "devices iot", model_filter=ALL_DEVICES_IOT, protocol_filter={"IOT"} ) -brightness = parametrize("brightness smart", component_filter="brightness") - def check_categories(): """Check that every fixture file is categorized.""" diff --git a/kasa/tests/test_feature_brightness.py b/kasa/tests/test_feature_brightness.py index d99b55d1d..a4b2ddc70 100644 --- a/kasa/tests/test_feature_brightness.py +++ b/kasa/tests/test_feature_brightness.py @@ -1,12 +1,22 @@ from kasa.smart import SmartDevice +from kasa.tests.conftest import parametrize -from .conftest import ( - brightness, -) +brightness = parametrize("brightness smart", component_filter="brightness") @brightness async def test_brightness_component(dev: SmartDevice): - """Placeholder to test framwework component filter.""" + """Test brightness feature.""" assert isinstance(dev, SmartDevice) assert "brightness" in dev._components + + # Test getting the value + feature = dev.features["brightness"] + assert isinstance(feature.value, int) + assert feature.value > 0 and feature.value <= 100 + + # Test setting the value + await feature.set_value(10) + assert feature.value == 10 + + From ac8903d5e72dd5fd1eab21302d0b3c5372234452 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Mon, 4 Mar 2024 21:38:48 +0100 Subject: [PATCH 2/3] Set range and fix linting --- kasa/smart/modules/brightness.py | 2 ++ kasa/tests/test_feature_brightness.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kasa/smart/modules/brightness.py b/kasa/smart/modules/brightness.py index 691ee94e1..7b788582d 100644 --- a/kasa/smart/modules/brightness.py +++ b/kasa/smart/modules/brightness.py @@ -22,6 +22,8 @@ def __init__(self, device: "SmartDevice", module: str): container=self, attribute_getter="brightness", attribute_setter="set_brightness", + minimum_value=1, + maximum_value=100, ) ) diff --git a/kasa/tests/test_feature_brightness.py b/kasa/tests/test_feature_brightness.py index a4b2ddc70..8aa3b3ea6 100644 --- a/kasa/tests/test_feature_brightness.py +++ b/kasa/tests/test_feature_brightness.py @@ -18,5 +18,3 @@ async def test_brightness_component(dev: SmartDevice): # Test setting the value await feature.set_value(10) assert feature.value == 10 - - From 6a86225551be87967b87f89066f8dfb0f43ac8b1 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Mon, 4 Mar 2024 21:42:22 +0100 Subject: [PATCH 3/3] Test that invalid values raise an error --- kasa/smart/modules/brightness.py | 3 ++- kasa/tests/test_feature_brightness.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kasa/smart/modules/brightness.py b/kasa/smart/modules/brightness.py index 7b788582d..03e9e238c 100644 --- a/kasa/smart/modules/brightness.py +++ b/kasa/smart/modules/brightness.py @@ -1,7 +1,7 @@ """Implementation of brightness module.""" from typing import TYPE_CHECKING, Dict -from ...feature import Feature +from ...feature import Feature, FeatureType from ..smartmodule import SmartModule if TYPE_CHECKING: @@ -24,6 +24,7 @@ def __init__(self, device: "SmartDevice", module: str): attribute_setter="set_brightness", minimum_value=1, maximum_value=100, + type=FeatureType.Number, ) ) diff --git a/kasa/tests/test_feature_brightness.py b/kasa/tests/test_feature_brightness.py index 8aa3b3ea6..9d9d3165a 100644 --- a/kasa/tests/test_feature_brightness.py +++ b/kasa/tests/test_feature_brightness.py @@ -1,3 +1,5 @@ +import pytest + from kasa.smart import SmartDevice from kasa.tests.conftest import parametrize @@ -18,3 +20,9 @@ async def test_brightness_component(dev: SmartDevice): # Test setting the value await feature.set_value(10) assert feature.value == 10 + + with pytest.raises(ValueError): + await feature.set_value(feature.minimum_value - 10) + + with pytest.raises(ValueError): + await feature.set_value(feature.maximum_value + 10)