From 101adda4457a0754fc62a790578a4dda0dfd1548 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Thu, 16 Mar 2023 18:46:21 +0100 Subject: [PATCH 1/4] Make hue, saturation and color_temp optional for smartbulbpresets --- kasa/smartbulb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kasa/smartbulb.py b/kasa/smartbulb.py index ff94e1697..b685141cf 100644 --- a/kasa/smartbulb.py +++ b/kasa/smartbulb.py @@ -30,9 +30,9 @@ class SmartBulbPreset(BaseModel): index: int brightness: int - hue: int - saturation: int - color_temp: int + hue: Optional[int] + saturation: Optional[int] + color_temp: Optional[int] class BehaviorMode(str, Enum): From 6f5c870677d32bdd6d02227fd850f4e172369d67 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Thu, 30 Mar 2023 14:52:37 +0200 Subject: [PATCH 2/4] Adjust bulb preset attributes for effect mode --- kasa/smartbulb.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kasa/smartbulb.py b/kasa/smartbulb.py index b685141cf..e9a9eba89 100644 --- a/kasa/smartbulb.py +++ b/kasa/smartbulb.py @@ -30,10 +30,17 @@ class SmartBulbPreset(BaseModel): index: int brightness: int + + # These are not available for effect mode presets on light strips hue: Optional[int] saturation: Optional[int] color_temp: Optional[int] + # Variables for effect mode presets + custom: Optional[int] + id: Optional[str] + mode: Optional[int] + class BehaviorMode(str, Enum): """Enum to present type of turn on behavior.""" @@ -514,7 +521,7 @@ def presets(self) -> List[SmartBulbPreset]: async def save_preset(self, preset: SmartBulbPreset): """Save a setting preset. - You can either construct a preset object manually, or pass an existing one obtained + You can either construct a preset object manually, or pass an existing one obtained using :func:`presets`. """ if len(self.presets) == 0: From eb730604de6bf640ac9fd8dd7e9d0b47228bb25b Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sat, 1 Apr 2023 16:07:12 +0200 Subject: [PATCH 3/4] Don't send None values on save_preset Just to avoid potential problems if devices do not accept extra keys. --- kasa/smartbulb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kasa/smartbulb.py b/kasa/smartbulb.py index e9a9eba89..ece659bea 100644 --- a/kasa/smartbulb.py +++ b/kasa/smartbulb.py @@ -181,7 +181,7 @@ class SmartBulb(SmartDevice): Bulb configuration presets can be accessed using the :func:`presets` property: >>> bulb.presets - [SmartBulbPreset(index=0, brightness=50, hue=0, saturation=0, color_temp=2700), SmartBulbPreset(index=1, brightness=100, hue=0, saturation=75, color_temp=0), SmartBulbPreset(index=2, brightness=100, hue=120, saturation=75, color_temp=0), SmartBulbPreset(index=3, brightness=100, hue=240, saturation=75, color_temp=0)] + [SmartBulbPreset(index=0, brightness=50, hue=0, saturation=0, color_temp=2700, custom=None, id=None, mode=None), SmartBulbPreset(index=1, brightness=100, hue=0, saturation=75, color_temp=0, custom=None, id=None, mode=None), SmartBulbPreset(index=2, brightness=100, hue=120, saturation=75, color_temp=0, custom=None, id=None, mode=None), SmartBulbPreset(index=3, brightness=100, hue=240, saturation=75, color_temp=0, custom=None, id=None, mode=None)] To modify an existing preset, pass :class:`~kasa.smartbulb.SmartBulbPreset` instance to :func:`save_preset` method: @@ -531,5 +531,5 @@ async def save_preset(self, preset: SmartBulbPreset): raise SmartDeviceException("Invalid preset index") return await self._query_helper( - self.LIGHT_SERVICE, "set_preferred_state", preset.dict() + self.LIGHT_SERVICE, "set_preferred_state", preset.dict(exclude_none=True) ) From d23394c48d8a77cff09537a943bcc7afa439fc55 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sat, 1 Apr 2023 16:07:36 +0200 Subject: [PATCH 4/4] Add tests for save_preset payloads --- kasa/tests/test_bulb.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/kasa/tests/test_bulb.py b/kasa/tests/test_bulb.py index 019afaf56..f73a948b2 100644 --- a/kasa/tests/test_bulb.py +++ b/kasa/tests/test_bulb.py @@ -263,7 +263,7 @@ async def test_list_presets(dev: SmartBulb): @bulb async def test_modify_preset(dev: SmartBulb, mocker): - """Verify that modifying preset calls the and exceptions are raised properly.""" + """Verify that modifying preset calls the and exceptions are raised properly.""" if not dev.presets: pytest.skip("Some strips do not support presets") @@ -289,3 +289,27 @@ async def test_modify_preset(dev: SmartBulb, mocker): await dev.save_preset( SmartBulbPreset(index=5, hue=0, brightness=0, saturation=0, color_temp=0) ) + + +@bulb +@pytest.mark.parametrize( + ("preset", "payload"), + [ + ( + SmartBulbPreset(index=0, hue=0, brightness=1, saturation=0), + {"index": 0, "hue": 0, "brightness": 1, "saturation": 0}, + ), + ( + SmartBulbPreset(index=0, brightness=1, id="testid", mode=2, custom=0), + {"index": 0, "brightness": 1, "id": "testid", "mode": 2, "custom": 0}, + ), + ], +) +async def test_modify_preset_payloads(dev: SmartBulb, preset, payload, mocker): + """Test that modify preset payloads ignore none values.""" + if not dev.presets: + pytest.skip("Some strips do not support presets") + + query_helper = mocker.patch("kasa.SmartBulb._query_helper") + await dev.save_preset(preset) + query_helper.assert_called_with(dev.LIGHT_SERVICE, "set_preferred_state", payload)