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