From 3af5bbda9718b6773ad6ecff8816736d4000d7b5 Mon Sep 17 00:00:00 2001 From: Jakub Bartkowiak Date: Tue, 5 May 2020 22:11:15 +0200 Subject: [PATCH 1/3] new: support fade_speed in lights (wLightBoxS, dimmerBox) --- blebox_uniapi/box.py | 4 +-- blebox_uniapi/error.py | 3 ++ blebox_uniapi/light.py | 61 +++++++++++++++++++++++++++++++++++---- blebox_uniapi/products.py | 8 ++--- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/blebox_uniapi/box.py b/blebox_uniapi/box.py index f19580d..d54de76 100644 --- a/blebox_uniapi/box.py +++ b/blebox_uniapi/box.py @@ -232,8 +232,8 @@ def extract_version(self, type, device_info, min_supported, max_supported, level outdated = current < latest_version return (current, outdated) - async def async_api_command(self, command, value=None): - method, *args = self._api[command](value) + async def async_api_command(self, command, *args): + method, *args = self._api[command](*args) self._last_real_update = None # force update return await self._async_api(False, method, *args) diff --git a/blebox_uniapi/error.py b/blebox_uniapi/error.py index 7d5603b..86fce09 100644 --- a/blebox_uniapi/error.py +++ b/blebox_uniapi/error.py @@ -134,6 +134,9 @@ class BadOnValueError(BoxError): pass +class BadFadeSpeedValueError(BoxError): + pass + # misc errors diff --git a/blebox_uniapi/light.py b/blebox_uniapi/light.py index 5fcd054..e45d11c 100644 --- a/blebox_uniapi/light.py +++ b/blebox_uniapi/light.py @@ -1,16 +1,43 @@ +import bisect +from datetime import timedelta from .feature import Feature -from .error import BadOnValueError +from .error import BadOnValueError, BadFadeSpeedValueError class Light(Feature): # TODO: better defaults? + FADE_SPEED = [0, 6, 12, 12, 21, 24, 39, 48, 63, 75, 93, 105, 120, 156, 183, 222, 258, 309, 342, + 384, 441, 513, 615, 684, 768, 855, 1026, 1197, 1280, 1368, 1435, 1536, 1640, 1710, + 1792, 1845, 2048, 2304, 2560, 2736, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 9216, + 10240, 11264, 12288, 13312, 14336, 15360, 16384, 18432, 20480, 22528, 24576, 26624, + 28672, 30720, 32768, 34816, 36864, 38912, 40960, 43008, 45056, 47104, 49152, 51200, + 53248, 55296, 57344, 59392, 61440, 63488, 65536, 67584, 69632, 71680, 73728, 75776, + 77824, 79872, 81920, 84992, 88064, 91136, 93184, 94208, 96256, 97280, 99328, 100352, + 102400, 105472, 105472, 108544, 108544, 111616, 111616, 114688, 115712, 117760, 118784, + 120832, 121856, 122880, 123904, 125952, 125952, 128000, 132096, 136192, 139264, 142336, + 144384, 146432, 148480, 151552, 155648, 160768, 164864, 168960, 169984, 174080, 175104, + 178176, 184320, 189440, 195584, 199680, 200704, 205824, 206848, 211968, 218112, 224256, + 230400, 237568, 237568, 244736, 244736, 251904, 259072, 266240, 273408, 274432, 281600, + 282624, 290816, 299008, 308224, 316416, 318464, 325632, 327680, 336896, 346112, 356352, + 366592, 368640, 376832, 379904, 390144, 401408, 412672, 416768, 424960, 429056, 441344, + 453632, 466944, 472064, 480256, 485376, 499712, 514048, 528384, 535552, 542720, 550912, + 566272, 582656, 599040, 608256, 616448, 625664, 643072, 661504, 672768, 679936, 691200, + 711680, 731136, 744448, 752640, 765952, 787456, 809984, 825344, 832512, 860160, 873472, + 898048, 916480, 923648, 942080, 969728, 996352, 1018880, 1047552, 1077248, 1107968, + 1133568, 1138688, 1165312, 1198080, 1231872, 1262592, 1266688, 1297408, 1334272, + 1368064, 1372160, 1406976, 1445888, 1483776, 1486848, 1525760, 1568768, 1611776, + 1613824, 1656832, 1703936, 1751040, 1752064, 1800192, 1851392, 1903616, 1904640, + 1953792, 2013184, 2019328, 2070528, 2076672, 2135040, 2195456, 2320384, 2453504, + 2593792, 2710528, 3073024, 3614720] + CONFIG = { "wLightBox": { "default": "FFFFFFFF", "off": "00000000", "brightness?": False, + "fade_speed?": False, "white?": True, "color?": True, "to_value": lambda int_value: int_value, @@ -20,6 +47,7 @@ class Light(Feature): "default": "FF", "off": "00", "brightness?": True, + "fade_speed?": True, "white?": False, "color?": False, "to_value": lambda int_value: "{:02x}".format(int_value), @@ -31,6 +59,7 @@ class Light(Feature): "default": 0xFF, "off": 0x0, "brightness?": True, + "fade_speed?": True, "white?": False, "color?": False, "to_value": lambda int_value: int_value, @@ -76,6 +105,22 @@ def apply_brightness(self, value, brightness): return method(brightness) # ok since not implemented for rgbw + @property + def supports_fade_speed(self): + return self.CONFIG[self._product.type]["fade_speed?"] + + def apply_fade_speed(self, fade_speed): + if not isinstance(fade_speed, timedelta): + raise BadFadeSpeedValueError( + f"apply_fade_speed called with bad parameter ({fade_speed} is {type(fade_speed)} instead of timedelta)") + + if timedelta() > fade_speed > timedelta(hours=1): + raise BadFadeSpeedValueError( + f"apply_fade_speed called with bad parameter ({fade_speed} is not within range of 0..1h)") + + fade_speed = int(fade_speed / timedelta(milliseconds=1)) + return 255 - bisect.bisect_left(self.FADE_SPEED, fade_speed) + @property def supports_white(self): return self.CONFIG[self._product.type]["white?"] @@ -156,7 +201,7 @@ def sensible_on_value(self): def rgbw_hex(self): return self._desired - async def async_on(self, value): + async def async_on(self, value, fade_speed=None): if not isinstance(value, type(self._off_value)): raise BadOnValueError( f"turn_on called with bad parameter ({value} is {type(value)}, compared to {self._off_value} which is {type(self._off_value)})" @@ -165,7 +210,13 @@ async def async_on(self, value): if value == self._off_value: raise BadOnValueError(f"turn_on called with invalid value ({value})") - await self.async_api_command("set", value) + if fade_speed is not None: + if not isinstance(fade_speed, int): + raise BadFadeSpeedValueError(f"turn_on called with invalid fade_speed value type ({type(fade_speed)})") + if 0 > fade_speed > 255: + raise BadFadeSpeedValueError(f"turn_on called with fade_speed value out of range ({fade_speed})") + + await self.async_api_command("set", value, fade_speed) - async def async_off(self, **kwargs): - await self.async_api_command("set", self._off_value) + async def async_off(self, fade_speed=None): + await self.async_api_command("set", self._off_value, fade_speed) diff --git a/blebox_uniapi/products.py b/blebox_uniapi/products.py index ac729b0..5d8d792 100644 --- a/blebox_uniapi/products.py +++ b/blebox_uniapi/products.py @@ -28,10 +28,10 @@ class Products: "api_path": "/api/dimmer/state", "api_level_range": [20170829, 20170829], "api": { - "set": lambda x: ( + "set": lambda x, y: ( "POST", "/api/dimmer/set", - '{"dimmer":{"desiredBrightness": ' + str(x) + "}}", + f'{{"dimmer":{{"desiredBrightness": {str(x)}, "fadeSpeed": {"null" if y is None else str(y)}}}}}', ), }, "lights": [["brightness", {"desired": "dimmer/desiredBrightness"}]], @@ -192,10 +192,10 @@ class Products: "api_path": "/api/light/state", "api_level_range": [20180718, 20180718], "api": { - "set": lambda x: ( + "set": lambda x, y: ( "POST", "/api/light/set", - f'{{"light": {{"desiredColor": "{x}"}}}}', + f'{{"light": {{"desiredColor": "{x}", "fadeSpeed": {"null" if y is None else str(y)}}}}}', ) }, "lights": [["brightness", {"desired": "light/desiredColor"}]], From 17d4a9244e84bad669524814ad99bc2a0580f710 Mon Sep 17 00:00:00 2001 From: Jakub Bartkowiak Date: Wed, 6 May 2020 00:01:12 +0200 Subject: [PATCH 2/3] fix: unit tests for light are not passing --- tests/test_light.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_light.py b/tests/test_light.py index 873f0ff..c4d8363 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -276,7 +276,7 @@ async def allow_set_brightness(self, code, aioclient_mock, value, response): code, aioclient_mock, "/api/dimmer/set", - '{"dimmer":{"desiredBrightness": ' + str(value) + "}}", + '{"dimmer":{"desiredBrightness": ' + str(value) + ', "fadeSpeed": ' + json.dumps(None) + '}}', response, ) @@ -465,7 +465,7 @@ async def allow_set_brightness(self, code, aioclient_mock, value, response): code, aioclient_mock, "/api/light/set", - json.dumps({"light": {"desiredColor": raw}}), + json.dumps({"light": {"desiredColor": raw, "fadeSpeed": None}}), response, ) @@ -680,7 +680,7 @@ async def allow_set_color(self, code, aioclient_mock, value, response): code, aioclient_mock, "/api/rgbw/set", - '{"rgbw":{"desiredColor": "' + str(value) + '"}}', + '{"rgbw":{"desiredColor": "' + str(value) + '", "fadeSpeed": ' + json.dumps(None) + '}}', response, ) From fdc53b0fbfd83908be312d8fa012c2e224e8e46b Mon Sep 17 00:00:00 2001 From: Jakub Bartkowiak Date: Wed, 6 May 2020 00:52:33 +0200 Subject: [PATCH 3/3] fix: check if fade_speed is supported (wLightBox) --- blebox_uniapi/light.py | 26 ++++++++++++++++++-------- tests/test_light.py | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/blebox_uniapi/light.py b/blebox_uniapi/light.py index e45d11c..e032ce7 100644 --- a/blebox_uniapi/light.py +++ b/blebox_uniapi/light.py @@ -210,13 +210,23 @@ async def async_on(self, value, fade_speed=None): if value == self._off_value: raise BadOnValueError(f"turn_on called with invalid value ({value})") - if fade_speed is not None: - if not isinstance(fade_speed, int): - raise BadFadeSpeedValueError(f"turn_on called with invalid fade_speed value type ({type(fade_speed)})") - if 0 > fade_speed > 255: - raise BadFadeSpeedValueError(f"turn_on called with fade_speed value out of range ({fade_speed})") - - await self.async_api_command("set", value, fade_speed) + if not self.supports_fade_speed: + await self.async_api_command("set", value) + else: + self.validate_fade_speed(fade_speed) + await self.async_api_command("set", value, fade_speed) async def async_off(self, fade_speed=None): - await self.async_api_command("set", self._off_value, fade_speed) + if not self.supports_fade_speed: + await self.async_api_command("set", self._off_value) + else: + self.validate_fade_speed(fade_speed) + await self.async_api_command("set", self._off_value, fade_speed) + + def validate_fade_speed(self, fade_speed): + if fade_speed is None: + return + if not isinstance(fade_speed, int): + raise BadFadeSpeedValueError(f"turn_on called with invalid fade_speed value type ({type(fade_speed)})") + if 0 > fade_speed > 255: + raise BadFadeSpeedValueError(f"turn_on called with fade_speed value out of range ({fade_speed})") diff --git a/tests/test_light.py b/tests/test_light.py index c4d8363..e673d77 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -680,7 +680,7 @@ async def allow_set_color(self, code, aioclient_mock, value, response): code, aioclient_mock, "/api/rgbw/set", - '{"rgbw":{"desiredColor": "' + str(value) + '", "fadeSpeed": ' + json.dumps(None) + '}}', + '{"rgbw":{"desiredColor": "' + str(value) + '"}}', response, )