1 """Support for Z-Wave lights."""
3 from __future__
import annotations
5 from typing
import Any, cast
7 from zwave_js_server.client
import Client
as ZwaveClient
8 from zwave_js_server.const
import (
10 TRANSITION_DURATION_OPTION,
13 from zwave_js_server.const.command_class.color_switch
import (
14 COLOR_SWITCH_COMBINED_AMBER,
15 COLOR_SWITCH_COMBINED_BLUE,
16 COLOR_SWITCH_COMBINED_COLD_WHITE,
17 COLOR_SWITCH_COMBINED_CYAN,
18 COLOR_SWITCH_COMBINED_GREEN,
19 COLOR_SWITCH_COMBINED_PURPLE,
20 COLOR_SWITCH_COMBINED_RED,
21 COLOR_SWITCH_COMBINED_WARM_WHITE,
22 CURRENT_COLOR_PROPERTY,
23 TARGET_COLOR_PROPERTY,
26 from zwave_js_server.const.command_class.multilevel_switch
import SET_TO_PREVIOUS_VALUE
27 from zwave_js_server.model.driver
import Driver
28 from zwave_js_server.model.value
import Value
36 DOMAIN
as LIGHT_DOMAIN,
47 from .const
import DATA_CLIENT, DOMAIN
48 from .discovery
import ZwaveDiscoveryInfo
49 from .entity
import ZWaveBaseEntity
54 ColorComponent.WARM_WHITE: COLOR_SWITCH_COMBINED_WARM_WHITE,
55 ColorComponent.COLD_WHITE: COLOR_SWITCH_COMBINED_COLD_WHITE,
56 ColorComponent.RED: COLOR_SWITCH_COMBINED_RED,
57 ColorComponent.GREEN: COLOR_SWITCH_COMBINED_GREEN,
58 ColorComponent.BLUE: COLOR_SWITCH_COMBINED_BLUE,
59 ColorComponent.AMBER: COLOR_SWITCH_COMBINED_AMBER,
60 ColorComponent.CYAN: COLOR_SWITCH_COMBINED_CYAN,
61 ColorComponent.PURPLE: COLOR_SWITCH_COMBINED_PURPLE,
67 config_entry: ConfigEntry,
68 async_add_entities: AddEntitiesCallback,
70 """Set up Z-Wave Light from Config Entry."""
71 client: ZwaveClient = config_entry.runtime_data[DATA_CLIENT]
74 def async_add_light(info: ZwaveDiscoveryInfo) ->
None:
75 """Add Z-Wave Light."""
76 driver = client.driver
77 assert driver
is not None
79 if info.platform_hint ==
"color_onoff":
84 config_entry.async_on_unload(
87 f
"{DOMAIN}_{config_entry.entry_id}_add_{LIGHT_DOMAIN}",
94 """Convert brightness in 0-255 scale to 0-99 scale.
96 `value` -- (int) Brightness byte value from 0-255.
99 return max(1, round((value / 255) * 99))
104 """Representation of a Z-Wave light."""
107 self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
109 """Initialize the light."""
110 super().
__init__(config_entry, driver, info)
116 self.
_hs_color_hs_color: tuple[float, float] |
None =
None
117 self.
_rgbw_color_rgbw_color: tuple[int, int, int, int] |
None =
None
122 TARGET_COLOR_PROPERTY,
123 CommandClass.SWITCH_COLOR,
124 value_property_key=ColorComponent.WARM_WHITE,
127 TARGET_COLOR_PROPERTY,
128 CommandClass.SWITCH_COLOR,
129 value_property_key=ColorComponent.COLD_WHITE,
131 self._supported_color_modes: set[ColorMode] = set()
136 if self.
infoinfo.primary_value.command_class == CommandClass.SWITCH_BINARY:
139 TARGET_VALUE_PROPERTY,
140 CommandClass.SWITCH_BINARY,
141 add_to_watched_value_ids=
False,
144 elif self.
infoinfo.primary_value.command_class == CommandClass.SWITCH_MULTILEVEL:
147 TARGET_VALUE_PROPERTY,
148 CommandClass.SWITCH_MULTILEVEL,
149 add_to_watched_value_ids=
False,
152 elif self.
infoinfo.primary_value.command_class == CommandClass.BASIC:
156 include_value_name=
True, alternate_value_name=
"Basic"
159 TARGET_VALUE_PROPERTY,
161 add_to_watched_value_ids=
False,
166 CURRENT_COLOR_PROPERTY,
167 CommandClass.SWITCH_COLOR,
168 value_property_key=
None,
171 TARGET_COLOR_PROPERTY,
172 CommandClass.SWITCH_COLOR,
173 add_to_watched_value_ids=
False,
178 self._supported_color_modes.
add(ColorMode.RGBW)
180 self._supported_color_modes.
add(ColorMode.HS)
182 self._supported_color_modes.
add(ColorMode.COLOR_TEMP)
183 if not self._supported_color_modes:
184 self._supported_color_modes.
add(ColorMode.BRIGHTNESS)
190 and TRANSITION_DURATION_OPTION
195 and TRANSITION_DURATION_OPTION
196 in self.
_target_color_target_color.metadata.value_change_options
200 self._attr_supported_features |= LightEntityFeature.TRANSITION
206 """Call when a watched value is added or updated."""
211 """Return the brightness of this light between 0..255.
213 Z-Wave multilevel switches use a range of [0, 99] to control brightness.
215 if self.
infoinfo.primary_value.value
is None:
217 return round((cast(int, self.
infoinfo.primary_value.value) / 99) * 255)
221 """Return the color mode of the light."""
226 """Return true if device is on (brightness above 0)."""
231 return brightness > 0
if brightness
is not None else None
235 """Return the hs color."""
240 """Return the RGBW color."""
245 """Return the color temperature."""
250 """Return the coldest color_temp that this light supports."""
255 """Return the warmest color_temp that this light supports."""
260 """Flag supported features."""
261 return self._supported_color_modes
264 """Turn the device on."""
266 transition = kwargs.get(ATTR_TRANSITION)
267 brightness = kwargs.get(ATTR_BRIGHTNESS)
269 hs_color = kwargs.get(ATTR_HS_COLOR)
270 color_temp = kwargs.get(ATTR_COLOR_TEMP)
271 rgbw = kwargs.get(ATTR_RGBW_COLOR)
273 new_colors = self.
_get_new_colors_get_new_colors(hs_color, color_temp, rgbw)
274 if new_colors
is not None:
281 """Turn the light off."""
286 hs_color: tuple[float, float] |
None,
287 color_temp: int |
None,
288 rgbw: tuple[int, int, int, int] |
None,
289 brightness_scale: float |
None =
None,
290 ) -> dict[ColorComponent, int] |
None:
291 """Determine the new color dict to set."""
295 red, green, blue = color_util.color_hs_to_RGB(*hs_color)
296 if brightness_scale
is not None:
297 red = round(red * brightness_scale)
298 green = round(green * brightness_scale)
299 blue = round(blue * brightness_scale)
301 ColorComponent.RED: red,
302 ColorComponent.GREEN: green,
303 ColorComponent.BLUE: blue,
307 colors[ColorComponent.WARM_WHITE] = 0
308 colors[ColorComponent.COLD_WHITE] = 0
327 ColorComponent.WARM_WHITE: warm,
328 ColorComponent.COLD_WHITE: cold,
332 colors[ColorComponent.RED] = 0
333 colors[ColorComponent.GREEN] = 0
334 colors[ColorComponent.BLUE] = 0
340 ColorComponent.RED: rgbw[0],
341 ColorComponent.GREEN: rgbw[1],
342 ColorComponent.BLUE: rgbw[2],
345 rgbw_channels[ColorComponent.WARM_WHITE] = rgbw[3]
348 rgbw_channels[ColorComponent.COLD_WHITE] = rgbw[3]
356 colors: dict[ColorComponent, int],
357 transition: float |
None =
None,
359 """Set (multiple) defined colors to given value(s)."""
363 combined_color_val = cast(
367 CommandClass.SWITCH_COLOR,
368 value_property_key=
None,
371 zwave_transition =
None
374 if transition
is not None:
375 zwave_transition = {TRANSITION_DURATION_OPTION: f
"{int(transition)}s"}
377 zwave_transition = {TRANSITION_DURATION_OPTION:
"default"}
380 for color, value
in colors.items():
381 color_name = MULTI_COLOR_MAP[color]
382 colors_dict[color_name] = value
384 await self.
_async_set_value_async_set_value(combined_color_val, colors_dict, zwave_transition)
387 self, brightness: int |
None, transition: float |
None =
None
389 """Set new brightness to light."""
393 if brightness
is None:
394 zwave_brightness = SET_TO_PREVIOUS_VALUE
400 zwave_transition =
None
402 if transition
is not None:
403 zwave_transition = {TRANSITION_DURATION_OPTION: f
"{int(transition)}s"}
405 zwave_transition = {TRANSITION_DURATION_OPTION:
"default"}
420 zwave_brightness == SET_TO_PREVIOUS_VALUE
421 and self.
infoinfo.primary_value.command_class
422 in (CommandClass.BASIC, CommandClass.SWITCH_MULTILEVEL)
429 """Get light colors."""
434 CURRENT_COLOR_PROPERTY,
435 CommandClass.SWITCH_COLOR,
436 value_property_key=ColorComponent.RED.value,
439 CURRENT_COLOR_PROPERTY,
440 CommandClass.SWITCH_COLOR,
441 value_property_key=ColorComponent.GREEN.value,
444 CURRENT_COLOR_PROPERTY,
445 CommandClass.SWITCH_COLOR,
446 value_property_key=ColorComponent.BLUE.value,
449 CURRENT_COLOR_PROPERTY,
450 CommandClass.SWITCH_COLOR,
451 value_property_key=ColorComponent.WARM_WHITE.value,
454 CURRENT_COLOR_PROPERTY,
455 CommandClass.SWITCH_COLOR,
456 value_property_key=ColorComponent.COLD_WHITE.value,
458 return (red_val, green_val, blue_val, ww_val, cw_val)
462 """Calculate light colors."""
463 (red, green, blue, warm_white, cool_white) = self.
_get_color_values_get_color_values()
465 if red
and green
and blue:
468 if warm_white
and cool_white:
471 elif red
and green
and blue
and warm_white
or cool_white:
476 """Calculate light colors."""
477 (red_val, green_val, blue_val, ww_val, cw_val) = self.
_get_color_values_get_color_values()
491 if red_val
and green_val
and blue_val:
493 red = multi_color.get(COLOR_SWITCH_COMBINED_RED, red_val.value)
494 green = multi_color.get(COLOR_SWITCH_COMBINED_GREEN, green_val.value)
495 blue = multi_color.get(COLOR_SWITCH_COMBINED_BLUE, blue_val.value)
496 if None not in (red, green, blue):
498 self.
_hs_color_hs_color = color_util.color_RGB_to_hs(red, green, blue)
503 if ww_val
and cw_val:
504 warm_white = multi_color.get(COLOR_SWITCH_COMBINED_WARM_WHITE, ww_val.value)
505 cold_white = multi_color.get(COLOR_SWITCH_COMBINED_COLD_WHITE, cw_val.value)
507 if cold_white
or warm_white:
517 elif red_val
and green_val
and blue_val
and ww_val:
518 white = multi_color.get(COLOR_SWITCH_COMBINED_WARM_WHITE, ww_val.value)
525 white = multi_color.get(COLOR_SWITCH_COMBINED_COLD_WHITE, cw_val.value)
526 self.
_rgbw_color_rgbw_color = (red, green, blue, white)
532 """Representation of a colored Z-Wave light with an optional binary switch to turn on/off.
534 Dimming for RGB lights is realized by scaling the color channels.
538 self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
540 """Initialize the light."""
541 super().
__init__(config_entry, driver, info)
543 self.
_last_on_color_last_on_color: dict[ColorComponent, int] |
None =
None
548 """Return the brightness of this light between 0..255.
550 Z-Wave multilevel switches use a range of [0, 99] to control brightness.
552 if self.
infoinfo.primary_value.value
is None:
562 if v
is not None and v.value
is not None
564 return max(color_values)
if color_values
else 0
567 """Turn the device on."""
570 kwargs.get(ATTR_RGBW_COLOR)
is not None
571 or kwargs.get(ATTR_COLOR_TEMP)
is not None
578 transition = kwargs.get(ATTR_TRANSITION)
579 brightness = kwargs.get(ATTR_BRIGHTNESS)
580 hs_color = kwargs.get(ATTR_HS_COLOR)
581 new_colors: dict[ColorComponent, int] |
None =
None
582 scale: float |
None =
None
584 if brightness
is None and hs_color
is None:
597 ColorComponent.RED: 255,
598 ColorComponent.GREEN: 255,
599 ColorComponent.BLUE: 255,
601 elif brightness
is not None:
604 scale = brightness / 255
611 new_scale = brightness / old_brightness
615 new_colors[color] = round(value * new_scale)
618 elif hs_color
is not None and brightness
is None:
621 if current_brightness == 0
and self.
_last_brightness_last_brightness
is not None:
624 elif current_brightness
is not None:
625 scale = current_brightness / 255
630 if new_colors
is None:
632 hs_color=hs_color, color_temp=
None, rgbw=
None, brightness_scale=scale
635 if new_colors
is not None:
642 """Turn the light off."""
647 red = self.
_current_color_current_color.value.get(COLOR_SWITCH_COMBINED_RED)
648 green = self.
_current_color_current_color.value.get(COLOR_SWITCH_COMBINED_GREEN)
649 blue = self.
_current_color_current_color.value.get(COLOR_SWITCH_COMBINED_BLUE)
651 last_color: dict[ColorComponent, int] = {}
653 last_color[ColorComponent.RED] = red
654 if green
is not None:
655 last_color[ColorComponent.GREEN] = green
657 last_color[ColorComponent.BLUE] = blue
668 ColorComponent.RED: 0,
669 ColorComponent.GREEN: 0,
670 ColorComponent.BLUE: 0,
675 kwargs.get(ATTR_TRANSITION),
int|None brightness(self)
set[ColorMode]|set[str]|None supported_color_modes(self)
ColorMode|str|None color_mode(self)
str generate_name(self, bool include_value_name=False, str|None alternate_value_name=None, Sequence[str|None]|None additional_info=None, str|None name_prefix=None)
SetValueResult|None _async_set_value(self, ZwaveValue value, Any new_value, dict|None options=None, bool|None wait_for_result=None)
ZwaveValue|None get_zwave_value(self, str|int value_property, int|None command_class=None, int|None endpoint=None, int|str|None value_property_key=None, bool add_to_watched_value_ids=True, bool check_all_endpoints=False)
None __init__(self, ConfigEntry config_entry, Driver driver, ZwaveDiscoveryInfo info)
None async_turn_on(self, **Any kwargs)
int|None brightness(self)
None async_turn_off(self, **Any kwargs)
dict[ColorComponent, int]|None _get_new_colors(self, tuple[float, float]|None hs_color, int|None color_temp, tuple[int, int, int, int]|None rgbw, float|None brightness_scale=None)
supports_color_transition
None __init__(self, ConfigEntry config_entry, Driver driver, ZwaveDiscoveryInfo info)
tuple[float, float]|None hs_color(self)
int|None brightness(self)
set[ColorMode]|None supported_color_modes(self)
None async_turn_on(self, **Any kwargs)
tuple[Value|None,...] _get_color_values(self)
None _async_set_colors(self, dict[ColorComponent, int] colors, float|None transition=None)
supports_brightness_transition
None on_value_update(self)
None _calculate_color_values(self)
int|None color_temp(self)
str|None color_mode(self)
tuple[int, int, int, int]|None rgbw_color(self)
None _calculate_color_support(self)
None _async_set_brightness(self, int|None brightness, float|None transition=None)
None async_turn_off(self, **Any kwargs)
None async_write_ha_state(self)
bool add(self, _T matcher)
int byte_to_zwave_brightness(int value)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)