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.
This commit is contained in:
Anders Melchiorsen 2017-06-11 21:19:58 +02:00 committed by GitHub
parent 1b1619fbf1
commit 9e16be3173
3 changed files with 42 additions and 27 deletions

View File

@ -4,7 +4,6 @@ Support for the LIFX platform that implements lights.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.lifx/ https://home-assistant.io/components/light.lifx/
""" """
import colorsys
import logging import logging
import asyncio import asyncio
import sys import sys
@ -24,8 +23,6 @@ from homeassistant.components.light import (
SUPPORT_XY_COLOR, SUPPORT_TRANSITION, SUPPORT_EFFECT, SUPPORT_XY_COLOR, SUPPORT_TRANSITION, SUPPORT_EFFECT,
preprocess_turn_on_alternatives) preprocess_turn_on_alternatives)
from homeassistant.config import load_yaml_config_file 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 import util
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.event import async_track_point_in_utc_time 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_HSBK = 'hsbk'
ATTR_POWER = 'power' ATTR_POWER = 'power'
BYTE_MAX = 255
SHORT_MAX = 65535
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SERVER, default='0.0.0.0'): cv.string, vol.Optional(CONF_SERVER, default='0.0.0.0'): cv.string,
}) })
@ -200,15 +194,14 @@ class AwaitAioLIFX:
return self.message return self.message
def convert_rgb_to_hsv(rgb): def convert_8_to_16(value):
"""Convert Home Assistant RGB values to HSV values.""" """Scale an 8 bit level into 16 bits."""
red, green, blue = [_ / BYTE_MAX for _ in rgb] return (value << 8) | value
hue, saturation, brightness = colorsys.rgb_to_hsv(red, green, blue)
return [int(hue * SHORT_MAX), def convert_16_to_8(value):
int(saturation * SHORT_MAX), """Scale a 16 bit level into 8 bits."""
int(brightness * SHORT_MAX)] return value >> 8
class LIFXLight(Light): class LIFXLight(Light):
@ -260,14 +253,14 @@ class LIFXLight(Light):
@property @property
def brightness(self): def brightness(self):
"""Return the brightness of this light between 0..255.""" """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) _LOGGER.debug("brightness: %d", brightness)
return brightness return brightness
@property @property
def color_temp(self): def color_temp(self):
"""Return the color temperature.""" """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) _LOGGER.debug("color_temp: %d", temperature)
return temperature return temperature
@ -280,7 +273,7 @@ class LIFXLight(Light):
kelvin = 6500 kelvin = 6500
else: else:
kelvin = 9000 kelvin = 9000
return math.floor(color_temperature_kelvin_to_mired(kelvin)) return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin))
@property @property
def max_mireds(self): def max_mireds(self):
@ -290,7 +283,7 @@ class LIFXLight(Light):
kelvin = 2700 kelvin = 2700
else: else:
kelvin = 2500 kelvin = 2500
return math.ceil(color_temperature_kelvin_to_mired(kelvin)) return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin))
@property @property
def is_on(self): def is_on(self):
@ -446,7 +439,9 @@ class LIFXLight(Light):
if ATTR_RGB_COLOR in kwargs: if ATTR_RGB_COLOR in kwargs:
hue, saturation, brightness = \ 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 changed_color = True
else: else:
hue = self._hue hue = self._hue
@ -455,12 +450,12 @@ class LIFXLight(Light):
if ATTR_XY_COLOR in kwargs: if ATTR_XY_COLOR in kwargs:
hue, saturation = color_util.color_xy_to_hs(*kwargs[ATTR_XY_COLOR]) 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 changed_color = True
# When color or temperature is set, use a default value for the other # When color or temperature is set, use a default value for the other
if ATTR_COLOR_TEMP in kwargs: 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])) kwargs[ATTR_COLOR_TEMP]))
if not changed_color: if not changed_color:
saturation = 0 saturation = 0
@ -472,7 +467,7 @@ class LIFXLight(Light):
kelvin = self._kel kelvin = self._kel
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
brightness = kwargs[ATTR_BRIGHTNESS] * (BYTE_MAX + 1) brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS])
changed_color = True changed_color = True
else: else:
brightness = self._bri brightness = self._bri
@ -491,12 +486,8 @@ class LIFXLight(Light):
self._bri = bri self._bri = bri
self._kel = kel self._kel = kel
red, green, blue = colorsys.hsv_to_rgb( red, green, blue = color_util.color_hsv_to_RGB(
hue / SHORT_MAX, sat / SHORT_MAX, bri / SHORT_MAX) hue, convert_16_to_8(sat), convert_16_to_8(bri))
red = int(red * BYTE_MAX)
green = int(green * BYTE_MAX)
blue = int(blue * BYTE_MAX)
_LOGGER.debug("set_color: %d %d %d %d [%d %d %d]", _LOGGER.debug("set_color: %d %d %d %d [%d %d %d]",
hue, sat, bri, kel, red, green, blue) hue, sat, bri, kel, red, green, blue)

View File

@ -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)) 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 # pylint: disable=invalid-sequence-index
def color_xy_to_hs(vX: float, vY: float) -> Tuple[int, int]: def color_xy_to_hs(vX: float, vY: float) -> Tuple[int, int]:
"""Convert an xy color to its hs representation.""" """Convert an xy color to its hs representation."""

View File

@ -56,6 +56,23 @@ class TestColorUtil(unittest.TestCase):
self.assertEqual((0, 255, 255), self.assertEqual((0, 255, 255),
color_util.color_RGB_to_hsv(255, 0, 0)) 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): def test_color_xy_to_hs(self):
"""Test color_xy_to_hs.""" """Test color_xy_to_hs."""
self.assertEqual((8609, 255), self.assertEqual((8609, 255),