Add color_mode support to zwave_js light (#49588)
parent
0d3d2edbff
commit
956c972e72
|
@ -11,14 +11,14 @@ from homeassistant.components.light import (
|
|||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_HS_COLOR,
|
||||
ATTR_RGBW_COLOR,
|
||||
ATTR_TRANSITION,
|
||||
ATTR_WHITE_VALUE,
|
||||
COLOR_MODE_BRIGHTNESS,
|
||||
COLOR_MODE_COLOR_TEMP,
|
||||
COLOR_MODE_HS,
|
||||
COLOR_MODE_RGBW,
|
||||
DOMAIN as LIGHT_DOMAIN,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_TRANSITION,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
LightEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -88,14 +88,14 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
"""Initialize the light."""
|
||||
super().__init__(config_entry, client, info)
|
||||
self._supports_color = False
|
||||
self._supports_white_value = False
|
||||
self._supports_rgbw = False
|
||||
self._supports_color_temp = False
|
||||
self._hs_color: tuple[float, float] | None = None
|
||||
self._white_value: int | None = None
|
||||
self._rgbw_color: tuple[int, int, int, int] | None = None
|
||||
self._color_mode: str | None = None
|
||||
self._color_temp: int | None = None
|
||||
self._min_mireds = 153 # 6500K as a safe default
|
||||
self._max_mireds = 370 # 2700K as a safe default
|
||||
self._supported_features = SUPPORT_BRIGHTNESS
|
||||
self._warm_white = self.get_zwave_value(
|
||||
"targetColor",
|
||||
CommandClass.SWITCH_COLOR,
|
||||
|
@ -106,6 +106,8 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
CommandClass.SWITCH_COLOR,
|
||||
value_property_key=ColorComponent.COLD_WHITE,
|
||||
)
|
||||
self._supported_color_modes = set()
|
||||
self._supported_features = 0
|
||||
|
||||
# get additional (optional) values and set features
|
||||
self._target_value = self.get_zwave_value("targetValue")
|
||||
|
@ -113,12 +115,14 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
if self._dimming_duration is not None:
|
||||
self._supported_features |= SUPPORT_TRANSITION
|
||||
self._calculate_color_values()
|
||||
if self._supports_color:
|
||||
self._supported_features |= SUPPORT_COLOR
|
||||
if self._supports_rgbw:
|
||||
self._supported_color_modes.add(COLOR_MODE_RGBW)
|
||||
elif self._supports_color:
|
||||
self._supported_color_modes.add(COLOR_MODE_HS)
|
||||
if self._supports_color_temp:
|
||||
self._supported_features |= SUPPORT_COLOR_TEMP
|
||||
if self._supports_white_value:
|
||||
self._supported_features |= SUPPORT_WHITE_VALUE
|
||||
self._supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
||||
if not self._supported_color_modes:
|
||||
self._supported_color_modes.add(COLOR_MODE_BRIGHTNESS)
|
||||
|
||||
@callback
|
||||
def on_value_update(self) -> None:
|
||||
|
@ -135,6 +139,11 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
return round((self.info.primary_value.value / 99) * 255)
|
||||
return 0
|
||||
|
||||
@property
|
||||
def color_mode(self) -> str | None:
|
||||
"""Return the color mode of the light."""
|
||||
return self._color_mode
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if device is on (brightness above 0)."""
|
||||
|
@ -146,9 +155,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
return self._hs_color
|
||||
|
||||
@property
|
||||
def white_value(self) -> int | None:
|
||||
"""Return the white value of this light between 0..255."""
|
||||
return self._white_value
|
||||
def rgbw_color(self) -> tuple[int, int, int, int] | None:
|
||||
"""Return the hs color."""
|
||||
return self._rgbw_color
|
||||
|
||||
@property
|
||||
def color_temp(self) -> int | None:
|
||||
|
@ -165,6 +174,11 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
"""Return the warmest color_temp that this light supports."""
|
||||
return self._max_mireds
|
||||
|
||||
@property
|
||||
def supported_color_modes(self) -> set | None:
|
||||
"""Flag supported features."""
|
||||
return self._supported_color_modes
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
|
@ -214,20 +228,20 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
}
|
||||
)
|
||||
|
||||
# White value
|
||||
white_value = kwargs.get(ATTR_WHITE_VALUE)
|
||||
if white_value is not None and self._supports_white_value:
|
||||
# white led brightness is controlled by white level
|
||||
# rgb leds (if any) can be on at the same time
|
||||
white_channel = {}
|
||||
|
||||
# RGBW
|
||||
rgbw = kwargs.get(ATTR_RGBW_COLOR)
|
||||
if rgbw is not None and self._supports_rgbw:
|
||||
rgbw_channels = {
|
||||
ColorComponent.RED: rgbw[0],
|
||||
ColorComponent.GREEN: rgbw[1],
|
||||
ColorComponent.BLUE: rgbw[2],
|
||||
}
|
||||
if self._warm_white:
|
||||
white_channel[ColorComponent.WARM_WHITE] = white_value
|
||||
rgbw_channels[ColorComponent.WARM_WHITE] = rgbw[3]
|
||||
|
||||
if self._cold_white:
|
||||
white_channel[ColorComponent.COLD_WHITE] = white_value
|
||||
|
||||
await self._async_set_colors(white_channel)
|
||||
rgbw_channels[ColorComponent.COLD_WHITE] = rgbw[3]
|
||||
await self._async_set_colors(rgbw_channels)
|
||||
|
||||
# set brightness
|
||||
await self._async_set_brightness(
|
||||
|
@ -364,6 +378,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
else:
|
||||
multi_color = {}
|
||||
|
||||
# Default: Brightness (no color)
|
||||
self._color_mode = COLOR_MODE_BRIGHTNESS
|
||||
|
||||
# RGB support
|
||||
if red_val and green_val and blue_val:
|
||||
# prefer values from the multicolor property
|
||||
|
@ -373,6 +390,8 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
self._supports_color = True
|
||||
# convert to HS
|
||||
self._hs_color = color_util.color_RGB_to_hs(red, green, blue)
|
||||
# Light supports color, set color mode to hs
|
||||
self._color_mode = COLOR_MODE_HS
|
||||
|
||||
# color temperature support
|
||||
if ww_val and cw_val:
|
||||
|
@ -385,13 +404,21 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||
self._max_mireds
|
||||
- ((cold_white / 255) * (self._max_mireds - self._min_mireds))
|
||||
)
|
||||
# White channels turned on, set color mode to color_temp
|
||||
self._color_mode = COLOR_MODE_COLOR_TEMP
|
||||
else:
|
||||
self._color_temp = None
|
||||
# only one white channel (warm white) = white_level support
|
||||
elif ww_val:
|
||||
self._supports_white_value = True
|
||||
self._white_value = multi_color.get("warmWhite", ww_val.value)
|
||||
# only one white channel (cool white) = white_level support
|
||||
# only one white channel (warm white) = rgbw support
|
||||
elif red_val and green_val and blue_val and ww_val:
|
||||
self._supports_rgbw = True
|
||||
white = multi_color.get("warmWhite", ww_val.value)
|
||||
self._rgbw_color = (red, green, blue, white)
|
||||
# Light supports rgbw, set color mode to rgbw
|
||||
self._color_mode = COLOR_MODE_RGBW
|
||||
# only one white channel (cool white) = rgbw support
|
||||
elif cw_val:
|
||||
self._supports_white_value = True
|
||||
self._white_value = multi_color.get("coldWhite", cw_val.value)
|
||||
self._supports_rgbw = True
|
||||
white = multi_color.get("coldWhite", cw_val.value)
|
||||
self._rgbw_color = (red, green, blue, white)
|
||||
# Light supports rgbw, set color mode to rgbw
|
||||
self._color_mode = COLOR_MODE_RGBW
|
||||
|
|
|
@ -5,11 +5,14 @@ from zwave_js_server.event import Event
|
|||
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_MODE,
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_MAX_MIREDS,
|
||||
ATTR_MIN_MIREDS,
|
||||
ATTR_RGB_COLOR,
|
||||
ATTR_WHITE_VALUE,
|
||||
ATTR_RGBW_COLOR,
|
||||
ATTR_SUPPORTED_COLOR_MODES,
|
||||
SUPPORT_TRANSITION,
|
||||
)
|
||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_OFF, STATE_ON
|
||||
|
||||
|
@ -30,7 +33,8 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
|
|||
assert state.state == STATE_OFF
|
||||
assert state.attributes[ATTR_MIN_MIREDS] == 153
|
||||
assert state.attributes[ATTR_MAX_MIREDS] == 370
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 51
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TRANSITION
|
||||
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["color_temp", "hs"]
|
||||
|
||||
# Test turning on
|
||||
await hass.services.async_call(
|
||||
|
@ -86,8 +90,10 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
|
|||
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_COLOR_MODE] == "color_temp"
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
||||
assert state.attributes[ATTR_COLOR_TEMP] == 370
|
||||
assert ATTR_RGB_COLOR not in state.attributes
|
||||
|
||||
# Test turning on with same brightness
|
||||
await hass.services.async_call(
|
||||
|
@ -231,8 +237,10 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
|
|||
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_COLOR_MODE] == "hs"
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
||||
assert state.attributes[ATTR_RGB_COLOR] == (255, 76, 255)
|
||||
assert ATTR_COLOR_TEMP not in state.attributes
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
|
@ -352,9 +360,10 @@ async def test_light(hass, client, bulb_6_multi_color, integration):
|
|||
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_COLOR_MODE] == "color_temp"
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
||||
assert state.attributes[ATTR_COLOR_TEMP] == 170
|
||||
assert state.attributes[ATTR_RGB_COLOR] == (255, 255, 255)
|
||||
assert ATTR_RGB_COLOR not in state.attributes
|
||||
|
||||
# Test turning on with same color temp
|
||||
await hass.services.async_call(
|
||||
|
@ -415,20 +424,20 @@ async def test_optional_light(hass, client, aeon_smart_switch_6, integration):
|
|||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_white_value_light(hass, client, zen_31, integration):
|
||||
async def test_rgbw_light(hass, client, zen_31, integration):
|
||||
"""Test the light entity."""
|
||||
zen_31
|
||||
state = hass.states.get(ZEN_31_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == 177
|
||||
assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TRANSITION
|
||||
|
||||
# Test turning on
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
"turn_on",
|
||||
{"entity_id": ZEN_31_ENTITY, ATTR_WHITE_VALUE: 128},
|
||||
{"entity_id": ZEN_31_ENTITY, ATTR_RGBW_COLOR: (0, 0, 0, 128)},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
@ -451,7 +460,7 @@ async def test_white_value_light(hass, client, zen_31, integration):
|
|||
},
|
||||
"value": {"blue": 70, "green": 159, "red": 255, "warmWhite": 141},
|
||||
}
|
||||
assert args["value"] == {"warmWhite": 128}
|
||||
assert args["value"] == {"blue": 0, "green": 0, "red": 0, "warmWhite": 128}
|
||||
|
||||
args = client.async_send_command.call_args_list[1][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
|
|
Loading…
Reference in New Issue