mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Add color mode support to zwave light (#55264)
* Add color mode support to zwave light * Fix typo
This commit is contained in:
parent
e48f6d548f
commit
c157f1a787
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user