mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 00:37:53 +00:00
Move Aeotec bulb color logic to Zwave workaround
Default behavior for warm/cold white channels is to assume the white channel is mixed with the rgb. This is a sane default and should support the Fibaro RGBW LED controller.
This commit is contained in:
parent
3c5c018e3e
commit
6477122b23
@ -14,16 +14,27 @@ from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \
|
||||
from homeassistant.components import zwave
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.util.color import HASS_COLOR_MAX, HASS_COLOR_MIN, \
|
||||
color_temperature_mired_to_kelvin, color_temperature_to_rgb
|
||||
color_temperature_mired_to_kelvin, color_temperature_to_rgb, \
|
||||
color_rgb_to_rgbw, color_rgbw_to_rgb
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
AEOTEC = 0x86
|
||||
AEOTEC_ZW098_LED_BULB = 0x62
|
||||
AEOTEC_ZW098_LED_BULB_LIGHT = (AEOTEC, AEOTEC_ZW098_LED_BULB)
|
||||
|
||||
COLOR_CHANNEL_WARM_WHITE = 0x01
|
||||
COLOR_CHANNEL_COLD_WHITE = 0x02
|
||||
COLOR_CHANNEL_RED = 0x04
|
||||
COLOR_CHANNEL_GREEN = 0x08
|
||||
COLOR_CHANNEL_BLUE = 0x10
|
||||
|
||||
WORKAROUND_ZW098 = 'zw098'
|
||||
|
||||
DEVICE_MAPPINGS = {
|
||||
AEOTEC_ZW098_LED_BULB_LIGHT: WORKAROUND_ZW098
|
||||
}
|
||||
|
||||
# Generate midpoint color temperatures for bulbs that have limited
|
||||
# support for white light colors
|
||||
TEMP_MID_HASS = (HASS_COLOR_MAX - HASS_COLOR_MIN) / 2 + HASS_COLOR_MIN
|
||||
@ -161,6 +172,7 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
self._color_channels = None
|
||||
self._rgb = None
|
||||
self._ct = None
|
||||
self._zw098 = None
|
||||
|
||||
# Here we attempt to find a zwave color value with the same instance
|
||||
# id as the dimmer value. Currently zwave nodes that change colors
|
||||
@ -182,6 +194,17 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
if self._value_color_channels is None:
|
||||
raise ValueError("Color Channels not found.")
|
||||
|
||||
# Make sure that we have values for the key before converting to int
|
||||
if (value.node.manufacturer_id.strip() and
|
||||
value.node.product_id.strip()):
|
||||
specific_sensor_key = (int(value.node.manufacturer_id, 16),
|
||||
int(value.node.product_id, 16))
|
||||
|
||||
if specific_sensor_key in DEVICE_MAPPINGS:
|
||||
if DEVICE_MAPPINGS[specific_sensor_key] == WORKAROUND_ZW098:
|
||||
_LOGGER.debug("AEOTEC ZW098 workaround enabled")
|
||||
self._zw098 = 1
|
||||
|
||||
super().__init__(value)
|
||||
|
||||
def update_properties(self):
|
||||
@ -218,11 +241,10 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
else:
|
||||
cold_white = 0
|
||||
|
||||
# Color temperature. With two white channels, only two color
|
||||
# temperatures are supported for the bulb. The channel values
|
||||
# Color temperature. With the AEOTEC ZW098 bulb, only two color
|
||||
# temperatures are supported. The warm and cold channel values
|
||||
# indicate brightness for warm/cold color temperature.
|
||||
if (self._color_channels & COLOR_CHANNEL_WARM_WHITE and
|
||||
self._color_channels & COLOR_CHANNEL_COLD_WHITE):
|
||||
if self._zw098:
|
||||
if warm_white > 0:
|
||||
self._ct = TEMP_WARM_HASS
|
||||
self._rgb = ct_to_rgb(self._ct)
|
||||
@ -233,17 +255,11 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
# RGB color is being used. Just report midpoint.
|
||||
self._ct = TEMP_MID_HASS
|
||||
|
||||
# If only warm white is reported 0-255 is color temperature.
|
||||
elif self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
||||
self._ct = HASS_COLOR_MIN + (HASS_COLOR_MAX - HASS_COLOR_MIN) * (
|
||||
warm_white / 255)
|
||||
self._rgb = ct_to_rgb(self._ct)
|
||||
self._rgb = list(color_rgbw_to_rgb(*self._rgb, w=warm_white))
|
||||
|
||||
# If only cold white is reported 0-255 is negative color temperature.
|
||||
elif self._color_channels & COLOR_CHANNEL_COLD_WHITE:
|
||||
self._ct = HASS_COLOR_MIN + (HASS_COLOR_MAX - HASS_COLOR_MIN) * (
|
||||
(255 - cold_white) / 255)
|
||||
self._rgb = ct_to_rgb(self._ct)
|
||||
self._rgb = list(color_rgbw_to_rgb(*self._rgb, w=cold_white))
|
||||
|
||||
# If no rgb channels supported, report None.
|
||||
if not (self._color_channels & COLOR_CHANNEL_RED or
|
||||
@ -266,10 +282,10 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
rgbw = None
|
||||
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
# With two white channels, only two color temperatures are
|
||||
# supported for the bulb.
|
||||
if (self._color_channels & COLOR_CHANNEL_WARM_WHITE and
|
||||
self._color_channels & COLOR_CHANNEL_COLD_WHITE):
|
||||
# Color temperature. With the AEOTEC ZW098 bulb, only two color
|
||||
# temperatures are supported. The warm and cold channel values
|
||||
# indicate brightness for warm/cold color temperature.
|
||||
if self._zw098:
|
||||
if kwargs[ATTR_COLOR_TEMP] > TEMP_MID_HASS:
|
||||
self._ct = TEMP_WARM_HASS
|
||||
rgbw = b'#000000FF00'
|
||||
@ -277,29 +293,20 @@ class ZwaveColorLight(ZwaveDimmer):
|
||||
self._ct = TEMP_COLD_HASS
|
||||
rgbw = b'#00000000FF'
|
||||
|
||||
# If only warm white is reported 0-255 is color temperature
|
||||
elif self._color_channels & COLOR_CHANNEL_WARM_WHITE:
|
||||
rgbw = b'#000000'
|
||||
temp = (
|
||||
(kwargs[ATTR_COLOR_TEMP] - HASS_COLOR_MIN) /
|
||||
(HASS_COLOR_MAX - HASS_COLOR_MIN) * 255)
|
||||
rgbw += format(int(temp)).encode('utf-8')
|
||||
|
||||
# If only cold white is reported 0-255 is negative color temp
|
||||
elif self._color_channels & COLOR_CHANNEL_COLD_WHITE:
|
||||
rgbw = b'#000000'
|
||||
temp = (
|
||||
255 - (kwargs[ATTR_COLOR_TEMP] - HASS_COLOR_MIN) /
|
||||
(HASS_COLOR_MAX - HASS_COLOR_MIN) * 255)
|
||||
rgbw += format(int(temp)).encode('utf-8')
|
||||
|
||||
elif ATTR_RGB_COLOR in kwargs:
|
||||
self._rgb = kwargs[ATTR_RGB_COLOR]
|
||||
|
||||
rgbw = b'#'
|
||||
for colorval in self._rgb:
|
||||
rgbw += format(colorval, '02x').encode('utf-8')
|
||||
rgbw += b'0000'
|
||||
if (not self._zw098 and (
|
||||
self._color_channels & COLOR_CHANNEL_WARM_WHITE or
|
||||
self._color_channels & COLOR_CHANNEL_COLD_WHITE)):
|
||||
rgbw = b'#'
|
||||
for colorval in color_rgb_to_rgbw(*self._rgb):
|
||||
rgbw += format(colorval, '02x').encode('utf-8')
|
||||
rgbw += b'00'
|
||||
else:
|
||||
rgbw = b'#'
|
||||
for colorval in self._rgb:
|
||||
rgbw += format(colorval, '02x').encode('utf-8')
|
||||
rgbw += b'0000'
|
||||
|
||||
if rgbw is None:
|
||||
_LOGGER.warning("rgbw string was not generated for turn_on")
|
||||
|
@ -112,6 +112,39 @@ def color_xy_brightness_to_RGB(vX, vY, brightness):
|
||||
return (r, g, b)
|
||||
|
||||
|
||||
def _match_max_scale(input_colors, output_colors):
|
||||
"""Match the maximum value of the output to the input."""
|
||||
max_in = max(input_colors)
|
||||
max_out = max(output_colors)
|
||||
if max_out == 0:
|
||||
factor = 0
|
||||
else:
|
||||
factor = max_in / max_out
|
||||
return tuple(int(round(i * factor)) for i in output_colors)
|
||||
|
||||
|
||||
def color_rgb_to_rgbw(r, g, b):
|
||||
"""Convert an rgb color to an rgbw representation."""
|
||||
# Calculate the white channel as the minimum of input rgb channels.
|
||||
# Subtract the white portion from the remaining rgb channels.
|
||||
w = min(r, g, b)
|
||||
rgbw = (r - w, g - w, b - w, w)
|
||||
|
||||
# Match the output maximum value to the input. This ensures the full
|
||||
# channel range is used.
|
||||
return _match_max_scale((r, g, b), rgbw)
|
||||
|
||||
|
||||
def color_rgbw_to_rgb(r, g, b, w):
|
||||
"""Convert an rgbw color to an rgb representation."""
|
||||
# Add the white channel back into the rgb channels.
|
||||
rgb = (r + w, g + w, b + w)
|
||||
|
||||
# Match the output maximum value to the input. This ensures the the
|
||||
# output doesn't overflow.
|
||||
return _match_max_scale((r, g, b, w), rgb)
|
||||
|
||||
|
||||
def rgb_hex_to_rgb_list(hex_string):
|
||||
"""Return an RGB color value list from a hex color string."""
|
||||
return [int(hex_string[i:i + len(hex_string) // 3], 16)
|
||||
|
@ -75,6 +75,58 @@ class TestColorUtil(unittest.TestCase):
|
||||
self.assertEqual((255, 255, 255),
|
||||
color_util.color_name_to_rgb('not a color'))
|
||||
|
||||
def test_color_rgb_to_rgbw(self):
|
||||
"""Test color_rgb_to_rgbw."""
|
||||
self.assertEqual((0, 0, 0, 0),
|
||||
color_util.color_rgb_to_rgbw(0, 0, 0))
|
||||
|
||||
self.assertEqual((0, 0, 0, 255),
|
||||
color_util.color_rgb_to_rgbw(255, 255, 255))
|
||||
|
||||
self.assertEqual((255, 0, 0, 0),
|
||||
color_util.color_rgb_to_rgbw(255, 0, 0))
|
||||
|
||||
self.assertEqual((0, 255, 0, 0),
|
||||
color_util.color_rgb_to_rgbw(0, 255, 0))
|
||||
|
||||
self.assertEqual((0, 0, 255, 0),
|
||||
color_util.color_rgb_to_rgbw(0, 0, 255))
|
||||
|
||||
self.assertEqual((255, 127, 0, 0),
|
||||
color_util.color_rgb_to_rgbw(255, 127, 0))
|
||||
|
||||
self.assertEqual((255, 0, 0, 253),
|
||||
color_util.color_rgb_to_rgbw(255, 127, 127))
|
||||
|
||||
self.assertEqual((0, 0, 0, 127),
|
||||
color_util.color_rgb_to_rgbw(127, 127, 127))
|
||||
|
||||
def test_color_rgbw_to_rgb(self):
|
||||
"""Test color_rgbw_to_rgb."""
|
||||
self.assertEqual((0, 0, 0),
|
||||
color_util.color_rgbw_to_rgb(0, 0, 0, 0))
|
||||
|
||||
self.assertEqual((255, 255, 255),
|
||||
color_util.color_rgbw_to_rgb(0, 0, 0, 255))
|
||||
|
||||
self.assertEqual((255, 0, 0),
|
||||
color_util.color_rgbw_to_rgb(255, 0, 0, 0))
|
||||
|
||||
self.assertEqual((0, 255, 0),
|
||||
color_util.color_rgbw_to_rgb(0, 255, 0, 0))
|
||||
|
||||
self.assertEqual((0, 0, 255),
|
||||
color_util.color_rgbw_to_rgb(0, 0, 255, 0))
|
||||
|
||||
self.assertEqual((255, 127, 0),
|
||||
color_util.color_rgbw_to_rgb(255, 127, 0, 0))
|
||||
|
||||
self.assertEqual((255, 127, 127),
|
||||
color_util.color_rgbw_to_rgb(255, 0, 0, 253))
|
||||
|
||||
self.assertEqual((127, 127, 127),
|
||||
color_util.color_rgbw_to_rgb(0, 0, 0, 127))
|
||||
|
||||
|
||||
class ColorTemperatureMiredToKelvinTests(unittest.TestCase):
|
||||
"""Test color_temperature_mired_to_kelvin."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user