mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 18:27:51 +00:00
This reverts commit c7cfd56b720be8212af2686ecfa5b8cad6ee299b.
This commit is contained in:
parent
fcf91954ff
commit
b8fd921c81
@ -238,12 +238,6 @@ SWITCH_BINARY_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
|
|||||||
command_class={CommandClass.SWITCH_BINARY}, property={CURRENT_VALUE_PROPERTY}
|
command_class={CommandClass.SWITCH_BINARY}, property={CURRENT_VALUE_PROPERTY}
|
||||||
)
|
)
|
||||||
|
|
||||||
COLOR_SWITCH_CURRENT_VALUE_SCHEMA = ZWaveValueDiscoverySchema(
|
|
||||||
command_class={CommandClass.SWITCH_COLOR},
|
|
||||||
property={CURRENT_COLOR_PROPERTY},
|
|
||||||
property_key={None},
|
|
||||||
)
|
|
||||||
|
|
||||||
SIREN_TONE_SCHEMA = ZWaveValueDiscoverySchema(
|
SIREN_TONE_SCHEMA = ZWaveValueDiscoverySchema(
|
||||||
command_class={CommandClass.SOUND_SWITCH},
|
command_class={CommandClass.SOUND_SWITCH},
|
||||||
property={TONE_ID_PROPERTY},
|
property={TONE_ID_PROPERTY},
|
||||||
@ -768,6 +762,33 @@ DISCOVERY_SCHEMAS = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
# HomeSeer HSM-200 v1
|
||||||
|
ZWaveDiscoverySchema(
|
||||||
|
platform=Platform.LIGHT,
|
||||||
|
hint="black_is_off",
|
||||||
|
manufacturer_id={0x001E},
|
||||||
|
product_id={0x0001},
|
||||||
|
product_type={0x0004},
|
||||||
|
primary_value=ZWaveValueDiscoverySchema(
|
||||||
|
command_class={CommandClass.SWITCH_COLOR},
|
||||||
|
property={CURRENT_COLOR_PROPERTY},
|
||||||
|
property_key={None},
|
||||||
|
),
|
||||||
|
absent_values=[SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA],
|
||||||
|
),
|
||||||
|
# Logic Group ZDB5100
|
||||||
|
ZWaveDiscoverySchema(
|
||||||
|
platform=Platform.LIGHT,
|
||||||
|
hint="black_is_off",
|
||||||
|
manufacturer_id={0x0234},
|
||||||
|
product_id={0x0121},
|
||||||
|
product_type={0x0003},
|
||||||
|
primary_value=ZWaveValueDiscoverySchema(
|
||||||
|
command_class={CommandClass.SWITCH_COLOR},
|
||||||
|
property={CURRENT_COLOR_PROPERTY},
|
||||||
|
property_key={None},
|
||||||
|
),
|
||||||
|
),
|
||||||
# ====== START OF GENERIC MAPPING SCHEMAS =======
|
# ====== START OF GENERIC MAPPING SCHEMAS =======
|
||||||
# locks
|
# locks
|
||||||
# Door Lock CC
|
# Door Lock CC
|
||||||
@ -969,11 +990,10 @@ DISCOVERY_SCHEMAS = [
|
|||||||
),
|
),
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
),
|
),
|
||||||
# binary switches without color support
|
# binary switches
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
platform=Platform.SWITCH,
|
platform=Platform.SWITCH,
|
||||||
primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
||||||
absent_values=[COLOR_SWITCH_CURRENT_VALUE_SCHEMA],
|
|
||||||
),
|
),
|
||||||
# switch for Indicator CC
|
# switch for Indicator CC
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
@ -1067,25 +1087,6 @@ DISCOVERY_SCHEMAS = [
|
|||||||
# catch any device with multilevel CC as light
|
# catch any device with multilevel CC as light
|
||||||
# NOTE: keep this at the bottom of the discovery scheme,
|
# NOTE: keep this at the bottom of the discovery scheme,
|
||||||
# to handle all others that need the multilevel CC first
|
# to handle all others that need the multilevel CC first
|
||||||
#
|
|
||||||
# Colored light (legacy device) that can only be controlled through Color Switch CC.
|
|
||||||
ZWaveDiscoverySchema(
|
|
||||||
platform=Platform.LIGHT,
|
|
||||||
hint="color_onoff",
|
|
||||||
primary_value=COLOR_SWITCH_CURRENT_VALUE_SCHEMA,
|
|
||||||
absent_values=[
|
|
||||||
SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
|
||||||
SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
# Colored light that can be turned on or off with the Binary Switch CC.
|
|
||||||
ZWaveDiscoverySchema(
|
|
||||||
platform=Platform.LIGHT,
|
|
||||||
hint="color_onoff",
|
|
||||||
primary_value=SWITCH_BINARY_CURRENT_VALUE_SCHEMA,
|
|
||||||
required_values=[COLOR_SWITCH_CURRENT_VALUE_SCHEMA],
|
|
||||||
),
|
|
||||||
# Dimmable light with or without color support.
|
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
platform=Platform.LIGHT,
|
platform=Platform.LIGHT,
|
||||||
primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
||||||
|
@ -76,8 +76,8 @@ async def async_setup_entry(
|
|||||||
driver = client.driver
|
driver = client.driver
|
||||||
assert driver is not None # Driver is ready before platforms are loaded.
|
assert driver is not None # Driver is ready before platforms are loaded.
|
||||||
|
|
||||||
if info.platform_hint == "color_onoff":
|
if info.platform_hint == "black_is_off":
|
||||||
async_add_entities([ZwaveColorOnOffLight(config_entry, driver, info)])
|
async_add_entities([ZwaveBlackIsOffLight(config_entry, driver, info)])
|
||||||
else:
|
else:
|
||||||
async_add_entities([ZwaveLight(config_entry, driver, info)])
|
async_add_entities([ZwaveLight(config_entry, driver, info)])
|
||||||
|
|
||||||
@ -111,10 +111,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
self._supports_color = False
|
self._supports_color = False
|
||||||
self._supports_rgbw = False
|
self._supports_rgbw = False
|
||||||
self._supports_color_temp = False
|
self._supports_color_temp = False
|
||||||
self._supports_dimming = False
|
|
||||||
self._color_mode: str | None = None
|
|
||||||
self._hs_color: tuple[float, float] | None = None
|
self._hs_color: tuple[float, float] | None = None
|
||||||
self._rgbw_color: tuple[int, int, int, 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
|
||||||
@ -130,28 +129,15 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
)
|
)
|
||||||
self._supported_color_modes: set[ColorMode] = set()
|
self._supported_color_modes: set[ColorMode] = set()
|
||||||
|
|
||||||
self._target_brightness: Value | None = None
|
|
||||||
|
|
||||||
# get additional (optional) values and set features
|
# get additional (optional) values and set features
|
||||||
if self.info.primary_value.command_class == CommandClass.SWITCH_BINARY:
|
# If the command class is Basic, we must geenerate a name that includes
|
||||||
# This light can not be dimmed separately from the color channels
|
# the command class name to avoid ambiguity
|
||||||
self._target_brightness = self.get_zwave_value(
|
self._target_brightness = self.get_zwave_value(
|
||||||
TARGET_VALUE_PROPERTY,
|
TARGET_VALUE_PROPERTY,
|
||||||
CommandClass.SWITCH_BINARY,
|
CommandClass.SWITCH_MULTILEVEL,
|
||||||
add_to_watched_value_ids=False,
|
add_to_watched_value_ids=False,
|
||||||
)
|
)
|
||||||
self._supports_dimming = False
|
if self.info.primary_value.command_class == CommandClass.BASIC:
|
||||||
elif self.info.primary_value.command_class == CommandClass.SWITCH_MULTILEVEL:
|
|
||||||
# This light can be dimmed separately from the color channels
|
|
||||||
self._target_brightness = self.get_zwave_value(
|
|
||||||
TARGET_VALUE_PROPERTY,
|
|
||||||
CommandClass.SWITCH_MULTILEVEL,
|
|
||||||
add_to_watched_value_ids=False,
|
|
||||||
)
|
|
||||||
self._supports_dimming = True
|
|
||||||
elif self.info.primary_value.command_class == CommandClass.BASIC:
|
|
||||||
# If the command class is Basic, we must generate a name that includes
|
|
||||||
# the command class name to avoid ambiguity
|
|
||||||
self._attr_name = self.generate_name(
|
self._attr_name = self.generate_name(
|
||||||
include_value_name=True, alternate_value_name="Basic"
|
include_value_name=True, alternate_value_name="Basic"
|
||||||
)
|
)
|
||||||
@ -160,13 +146,6 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
CommandClass.BASIC,
|
CommandClass.BASIC,
|
||||||
add_to_watched_value_ids=False,
|
add_to_watched_value_ids=False,
|
||||||
)
|
)
|
||||||
self._supports_dimming = True
|
|
||||||
|
|
||||||
self._current_color = self.get_zwave_value(
|
|
||||||
CURRENT_COLOR_PROPERTY,
|
|
||||||
CommandClass.SWITCH_COLOR,
|
|
||||||
value_property_key=None,
|
|
||||||
)
|
|
||||||
self._target_color = self.get_zwave_value(
|
self._target_color = self.get_zwave_value(
|
||||||
TARGET_COLOR_PROPERTY,
|
TARGET_COLOR_PROPERTY,
|
||||||
CommandClass.SWITCH_COLOR,
|
CommandClass.SWITCH_COLOR,
|
||||||
@ -237,7 +216,7 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def rgbw_color(self) -> tuple[int, int, int, int] | None:
|
def rgbw_color(self) -> tuple[int, int, int, int] | None:
|
||||||
"""Return the RGBW color."""
|
"""Return the hs color."""
|
||||||
return self._rgbw_color
|
return self._rgbw_color
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -264,39 +243,11 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
|
|
||||||
transition = kwargs.get(ATTR_TRANSITION)
|
transition = kwargs.get(ATTR_TRANSITION)
|
||||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
|
||||||
|
|
||||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
|
||||||
color_temp = kwargs.get(ATTR_COLOR_TEMP)
|
|
||||||
rgbw = kwargs.get(ATTR_RGBW_COLOR)
|
|
||||||
|
|
||||||
new_colors = self._get_new_colors(hs_color, color_temp, rgbw)
|
|
||||||
if new_colors is not None:
|
|
||||||
await self._async_set_colors(new_colors, transition)
|
|
||||||
|
|
||||||
# set brightness (or turn on if dimming is not supported)
|
|
||||||
await self._async_set_brightness(brightness, transition)
|
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
||||||
"""Turn the light off."""
|
|
||||||
await self._async_set_brightness(0, kwargs.get(ATTR_TRANSITION))
|
|
||||||
|
|
||||||
def _get_new_colors(
|
|
||||||
self,
|
|
||||||
hs_color: tuple[float, float] | None,
|
|
||||||
color_temp: int | None,
|
|
||||||
rgbw: tuple[int, int, int, int] | None,
|
|
||||||
brightness_scale: float | None = None,
|
|
||||||
) -> dict[ColorComponent, int] | None:
|
|
||||||
"""Determine the new color dict to set."""
|
|
||||||
|
|
||||||
# RGB/HS color
|
# RGB/HS color
|
||||||
|
hs_color = kwargs.get(ATTR_HS_COLOR)
|
||||||
if hs_color is not None and self._supports_color:
|
if hs_color is not None and self._supports_color:
|
||||||
red, green, blue = color_util.color_hs_to_RGB(*hs_color)
|
red, green, blue = color_util.color_hs_to_RGB(*hs_color)
|
||||||
if brightness_scale is not None:
|
|
||||||
red = round(red * brightness_scale)
|
|
||||||
green = round(green * brightness_scale)
|
|
||||||
blue = round(blue * brightness_scale)
|
|
||||||
colors = {
|
colors = {
|
||||||
ColorComponent.RED: red,
|
ColorComponent.RED: red,
|
||||||
ColorComponent.GREEN: green,
|
ColorComponent.GREEN: green,
|
||||||
@ -306,9 +257,10 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
# turn of white leds when setting rgb
|
# turn of white leds when setting rgb
|
||||||
colors[ColorComponent.WARM_WHITE] = 0
|
colors[ColorComponent.WARM_WHITE] = 0
|
||||||
colors[ColorComponent.COLD_WHITE] = 0
|
colors[ColorComponent.COLD_WHITE] = 0
|
||||||
return colors
|
await self._async_set_colors(colors, transition)
|
||||||
|
|
||||||
# Color temperature
|
# Color temperature
|
||||||
|
color_temp = kwargs.get(ATTR_COLOR_TEMP)
|
||||||
if color_temp is not None and self._supports_color_temp:
|
if color_temp is not None and self._supports_color_temp:
|
||||||
# Limit color temp to min/max values
|
# Limit color temp to min/max values
|
||||||
cold = max(
|
cold = max(
|
||||||
@ -323,18 +275,20 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
warm = 255 - cold
|
warm = 255 - cold
|
||||||
colors = {
|
await self._async_set_colors(
|
||||||
ColorComponent.WARM_WHITE: warm,
|
{
|
||||||
ColorComponent.COLD_WHITE: cold,
|
# turn off color leds when setting color temperature
|
||||||
}
|
ColorComponent.RED: 0,
|
||||||
if self._supports_color:
|
ColorComponent.GREEN: 0,
|
||||||
# turn off color leds when setting color temperature
|
ColorComponent.BLUE: 0,
|
||||||
colors[ColorComponent.RED] = 0
|
ColorComponent.WARM_WHITE: warm,
|
||||||
colors[ColorComponent.GREEN] = 0
|
ColorComponent.COLD_WHITE: cold,
|
||||||
colors[ColorComponent.BLUE] = 0
|
},
|
||||||
return colors
|
transition,
|
||||||
|
)
|
||||||
|
|
||||||
# RGBW
|
# RGBW
|
||||||
|
rgbw = kwargs.get(ATTR_RGBW_COLOR)
|
||||||
if rgbw is not None and self._supports_rgbw:
|
if rgbw is not None and self._supports_rgbw:
|
||||||
rgbw_channels = {
|
rgbw_channels = {
|
||||||
ColorComponent.RED: rgbw[0],
|
ColorComponent.RED: rgbw[0],
|
||||||
@ -346,15 +300,17 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
|
|
||||||
if self._cold_white:
|
if self._cold_white:
|
||||||
rgbw_channels[ColorComponent.COLD_WHITE] = rgbw[3]
|
rgbw_channels[ColorComponent.COLD_WHITE] = rgbw[3]
|
||||||
|
await self._async_set_colors(rgbw_channels, transition)
|
||||||
|
|
||||||
return rgbw_channels
|
# set brightness
|
||||||
|
await self._async_set_brightness(kwargs.get(ATTR_BRIGHTNESS), transition)
|
||||||
|
|
||||||
return None
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn the light off."""
|
||||||
|
await self._async_set_brightness(0, kwargs.get(ATTR_TRANSITION))
|
||||||
|
|
||||||
async def _async_set_colors(
|
async def _async_set_colors(
|
||||||
self,
|
self, colors: dict[ColorComponent, int], transition: float | None = None
|
||||||
colors: dict[ColorComponent, int],
|
|
||||||
transition: float | None = None,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set (multiple) defined colors to given value(s)."""
|
"""Set (multiple) defined colors to given value(s)."""
|
||||||
# prefer the (new) combined color property
|
# prefer the (new) combined color property
|
||||||
@ -405,14 +361,9 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
zwave_transition = {TRANSITION_DURATION_OPTION: "default"}
|
zwave_transition = {TRANSITION_DURATION_OPTION: "default"}
|
||||||
|
|
||||||
# setting a value requires setting targetValue
|
# setting a value requires setting targetValue
|
||||||
if self._supports_dimming:
|
await self._async_set_value(
|
||||||
await self._async_set_value(
|
self._target_brightness, zwave_brightness, zwave_transition
|
||||||
self._target_brightness, zwave_brightness, zwave_transition
|
)
|
||||||
)
|
|
||||||
else:
|
|
||||||
await self._async_set_value(
|
|
||||||
self._target_brightness, zwave_brightness > 0, zwave_transition
|
|
||||||
)
|
|
||||||
# We do an optimistic state update when setting to a previous value
|
# We do an optimistic state update when setting to a previous value
|
||||||
# to avoid waiting for the value to be updated from the device which is
|
# to avoid waiting for the value to be updated from the device which is
|
||||||
# typically delayed and causes a confusing UX.
|
# typically delayed and causes a confusing UX.
|
||||||
@ -476,8 +427,15 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
"""Calculate light colors."""
|
"""Calculate light colors."""
|
||||||
(red_val, green_val, blue_val, ww_val, cw_val) = self._get_color_values()
|
(red_val, green_val, blue_val, ww_val, cw_val) = self._get_color_values()
|
||||||
|
|
||||||
if self._current_color and isinstance(self._current_color.value, dict):
|
# prefer the (new) combined color property
|
||||||
multi_color = self._current_color.value
|
# https://github.com/zwave-js/node-zwave-js/pull/1782
|
||||||
|
combined_color_val = self.get_zwave_value(
|
||||||
|
CURRENT_COLOR_PROPERTY,
|
||||||
|
CommandClass.SWITCH_COLOR,
|
||||||
|
value_property_key=None,
|
||||||
|
)
|
||||||
|
if combined_color_val and isinstance(combined_color_val.value, dict):
|
||||||
|
multi_color = combined_color_val.value
|
||||||
else:
|
else:
|
||||||
multi_color = {}
|
multi_color = {}
|
||||||
|
|
||||||
@ -528,10 +486,11 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
|
|||||||
self._color_mode = ColorMode.RGBW
|
self._color_mode = ColorMode.RGBW
|
||||||
|
|
||||||
|
|
||||||
class ZwaveColorOnOffLight(ZwaveLight):
|
class ZwaveBlackIsOffLight(ZwaveLight):
|
||||||
"""Representation of a colored Z-Wave light with an optional binary switch to turn on/off.
|
"""Representation of a Z-Wave light where setting the color to black turns it off.
|
||||||
|
|
||||||
Dimming for RGB lights is realized by scaling the color channels.
|
Currently only supports lights with RGB, no color temperature, and no white
|
||||||
|
channels.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -540,137 +499,61 @@ class ZwaveColorOnOffLight(ZwaveLight):
|
|||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
super().__init__(config_entry, driver, info)
|
super().__init__(config_entry, driver, info)
|
||||||
|
|
||||||
self._last_on_color: dict[ColorComponent, int] | None = None
|
self._last_color: dict[str, int] | None = None
|
||||||
self._last_brightness: int | None = None
|
self._supported_color_modes.discard(ColorMode.BRIGHTNESS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brightness(self) -> int | None:
|
def brightness(self) -> int:
|
||||||
"""Return the brightness of this light between 0..255.
|
"""Return the brightness of this light between 0..255."""
|
||||||
|
return 255
|
||||||
|
|
||||||
Z-Wave multilevel switches use a range of [0, 99] to control brightness.
|
@property
|
||||||
"""
|
def is_on(self) -> bool | None:
|
||||||
|
"""Return true if device is on (brightness above 0)."""
|
||||||
if self.info.primary_value.value is None:
|
if self.info.primary_value.value is None:
|
||||||
return None
|
return None
|
||||||
if self._target_brightness and self.info.primary_value.value is False:
|
return any(value != 0 for value in self.info.primary_value.value.values())
|
||||||
# Binary switch exists and is turned off
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Brightness is encoded in the color channels by scaling them lower than 255
|
|
||||||
color_values = [
|
|
||||||
v.value
|
|
||||||
for v in self._get_color_values()
|
|
||||||
if v is not None and v.value is not None
|
|
||||||
]
|
|
||||||
return max(color_values) if color_values else 0
|
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
|
|
||||||
if (
|
if (
|
||||||
kwargs.get(ATTR_RGBW_COLOR) is not None
|
kwargs.get(ATTR_RGBW_COLOR) is not None
|
||||||
or kwargs.get(ATTR_COLOR_TEMP) is not None
|
or kwargs.get(ATTR_COLOR_TEMP) is not None
|
||||||
|
or kwargs.get(ATTR_HS_COLOR) is not None
|
||||||
):
|
):
|
||||||
# RGBW and color temp are not supported in this mode,
|
|
||||||
# delegate to the parent class
|
|
||||||
await super().async_turn_on(**kwargs)
|
await super().async_turn_on(**kwargs)
|
||||||
return
|
return
|
||||||
|
|
||||||
transition = kwargs.get(ATTR_TRANSITION)
|
transition = kwargs.get(ATTR_TRANSITION)
|
||||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
# turn on light to last color if known, otherwise set to white
|
||||||
hs_color = kwargs.get(ATTR_HS_COLOR)
|
if self._last_color is not None:
|
||||||
new_colors: dict[ColorComponent, int] | None = None
|
await self._async_set_colors(
|
||||||
scale: float | None = None
|
{
|
||||||
|
ColorComponent.RED: self._last_color["red"],
|
||||||
if brightness is None and hs_color is None:
|
ColorComponent.GREEN: self._last_color["green"],
|
||||||
# Turned on without specifying brightness or color
|
ColorComponent.BLUE: self._last_color["blue"],
|
||||||
if self._last_on_color is not None:
|
},
|
||||||
if self._target_brightness:
|
transition,
|
||||||
# Color is already set, use the binary switch to turn on
|
)
|
||||||
await self._async_set_brightness(None, transition)
|
else:
|
||||||
return
|
await self._async_set_colors(
|
||||||
|
{
|
||||||
# Preserve the previous color
|
|
||||||
new_colors = self._last_on_color
|
|
||||||
elif self._supports_color:
|
|
||||||
# Turned on for the first time. Make it white
|
|
||||||
new_colors = {
|
|
||||||
ColorComponent.RED: 255,
|
ColorComponent.RED: 255,
|
||||||
ColorComponent.GREEN: 255,
|
ColorComponent.GREEN: 255,
|
||||||
ColorComponent.BLUE: 255,
|
ColorComponent.BLUE: 255,
|
||||||
}
|
},
|
||||||
elif brightness is not None:
|
transition,
|
||||||
# If brightness gets set, preserve the color and mix it with the new brightness
|
|
||||||
if self.color_mode == ColorMode.HS:
|
|
||||||
scale = brightness / 255
|
|
||||||
if (
|
|
||||||
self._last_on_color is not None
|
|
||||||
and None not in self._last_on_color.values()
|
|
||||||
):
|
|
||||||
# Changed brightness from 0 to >0
|
|
||||||
old_brightness = max(self._last_on_color.values())
|
|
||||||
new_scale = brightness / old_brightness
|
|
||||||
scale = new_scale
|
|
||||||
new_colors = {}
|
|
||||||
for color, value in self._last_on_color.items():
|
|
||||||
new_colors[color] = round(value * new_scale)
|
|
||||||
elif hs_color is None and self._color_mode == ColorMode.HS:
|
|
||||||
hs_color = self._hs_color
|
|
||||||
elif hs_color is not None and brightness is None:
|
|
||||||
# Turned on by using the color controls
|
|
||||||
current_brightness = self.brightness
|
|
||||||
if current_brightness == 0 and self._last_brightness is not None:
|
|
||||||
# Use the last brightness value if the light is currently off
|
|
||||||
scale = self._last_brightness / 255
|
|
||||||
elif current_brightness is not None:
|
|
||||||
scale = current_brightness / 255
|
|
||||||
|
|
||||||
# Reset last color until turning off again
|
|
||||||
self._last_on_color = None
|
|
||||||
|
|
||||||
if new_colors is None:
|
|
||||||
new_colors = self._get_new_colors(
|
|
||||||
hs_color=hs_color, color_temp=None, rgbw=None, brightness_scale=scale
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if new_colors is not None:
|
|
||||||
await self._async_set_colors(new_colors, transition)
|
|
||||||
|
|
||||||
# Turn the binary switch on if there is one
|
|
||||||
await self._async_set_brightness(brightness, transition)
|
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the light off."""
|
"""Turn the light off."""
|
||||||
|
self._last_color = self.info.primary_value.value
|
||||||
# Remember last color and brightness to restore it when turning on
|
await self._async_set_colors(
|
||||||
self._last_brightness = self.brightness
|
{
|
||||||
if self._current_color and isinstance(self._current_color.value, dict):
|
|
||||||
red = self._current_color.value.get(COLOR_SWITCH_COMBINED_RED)
|
|
||||||
green = self._current_color.value.get(COLOR_SWITCH_COMBINED_GREEN)
|
|
||||||
blue = self._current_color.value.get(COLOR_SWITCH_COMBINED_BLUE)
|
|
||||||
|
|
||||||
last_color: dict[ColorComponent, int] = {}
|
|
||||||
if red is not None:
|
|
||||||
last_color[ColorComponent.RED] = red
|
|
||||||
if green is not None:
|
|
||||||
last_color[ColorComponent.GREEN] = green
|
|
||||||
if blue is not None:
|
|
||||||
last_color[ColorComponent.BLUE] = blue
|
|
||||||
|
|
||||||
if last_color:
|
|
||||||
self._last_on_color = last_color
|
|
||||||
|
|
||||||
if self._target_brightness:
|
|
||||||
# Turn off the binary switch only
|
|
||||||
await self._async_set_brightness(0, kwargs.get(ATTR_TRANSITION))
|
|
||||||
else:
|
|
||||||
# turn off all color channels
|
|
||||||
colors = {
|
|
||||||
ColorComponent.RED: 0,
|
ColorComponent.RED: 0,
|
||||||
ColorComponent.GREEN: 0,
|
ColorComponent.GREEN: 0,
|
||||||
ColorComponent.BLUE: 0,
|
ColorComponent.BLUE: 0,
|
||||||
}
|
},
|
||||||
|
kwargs.get(ATTR_TRANSITION),
|
||||||
await self._async_set_colors(
|
)
|
||||||
colors,
|
await self._async_set_brightness(0, kwargs.get(ATTR_TRANSITION))
|
||||||
kwargs.get(ATTR_TRANSITION),
|
|
||||||
)
|
|
||||||
|
@ -8,7 +8,6 @@ from homeassistant.components.light import (
|
|||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_MODE,
|
ATTR_COLOR_MODE,
|
||||||
ATTR_COLOR_TEMP,
|
ATTR_COLOR_TEMP,
|
||||||
ATTR_HS_COLOR,
|
|
||||||
ATTR_MAX_MIREDS,
|
ATTR_MAX_MIREDS,
|
||||||
ATTR_MIN_MIREDS,
|
ATTR_MIN_MIREDS,
|
||||||
ATTR_RGB_COLOR,
|
ATTR_RGB_COLOR,
|
||||||
@ -38,8 +37,8 @@ from .common import (
|
|||||||
ZEN_31_ENTITY,
|
ZEN_31_ENTITY,
|
||||||
)
|
)
|
||||||
|
|
||||||
ZDB5100_ENTITY = "light.matrix_office"
|
|
||||||
HSM200_V1_ENTITY = "light.hsm200"
|
HSM200_V1_ENTITY = "light.hsm200"
|
||||||
|
ZDB5100_ENTITY = "light.matrix_office"
|
||||||
|
|
||||||
|
|
||||||
async def test_light(
|
async def test_light(
|
||||||
@ -511,388 +510,14 @@ async def test_light_none_color_value(
|
|||||||
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["hs"]
|
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == ["hs"]
|
||||||
|
|
||||||
|
|
||||||
async def test_light_on_off_color(
|
async def test_black_is_off(
|
||||||
hass: HomeAssistant, client, logic_group_zdb5100, integration
|
|
||||||
) -> None:
|
|
||||||
"""Test the light entity for RGB lights without dimming support."""
|
|
||||||
node = logic_group_zdb5100
|
|
||||||
state = hass.states.get(ZDB5100_ENTITY)
|
|
||||||
assert state.state == STATE_OFF
|
|
||||||
|
|
||||||
async def update_color(red: int, green: int, blue: int) -> None:
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 2, # red
|
|
||||||
"newValue": red,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "red",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 3, # green
|
|
||||||
"newValue": green,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "green",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 4, # blue
|
|
||||||
"newValue": blue,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "blue",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "currentColor",
|
|
||||||
"newValue": {
|
|
||||||
"red": red,
|
|
||||||
"green": green,
|
|
||||||
"blue": blue,
|
|
||||||
},
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
async def update_switch_state(state: bool) -> None:
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Binary Switch",
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "currentValue",
|
|
||||||
"newValue": state,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentValue",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# Turn on the light. Since this is the first call, the light should default to white
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 2
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {
|
|
||||||
"red": 255,
|
|
||||||
"green": 255,
|
|
||||||
"blue": 255,
|
|
||||||
}
|
|
||||||
|
|
||||||
args = client.async_send_command.call_args_list[1][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetValue",
|
|
||||||
}
|
|
||||||
assert args["value"] is True
|
|
||||||
|
|
||||||
# Force the light to turn off
|
|
||||||
await update_switch_state(False)
|
|
||||||
|
|
||||||
state = hass.states.get(ZDB5100_ENTITY)
|
|
||||||
assert state.state == STATE_OFF
|
|
||||||
|
|
||||||
# Force the light to turn on (green)
|
|
||||||
await update_color(0, 255, 0)
|
|
||||||
await update_switch_state(True)
|
|
||||||
|
|
||||||
state = hass.states.get(ZDB5100_ENTITY)
|
|
||||||
assert state.state == STATE_ON
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Set the brightness to 128. This should be encoded in the color value
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ZDB5100_ENTITY, ATTR_BRIGHTNESS: 128},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 2
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {
|
|
||||||
"red": 0,
|
|
||||||
"green": 128,
|
|
||||||
"blue": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
args = client.async_send_command.call_args_list[1][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetValue",
|
|
||||||
}
|
|
||||||
assert args["value"] is True
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Force the light to turn on (green, 50%)
|
|
||||||
await update_color(0, 128, 0)
|
|
||||||
|
|
||||||
# Set the color to red. This should preserve the previous brightness value
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ZDB5100_ENTITY, ATTR_HS_COLOR: (0, 100)},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 2
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {
|
|
||||||
"red": 128,
|
|
||||||
"green": 0,
|
|
||||||
"blue": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
args = client.async_send_command.call_args_list[1][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetValue",
|
|
||||||
}
|
|
||||||
assert args["value"] is True
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Force the light to turn on (red, 50%)
|
|
||||||
await update_color(128, 0, 0)
|
|
||||||
|
|
||||||
# Turn the device off. This should only affect the binary switch, not the color
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_OFF,
|
|
||||||
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetValue",
|
|
||||||
}
|
|
||||||
assert args["value"] is False
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Force the light to turn off
|
|
||||||
await update_switch_state(False)
|
|
||||||
|
|
||||||
# Turn the device on again. This should only affect the binary switch, not the color
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 37,
|
|
||||||
"endpoint": 1,
|
|
||||||
"property": "targetValue",
|
|
||||||
}
|
|
||||||
assert args["value"] is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_light_color_only(
|
|
||||||
hass: HomeAssistant, client, express_controls_ezmultipli, integration
|
hass: HomeAssistant, client, express_controls_ezmultipli, integration
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the light entity for RGB lights with Color Switch CC only."""
|
"""Test the black is off light entity."""
|
||||||
node = express_controls_ezmultipli
|
node = express_controls_ezmultipli
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
state = hass.states.get(HSM200_V1_ENTITY)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
async def update_color(red: int, green: int, blue: int) -> None:
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 2, # red
|
|
||||||
"newValue": red,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "red",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 3, # green
|
|
||||||
"newValue": green,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "green",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "currentColor",
|
|
||||||
"propertyKey": 4, # blue
|
|
||||||
"newValue": blue,
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
"propertyKeyName": "blue",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
event = Event(
|
|
||||||
type="value updated",
|
|
||||||
data={
|
|
||||||
"source": "node",
|
|
||||||
"event": "value updated",
|
|
||||||
"nodeId": node.node_id,
|
|
||||||
"args": {
|
|
||||||
"commandClassName": "Color Switch",
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "currentColor",
|
|
||||||
"newValue": {
|
|
||||||
"red": red,
|
|
||||||
"green": green,
|
|
||||||
"blue": blue,
|
|
||||||
},
|
|
||||||
"prevValue": None,
|
|
||||||
"propertyName": "currentColor",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
node.receive_event(event)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# Attempt to turn on the light and ensure it defaults to white
|
# Attempt to turn on the light and ensure it defaults to white
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
@ -914,14 +539,64 @@ async def test_light_color_only(
|
|||||||
client.async_send_command.reset_mock()
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
# Force the light to turn off
|
# Force the light to turn off
|
||||||
await update_color(0, 0, 0)
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Color Switch",
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "currentColor",
|
||||||
|
"newValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 0,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"propertyName": "currentColor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
state = hass.states.get(HSM200_V1_ENTITY)
|
||||||
assert state.state == STATE_OFF
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
# Force the light to turn on (50% green)
|
# Force the light to turn on
|
||||||
await update_color(0, 128, 0)
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Color Switch",
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "currentColor",
|
||||||
|
"newValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 0,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"propertyName": "currentColor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
state = hass.states.get(HSM200_V1_ENTITY)
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
@ -944,9 +619,6 @@ async def test_light_color_only(
|
|||||||
|
|
||||||
client.async_send_command.reset_mock()
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
# Force the light to turn off
|
|
||||||
await update_color(0, 0, 0)
|
|
||||||
|
|
||||||
# Assert that the last color is restored
|
# Assert that the last color is restored
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
@ -963,131 +635,11 @@ async def test_light_color_only(
|
|||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
"property": "targetColor",
|
"property": "targetColor",
|
||||||
}
|
}
|
||||||
assert args["value"] == {"red": 0, "green": 128, "blue": 0}
|
assert args["value"] == {"red": 0, "green": 255, "blue": 0}
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
# Force the light to turn on (50% green)
|
# Force the light to turn on
|
||||||
await update_color(0, 128, 0)
|
|
||||||
|
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
|
||||||
assert state.state == STATE_ON
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Assert that the brightness is preserved when changing colors
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY, ATTR_RGB_COLOR: (255, 0, 0)},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {"red": 128, "green": 0, "blue": 0}
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Force the light to turn on (50% red)
|
|
||||||
await update_color(128, 0, 0)
|
|
||||||
|
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
|
||||||
assert state.state == STATE_ON
|
|
||||||
|
|
||||||
# Assert that the color is preserved when changing brightness
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY, ATTR_BRIGHTNESS: 69},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {"red": 69, "green": 0, "blue": 0}
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
await update_color(69, 0, 0)
|
|
||||||
|
|
||||||
# Turn off again
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_OFF,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
await update_color(0, 0, 0)
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Assert that the color is preserved when turning on with brightness
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY, ATTR_BRIGHTNESS: 123},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {"red": 123, "green": 0, "blue": 0}
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
await update_color(123, 0, 0)
|
|
||||||
|
|
||||||
# Turn off again
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_OFF,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
await update_color(0, 0, 0)
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Assert that the brightness is preserved when turning on with color
|
|
||||||
await hass.services.async_call(
|
|
||||||
LIGHT_DOMAIN,
|
|
||||||
SERVICE_TURN_ON,
|
|
||||||
{ATTR_ENTITY_ID: HSM200_V1_ENTITY, ATTR_HS_COLOR: (240, 100)},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
assert len(client.async_send_command.call_args_list) == 1
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
|
||||||
assert args["command"] == "node.set_value"
|
|
||||||
assert args["nodeId"] == node.node_id
|
|
||||||
assert args["valueId"] == {
|
|
||||||
"commandClass": 51,
|
|
||||||
"endpoint": 0,
|
|
||||||
"property": "targetColor",
|
|
||||||
}
|
|
||||||
assert args["value"] == {"red": 0, "green": 0, "blue": 123}
|
|
||||||
|
|
||||||
client.async_send_command.reset_mock()
|
|
||||||
|
|
||||||
# Clear the color value to trigger an unknown state
|
|
||||||
event = Event(
|
event = Event(
|
||||||
type="value updated",
|
type="value updated",
|
||||||
data={
|
data={
|
||||||
@ -1100,14 +652,17 @@ async def test_light_color_only(
|
|||||||
"endpoint": 0,
|
"endpoint": 0,
|
||||||
"property": "currentColor",
|
"property": "currentColor",
|
||||||
"newValue": None,
|
"newValue": None,
|
||||||
"prevValue": None,
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
"propertyName": "currentColor",
|
"propertyName": "currentColor",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
node.receive_event(event)
|
node.receive_event(event)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get(HSM200_V1_ENTITY)
|
state = hass.states.get(HSM200_V1_ENTITY)
|
||||||
assert state.state == STATE_UNKNOWN
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
@ -1132,6 +687,183 @@ async def test_light_color_only(
|
|||||||
assert args["value"] == {"red": 255, "green": 76, "blue": 255}
|
assert args["value"] == {"red": 255, "green": 76, "blue": 255}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_black_is_off_zdb5100(
|
||||||
|
hass: HomeAssistant, client, logic_group_zdb5100, integration
|
||||||
|
) -> None:
|
||||||
|
"""Test the black is off light entity."""
|
||||||
|
node = logic_group_zdb5100
|
||||||
|
state = hass.states.get(ZDB5100_ENTITY)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Attempt to turn on the light and ensure it defaults to white
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(client.async_send_command.call_args_list) == 1
|
||||||
|
args = client.async_send_command.call_args_list[0][0][0]
|
||||||
|
assert args["command"] == "node.set_value"
|
||||||
|
assert args["nodeId"] == node.node_id
|
||||||
|
assert args["valueId"] == {
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "targetColor",
|
||||||
|
}
|
||||||
|
assert args["value"] == {"red": 255, "green": 255, "blue": 255}
|
||||||
|
|
||||||
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
|
# Force the light to turn off
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Color Switch",
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "currentColor",
|
||||||
|
"newValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 0,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"propertyName": "currentColor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get(ZDB5100_ENTITY)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
|
||||||
|
# Force the light to turn on
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Color Switch",
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "currentColor",
|
||||||
|
"newValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 0,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"propertyName": "currentColor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get(ZDB5100_ENTITY)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(client.async_send_command.call_args_list) == 1
|
||||||
|
args = client.async_send_command.call_args_list[0][0][0]
|
||||||
|
assert args["command"] == "node.set_value"
|
||||||
|
assert args["nodeId"] == node.node_id
|
||||||
|
assert args["valueId"] == {
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "targetColor",
|
||||||
|
}
|
||||||
|
assert args["value"] == {"red": 0, "green": 0, "blue": 0}
|
||||||
|
|
||||||
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
|
# Assert that the last color is restored
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ZDB5100_ENTITY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(client.async_send_command.call_args_list) == 1
|
||||||
|
args = client.async_send_command.call_args_list[0][0][0]
|
||||||
|
assert args["command"] == "node.set_value"
|
||||||
|
assert args["nodeId"] == node.node_id
|
||||||
|
assert args["valueId"] == {
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "targetColor",
|
||||||
|
}
|
||||||
|
assert args["value"] == {"red": 0, "green": 255, "blue": 0}
|
||||||
|
|
||||||
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
|
# Force the light to turn on
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Color Switch",
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "currentColor",
|
||||||
|
"newValue": None,
|
||||||
|
"prevValue": {
|
||||||
|
"red": 0,
|
||||||
|
"green": 255,
|
||||||
|
"blue": 0,
|
||||||
|
},
|
||||||
|
"propertyName": "currentColor",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get(ZDB5100_ENTITY)
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
client.async_send_command.reset_mock()
|
||||||
|
|
||||||
|
# Assert that call fails if attribute is added to service call
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: ZDB5100_ENTITY, ATTR_RGBW_COLOR: (255, 76, 255, 0)},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(client.async_send_command.call_args_list) == 1
|
||||||
|
args = client.async_send_command.call_args_list[0][0][0]
|
||||||
|
assert args["command"] == "node.set_value"
|
||||||
|
assert args["nodeId"] == node.node_id
|
||||||
|
assert args["valueId"] == {
|
||||||
|
"commandClass": 51,
|
||||||
|
"endpoint": 1,
|
||||||
|
"property": "targetColor",
|
||||||
|
}
|
||||||
|
assert args["value"] == {"red": 255, "green": 76, "blue": 255}
|
||||||
|
|
||||||
|
|
||||||
async def test_basic_cc_light(
|
async def test_basic_cc_light(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user