diff --git a/blebox_uniapi/light.py b/blebox_uniapi/light.py index 2b64b9f..c99c9b2 100644 --- a/blebox_uniapi/light.py +++ b/blebox_uniapi/light.py @@ -280,6 +280,23 @@ def evaluate_brightness_from_rgb(iterable: Sequence[int]) -> int: ) return int(max(iterable)) + @staticmethod + def swap_ww_cw(value: list) -> list: + """Swap WW and CW channels in RGBWW mode for API compatibility.""" + if len(value) == 5: + value = list(value) + value[3], value[4] = value[4], value[3] + return value + + @staticmethod + def scale_rgbww_brightness(value: list, brightness: int) -> list: + """Scale RGBWW value preserving WW:CW ratio.""" + current_max = max(value) + if current_max > 0: + return [round(x * brightness / current_max) for x in value] + else: + return [0, 0, 0, 0, brightness] + def apply_brightness(self, value: int, brightness: int) -> Any: """Return list of values with applied brightness.""" if not isinstance(brightness, int): @@ -303,7 +320,11 @@ def apply_brightness(self, value: int, brightness: int) -> Any: BleboxColorMode.RGBorW, ): value = self.normalise_elements_of_rgb(list(value)) - res = list(map(lambda x: round(x * (brightness / 255)), value)) + res = list(map(lambda x: round(x * (brightness / 255)), value)) + elif self.color_mode == BleboxColorMode.RGBWW: + res = self.scale_rgbww_brightness(value, brightness) + else: + res = list(map(lambda x: round(x * (brightness / 255)), value)) return res def evaluate_off_value(self, config: dict, raw_hex: str) -> str: @@ -544,6 +565,9 @@ def sensible_on_value(self) -> Any: return self.rgb_hex_to_rgb_list(self._last_on_state[:6]) elif self.color_mode == BleboxColorMode.MONO: return self._last_on_state + elif self.color_mode == BleboxColorMode.RGBWW: + result = self.rgb_hex_to_rgb_list(self._last_on_state) + return self.swap_ww_cw(result) else: return self.rgb_hex_to_rgb_list(self._last_on_state) else: diff --git a/tests/test_light.py b/tests/test_light.py index dcce6a9..96d2fb0 100644 --- a/tests/test_light.py +++ b/tests/test_light.py @@ -1337,3 +1337,33 @@ def mask(x): light._set_last_on_value("test_alias", box, light._off_value) box.expect_rgbw.assert_not_called() assert light._last_on_state == light._default_on_value + + +def test_unit_light_swap_ww_cw(): + """Test swap_ww_cw preserves first 3 channels and swaps last 2.""" + value = [100, 150, 200, 120, 80] + result = Light.swap_ww_cw(value) + assert result == [100, 150, 200, 80, 120] + + +def test_unit_light_swap_ww_cw_non_5_channel(): + """Test swap_ww_cw returns unchanged for non-5-channel values.""" + value = [100, 150, 200] + result = Light.swap_ww_cw(value) + assert result == [100, 150, 200] + + +def test_unit_light_scale_rgbww_brightness_preserves_ratio(): + """Test scale_rgbww_brightness preserves WW:CW ratio within rounding tolerance.""" + value = [100, 100, 100, 120, 80] + result = Light.scale_rgbww_brightness(value, 100) + expected_ratio = 120 / 80 + actual_ratio = result[3] / result[4] + assert abs(actual_ratio - expected_ratio) < 0.01 + + +def test_unit_light_scale_rgbww_brightness_zero(): + """Test scale_rgbww_brightness with zero max.""" + value = [0, 0, 0, 0, 0] + result = Light.scale_rgbww_brightness(value, 200) + assert result == [0, 0, 0, 0, 200]