From 9e16be31738187146b7fc11b822f36fbb607f781 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen <amelchio@nogoto.net> Date: Sun, 11 Jun 2017 21:19:58 +0200 Subject: [PATCH] LIFX: clean up internal color conversions (#7964) * Add color_util.color_hsv_to_RGB * Use helper functions for LIFX conversions The LIFX API uses 16 bits for saturation/brightness while HA uses 8 bits. Using helper functions makes the conversion a bit nicer and less prone to off-by-one issues. The colorsys library uses 0.0-1.0 but we can avoid that by using the HA color_util converters instead. --- .../components/light/lifx/__init__.py | 45 ++++++++----------- homeassistant/util/color.py | 7 +++ tests/util/test_color.py | 17 +++++++ 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/homeassistant/components/light/lifx/__init__.py b/homeassistant/components/light/lifx/__init__.py index 1a0e8d7d9ed..3bd53c425b9 100644 --- a/homeassistant/components/light/lifx/__init__.py +++ b/homeassistant/components/light/lifx/__init__.py @@ -4,7 +4,6 @@ Support for the LIFX platform that implements lights. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/light.lifx/ """ -import colorsys import logging import asyncio import sys @@ -24,8 +23,6 @@ from homeassistant.components.light import ( SUPPORT_XY_COLOR, SUPPORT_TRANSITION, SUPPORT_EFFECT, preprocess_turn_on_alternatives) from homeassistant.config import load_yaml_config_file -from homeassistant.util.color import ( - color_temperature_mired_to_kelvin, color_temperature_kelvin_to_mired) from homeassistant import util from homeassistant.core import callback from homeassistant.helpers.event import async_track_point_in_utc_time @@ -51,9 +48,6 @@ SERVICE_LIFX_SET_STATE = 'lifx_set_state' ATTR_HSBK = 'hsbk' ATTR_POWER = 'power' -BYTE_MAX = 255 -SHORT_MAX = 65535 - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_SERVER, default='0.0.0.0'): cv.string, }) @@ -200,15 +194,14 @@ class AwaitAioLIFX: return self.message -def convert_rgb_to_hsv(rgb): - """Convert Home Assistant RGB values to HSV values.""" - red, green, blue = [_ / BYTE_MAX for _ in rgb] +def convert_8_to_16(value): + """Scale an 8 bit level into 16 bits.""" + return (value << 8) | value - hue, saturation, brightness = colorsys.rgb_to_hsv(red, green, blue) - return [int(hue * SHORT_MAX), - int(saturation * SHORT_MAX), - int(brightness * SHORT_MAX)] +def convert_16_to_8(value): + """Scale a 16 bit level into 8 bits.""" + return value >> 8 class LIFXLight(Light): @@ -260,14 +253,14 @@ class LIFXLight(Light): @property def brightness(self): """Return the brightness of this light between 0..255.""" - brightness = int(self._bri / (BYTE_MAX + 1)) + brightness = convert_16_to_8(self._bri) _LOGGER.debug("brightness: %d", brightness) return brightness @property def color_temp(self): """Return the color temperature.""" - temperature = color_temperature_kelvin_to_mired(self._kel) + temperature = color_util.color_temperature_kelvin_to_mired(self._kel) _LOGGER.debug("color_temp: %d", temperature) return temperature @@ -280,7 +273,7 @@ class LIFXLight(Light): kelvin = 6500 else: kelvin = 9000 - return math.floor(color_temperature_kelvin_to_mired(kelvin)) + return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin)) @property def max_mireds(self): @@ -290,7 +283,7 @@ class LIFXLight(Light): kelvin = 2700 else: kelvin = 2500 - return math.ceil(color_temperature_kelvin_to_mired(kelvin)) + return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin)) @property def is_on(self): @@ -446,7 +439,9 @@ class LIFXLight(Light): if ATTR_RGB_COLOR in kwargs: hue, saturation, brightness = \ - convert_rgb_to_hsv(kwargs[ATTR_RGB_COLOR]) + color_util.color_RGB_to_hsv(*kwargs[ATTR_RGB_COLOR]) + saturation = convert_8_to_16(saturation) + brightness = convert_8_to_16(brightness) changed_color = True else: hue = self._hue @@ -455,12 +450,12 @@ class LIFXLight(Light): if ATTR_XY_COLOR in kwargs: hue, saturation = color_util.color_xy_to_hs(*kwargs[ATTR_XY_COLOR]) - saturation = saturation * (BYTE_MAX + 1) + saturation = convert_8_to_16(saturation) changed_color = True # When color or temperature is set, use a default value for the other if ATTR_COLOR_TEMP in kwargs: - kelvin = int(color_temperature_mired_to_kelvin( + kelvin = int(color_util.color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP])) if not changed_color: saturation = 0 @@ -472,7 +467,7 @@ class LIFXLight(Light): kelvin = self._kel if ATTR_BRIGHTNESS in kwargs: - brightness = kwargs[ATTR_BRIGHTNESS] * (BYTE_MAX + 1) + brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) changed_color = True else: brightness = self._bri @@ -491,12 +486,8 @@ class LIFXLight(Light): self._bri = bri self._kel = kel - red, green, blue = colorsys.hsv_to_rgb( - hue / SHORT_MAX, sat / SHORT_MAX, bri / SHORT_MAX) - - red = int(red * BYTE_MAX) - green = int(green * BYTE_MAX) - blue = int(blue * BYTE_MAX) + red, green, blue = color_util.color_hsv_to_RGB( + hue, convert_16_to_8(sat), convert_16_to_8(bri)) _LOGGER.debug("set_color: %d %d %d %d [%d %d %d]", hue, sat, bri, kel, red, green, blue) diff --git a/homeassistant/util/color.py b/homeassistant/util/color.py index 396b8a63601..d76816cfbb8 100644 --- a/homeassistant/util/color.py +++ b/homeassistant/util/color.py @@ -264,6 +264,13 @@ def color_RGB_to_hsv(iR: int, iG: int, iB: int) -> Tuple[int, int, int]: return (int(fHSV[0]*65536), int(fHSV[1]*255), int(fHSV[2]*255)) +# pylint: disable=invalid-sequence-index +def color_hsv_to_RGB(iH: int, iS: int, iV: int) -> Tuple[int, int, int]: + """Convert an hsv color into its rgb representation.""" + fRGB = colorsys.hsv_to_rgb(iH/65536, iS/255, iV/255) + return (int(fRGB[0]*255), int(fRGB[1]*255), int(fRGB[2]*255)) + + # pylint: disable=invalid-sequence-index def color_xy_to_hs(vX: float, vY: float) -> Tuple[int, int]: """Convert an xy color to its hs representation.""" diff --git a/tests/util/test_color.py b/tests/util/test_color.py index 43b5904a895..dfb2cd0733c 100644 --- a/tests/util/test_color.py +++ b/tests/util/test_color.py @@ -56,6 +56,23 @@ class TestColorUtil(unittest.TestCase): self.assertEqual((0, 255, 255), color_util.color_RGB_to_hsv(255, 0, 0)) + def test_color_hsv_to_RGB(self): + """Test color_RGB_to_hsv.""" + self.assertEqual((0, 0, 0), + color_util.color_hsv_to_RGB(0, 0, 0)) + + self.assertEqual((255, 255, 255), + color_util.color_hsv_to_RGB(0, 0, 255)) + + self.assertEqual((0, 0, 255), + color_util.color_hsv_to_RGB(43690, 255, 255)) + + self.assertEqual((0, 255, 0), + color_util.color_hsv_to_RGB(21845, 255, 255)) + + self.assertEqual((255, 0, 0), + color_util.color_hsv_to_RGB(0, 255, 255)) + def test_color_xy_to_hs(self): """Test color_xy_to_hs.""" self.assertEqual((8609, 255),