diff --git a/homeassistant/components/zwave/light.py b/homeassistant/components/zwave/light.py index 140f601b1d9..c03ac9d229e 100644 --- a/homeassistant/components/zwave/light.py +++ b/homeassistant/components/zwave/light.py @@ -5,21 +5,20 @@ from threading import Timer from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, - ATTR_HS_COLOR, + ATTR_RGB_COLOR, + ATTR_RGBW_COLOR, ATTR_TRANSITION, - ATTR_WHITE_VALUE, + COLOR_MODE_BRIGHTNESS, + COLOR_MODE_COLOR_TEMP, + COLOR_MODE_RGB, + COLOR_MODE_RGBW, DOMAIN, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, SUPPORT_TRANSITION, - SUPPORT_WHITE_VALUE, LightEntity, ) from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -import homeassistant.util.color as color_util from . import CONF_REFRESH_DELAY, CONF_REFRESH_VALUE, ZWaveDeviceEntity, const @@ -108,16 +107,6 @@ def byte_to_zwave_brightness(value): return 0 -def ct_to_hs(temp): - """Convert color temperature (mireds) to hs.""" - colorlist = list( - color_util.color_temperature_to_hs( - color_util.color_temperature_mired_to_kelvin(temp) - ) - ) - return [int(val) for val in colorlist] - - class ZwaveDimmer(ZWaveDeviceEntity, LightEntity): """Representation of a Z-Wave dimmer.""" @@ -126,6 +115,8 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity): ZWaveDeviceEntity.__init__(self, values, DOMAIN) self._brightness = None self._state = None + self._color_mode = None + self._supported_color_modes = set() self._supported_features = None self._delay = delay self._refresh_value = refresh @@ -161,9 +152,10 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity): def value_added(self): """Call when a new value is added to this entity.""" - self._supported_features = SUPPORT_BRIGHTNESS + self._supported_color_modes = {COLOR_MODE_BRIGHTNESS} + self._color_mode = COLOR_MODE_BRIGHTNESS if self.values.dimming_duration is not None: - self._supported_features |= SUPPORT_TRANSITION + self._supported_features = SUPPORT_TRANSITION def value_changed(self): """Call when a value for this entity's node has changed.""" @@ -195,6 +187,16 @@ class ZwaveDimmer(ZWaveDeviceEntity, LightEntity): """Return true if device is on.""" return self._state == STATE_ON + @property + def color_mode(self): + """Return the current color mode.""" + return self._color_mode + + @property + def supported_color_modes(self): + """Flag supported color modes.""" + return self._supported_color_modes + @property def supported_features(self): """Flag supported features.""" @@ -260,7 +262,7 @@ class ZwaveColorLight(ZwaveDimmer): def __init__(self, values, refresh, delay): """Initialize the light.""" self._color_channels = None - self._hs = None + self._rgb = None self._ct = None self._white = None @@ -268,15 +270,18 @@ class ZwaveColorLight(ZwaveDimmer): def value_added(self): """Call when a new value is added to this entity.""" - super().value_added() + if self.values.dimming_duration is not None: + self._supported_features = SUPPORT_TRANSITION - self._supported_features |= SUPPORT_COLOR + self._supported_color_modes = {COLOR_MODE_RGB} + self._color_mode = COLOR_MODE_RGB if self._zw098: - self._supported_features |= SUPPORT_COLOR_TEMP + self._supported_color_modes.add(COLOR_MODE_COLOR_TEMP) elif self._color_channels is not None and self._color_channels & ( COLOR_CHANNEL_WARM_WHITE | COLOR_CHANNEL_COLD_WHITE ): - self._supported_features |= SUPPORT_WHITE_VALUE + self._supported_color_modes = {COLOR_MODE_RGBW} + self._color_mode = COLOR_MODE_RGBW def update_properties(self): """Update internal properties based on zwave values.""" @@ -294,8 +299,7 @@ class ZwaveColorLight(ZwaveDimmer): data = self.values.color.data # RGB is always present in the openzwave color data string. - rgb = [int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)] - self._hs = color_util.color_RGB_to_hs(*rgb) + self._rgb = (int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)) # Parse remaining color channels. Openzwave appends white channels # that are present. @@ -321,13 +325,12 @@ class ZwaveColorLight(ZwaveDimmer): if self._zw098: if warm_white > 0: self._ct = TEMP_WARM_HASS - self._hs = ct_to_hs(self._ct) + self._color_mode = COLOR_MODE_COLOR_TEMP elif cold_white > 0: self._ct = TEMP_COLD_HASS - self._hs = ct_to_hs(self._ct) + self._color_mode = COLOR_MODE_COLOR_TEMP else: - # RGB color is being used. Just report midpoint. - self._ct = TEMP_MID_HASS + self._color_mode = COLOR_MODE_RGB elif self._color_channels & COLOR_CHANNEL_WARM_WHITE: self._white = warm_white @@ -341,17 +344,19 @@ class ZwaveColorLight(ZwaveDimmer): or self._color_channels & COLOR_CHANNEL_GREEN or self._color_channels & COLOR_CHANNEL_BLUE ): - self._hs = None + self._rgb = None @property - def hs_color(self): - """Return the hs color.""" - return self._hs + def rgb_color(self): + """Return the rgb color.""" + return self._rgb @property - def white_value(self): - """Return the white value of this light between 0..255.""" - return self._white + def rgbw_color(self): + """Return the rgbw color.""" + if self._rgb is None: + return None + return (*self._rgb, self._white) @property def color_temp(self): @@ -362,31 +367,28 @@ class ZwaveColorLight(ZwaveDimmer): """Turn the device on.""" rgbw = None - if ATTR_WHITE_VALUE in kwargs: - self._white = kwargs[ATTR_WHITE_VALUE] - if ATTR_COLOR_TEMP in kwargs: # 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: + self._color_mode = COLOR_MODE_COLOR_TEMP if kwargs[ATTR_COLOR_TEMP] > TEMP_MID_HASS: self._ct = TEMP_WARM_HASS rgbw = "#000000ff00" else: self._ct = TEMP_COLD_HASS rgbw = "#00000000ff" - elif ATTR_HS_COLOR in kwargs: - self._hs = kwargs[ATTR_HS_COLOR] - if ATTR_WHITE_VALUE not in kwargs: - # white LED must be off in order for color to work - self._white = 0 + elif ATTR_RGB_COLOR in kwargs: + self._rgb = kwargs[ATTR_RGB_COLOR] + self._white = 0 + elif ATTR_RGBW_COLOR in kwargs: + self._rgb = kwargs[ATTR_RGBW_COLOR][0:3] + self._white = kwargs[ATTR_RGBW_COLOR][3] - if ( - ATTR_WHITE_VALUE in kwargs or ATTR_HS_COLOR in kwargs - ) and self._hs is not None: + if ATTR_RGB_COLOR in kwargs or ATTR_RGBW_COLOR in kwargs: rgbw = "#" - for colorval in color_util.color_hs_to_RGB(*self._hs): + for colorval in self._rgb: rgbw += format(colorval, "02x") if self._white is not None: rgbw += format(self._white, "02x") + "00" diff --git a/tests/components/zwave/test_light.py b/tests/components/zwave/test_light.py index 9e943c54bb4..35128ccc69a 100644 --- a/tests/components/zwave/test_light.py +++ b/tests/components/zwave/test_light.py @@ -5,14 +5,14 @@ from homeassistant.components import zwave from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, - ATTR_HS_COLOR, + ATTR_RGB_COLOR, + ATTR_RGBW_COLOR, ATTR_TRANSITION, - ATTR_WHITE_VALUE, - SUPPORT_BRIGHTNESS, - SUPPORT_COLOR, - SUPPORT_COLOR_TEMP, + COLOR_MODE_BRIGHTNESS, + COLOR_MODE_COLOR_TEMP, + COLOR_MODE_RGB, + COLOR_MODE_RGBW, SUPPORT_TRANSITION, - SUPPORT_WHITE_VALUE, ) from homeassistant.components.zwave import const, light @@ -38,7 +38,9 @@ def test_get_device_detects_dimmer(mock_openzwave): device = light.get_device(node=node, values=values, node_config={}) assert isinstance(device, light.ZwaveDimmer) - assert device.supported_features == SUPPORT_BRIGHTNESS + assert device.color_mode == COLOR_MODE_BRIGHTNESS + assert device.supported_features is None + assert device.supported_color_modes == {COLOR_MODE_BRIGHTNESS} def test_get_device_detects_colorlight(mock_openzwave): @@ -49,7 +51,9 @@ def test_get_device_detects_colorlight(mock_openzwave): device = light.get_device(node=node, values=values, node_config={}) assert isinstance(device, light.ZwaveColorLight) - assert device.supported_features == SUPPORT_BRIGHTNESS | SUPPORT_COLOR + assert device.color_mode == COLOR_MODE_RGB + assert device.supported_features is None + assert device.supported_color_modes == {COLOR_MODE_RGB} def test_get_device_detects_zw098(mock_openzwave): @@ -63,9 +67,9 @@ def test_get_device_detects_zw098(mock_openzwave): values = MockLightValues(primary=value) device = light.get_device(node=node, values=values, node_config={}) assert isinstance(device, light.ZwaveColorLight) - assert device.supported_features == ( - SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_COLOR_TEMP - ) + assert device.color_mode == COLOR_MODE_RGB + assert device.supported_features is None + assert device.supported_color_modes == {COLOR_MODE_COLOR_TEMP, COLOR_MODE_RGB} def test_get_device_detects_rgbw_light(mock_openzwave): @@ -79,9 +83,9 @@ def test_get_device_detects_rgbw_light(mock_openzwave): device = light.get_device(node=node, values=values, node_config={}) device.value_added() assert isinstance(device, light.ZwaveColorLight) - assert device.supported_features == ( - SUPPORT_BRIGHTNESS | SUPPORT_COLOR | SUPPORT_WHITE_VALUE - ) + assert device.color_mode == COLOR_MODE_RGBW + assert device.supported_features is None + assert device.supported_color_modes == {COLOR_MODE_RGBW} def test_dimmer_turn_on(mock_openzwave): @@ -153,7 +157,9 @@ def test_dimmer_transitions(mock_openzwave): duration = MockValue(data=0, node=node) values = MockLightValues(primary=value, dimming_duration=duration) device = light.get_device(node=node, values=values, node_config={}) - assert device.supported_features == SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION + assert device.color_mode == COLOR_MODE_BRIGHTNESS + assert device.supported_features == SUPPORT_TRANSITION + assert device.supported_color_modes == {COLOR_MODE_BRIGHTNESS} # Test turn_on # Factory Default @@ -261,7 +267,7 @@ def test_dimmer_refresh_value(mock_openzwave): assert device.brightness == 118 -def test_set_hs_color(mock_openzwave): +def test_set_rgb_color(mock_openzwave): """Test setting zwave light color.""" node = MockNode(command_classes=[const.COMMAND_CLASS_SWITCH_COLOR]) value = MockValue(data=0, node=node) @@ -273,7 +279,7 @@ def test_set_hs_color(mock_openzwave): assert color.data == "#0000000000" - device.turn_on(**{ATTR_HS_COLOR: (30, 50)}) + device.turn_on(**{ATTR_RGB_COLOR: (0xFF, 0xBF, 0x7F)}) assert color.data == "#ffbf7f0000" @@ -290,14 +296,14 @@ def test_set_white_value(mock_openzwave): assert color.data == "#0000000000" - device.turn_on(**{ATTR_WHITE_VALUE: 200}) + device.turn_on(**{ATTR_RGBW_COLOR: (0xFF, 0xFF, 0xFF, 0xC8)}) assert color.data == "#ffffffc800" def test_disable_white_if_set_color(mock_openzwave): """ - Test that _white is set to 0 if turn_on with ATTR_HS_COLOR. + Test that _white is set to 0 if turn_on with ATTR_RGB_COLOR. See Issue #13930 - many RGBW ZWave bulbs will only activate the RGB LED to produce color if _white is set to zero. @@ -312,12 +318,12 @@ def test_disable_white_if_set_color(mock_openzwave): device._white = 234 assert color.data == "#0000000000" - assert device.white_value == 234 + assert device.rgbw_color == (0, 0, 0, 234) - device.turn_on(**{ATTR_HS_COLOR: (30, 50)}) + device.turn_on(**{ATTR_RGB_COLOR: (0xFF, 0xBF, 0x7F)}) - assert device.white_value == 0 assert color.data == "#ffbf7f0000" + assert device.rgbw_color == (0xFF, 0xBF, 0x7F, 0x00) def test_zw098_set_color_temp(mock_openzwave): @@ -355,7 +361,8 @@ def test_rgb_not_supported(mock_openzwave): values = MockLightValues(primary=value, color=color, color_channels=color_channels) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color is None + assert device.rgb_color is None + assert device.rgbw_color is None def test_no_color_value(mock_openzwave): @@ -365,7 +372,8 @@ def test_no_color_value(mock_openzwave): values = MockLightValues(primary=value) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color is None + assert device.rgb_color is None + assert device.rgbw_color is None def test_no_color_channels_value(mock_openzwave): @@ -376,7 +384,8 @@ def test_no_color_channels_value(mock_openzwave): values = MockLightValues(primary=value, color=color) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color is None + assert device.rgb_color is None + assert device.rgbw_color is None def test_rgb_value_changed(mock_openzwave): @@ -389,12 +398,12 @@ def test_rgb_value_changed(mock_openzwave): values = MockLightValues(primary=value, color=color, color_channels=color_channels) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color == (0, 0) + assert device.rgb_color == (0, 0, 0) color.data = "#ffbf800000" value_changed(color) - assert device.hs_color == (29.764, 49.804) + assert device.rgb_color == (0xFF, 0xBF, 0x80) def test_rgbww_value_changed(mock_openzwave): @@ -407,14 +416,12 @@ def test_rgbww_value_changed(mock_openzwave): values = MockLightValues(primary=value, color=color, color_channels=color_channels) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color == (0, 0) - assert device.white_value == 0 + assert device.rgbw_color == (0, 0, 0, 0) color.data = "#c86400c800" value_changed(color) - assert device.hs_color == (30, 100) - assert device.white_value == 200 + assert device.rgbw_color == (0xC8, 0x64, 0x00, 0xC8) def test_rgbcw_value_changed(mock_openzwave): @@ -427,14 +434,12 @@ def test_rgbcw_value_changed(mock_openzwave): values = MockLightValues(primary=value, color=color, color_channels=color_channels) device = light.get_device(node=node, values=values, node_config={}) - assert device.hs_color == (0, 0) - assert device.white_value == 0 + assert device.rgbw_color == (0, 0, 0, 0) color.data = "#c86400c800" value_changed(color) - assert device.hs_color == (30, 100) - assert device.white_value == 200 + assert device.rgbw_color == (0xC8, 0x64, 0x00, 0xC8) def test_ct_value_changed(mock_openzwave): @@ -451,14 +456,21 @@ def test_ct_value_changed(mock_openzwave): values = MockLightValues(primary=value, color=color, color_channels=color_channels) device = light.get_device(node=node, values=values, node_config={}) - assert device.color_temp == light.TEMP_MID_HASS + assert device.color_mode == COLOR_MODE_RGB + assert device.color_temp is None color.data = "#000000ff00" value_changed(color) + assert device.color_mode == COLOR_MODE_COLOR_TEMP assert device.color_temp == light.TEMP_WARM_HASS color.data = "#00000000ff" value_changed(color) + assert device.color_mode == COLOR_MODE_COLOR_TEMP assert device.color_temp == light.TEMP_COLD_HASS + + color.data = "#ff00000000" + value_changed(color) + assert device.color_mode == COLOR_MODE_RGB