Add guards to ZHA light groups when using hs color mode (#75599)

* fix polling currentSaturation

* Guard light groups against enhanced current hue

* Use XY if all group members do not support HS

* add config option to always prefer XY color mode

* use correct enum

* remove periods
This commit is contained in:
David F. Mulcahey 2022-07-26 16:42:08 -04:00 committed by GitHub
parent 2b1a5e5549
commit e2dd2c9424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 12 deletions

View File

@ -129,6 +129,7 @@ CONF_DATABASE = "database_path"
CONF_DEFAULT_LIGHT_TRANSITION = "default_light_transition" CONF_DEFAULT_LIGHT_TRANSITION = "default_light_transition"
CONF_DEVICE_CONFIG = "device_config" CONF_DEVICE_CONFIG = "device_config"
CONF_ENABLE_ENHANCED_LIGHT_TRANSITION = "enhanced_light_transition" CONF_ENABLE_ENHANCED_LIGHT_TRANSITION = "enhanced_light_transition"
CONF_ALWAYS_PREFER_XY_COLOR_MODE = "always_prefer_xy_color_mode"
CONF_ENABLE_IDENTIFY_ON_JOIN = "enable_identify_on_join" CONF_ENABLE_IDENTIFY_ON_JOIN = "enable_identify_on_join"
CONF_ENABLE_QUIRKS = "enable_quirks" CONF_ENABLE_QUIRKS = "enable_quirks"
CONF_FLOWCONTROL = "flow_control" CONF_FLOWCONTROL = "flow_control"
@ -145,6 +146,7 @@ CONF_ZHA_OPTIONS_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION): cv.positive_int, vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION): cv.positive_int,
vol.Required(CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, default=False): cv.boolean, vol.Required(CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, default=False): cv.boolean,
vol.Required(CONF_ALWAYS_PREFER_XY_COLOR_MODE, default=True): cv.boolean,
vol.Required(CONF_ENABLE_IDENTIFY_ON_JOIN, default=True): cv.boolean, vol.Required(CONF_ENABLE_IDENTIFY_ON_JOIN, default=True): cv.boolean,
vol.Optional( vol.Optional(
CONF_CONSIDER_UNAVAILABLE_MAINS, CONF_CONSIDER_UNAVAILABLE_MAINS,

View File

@ -40,6 +40,7 @@ from .core.const import (
CHANNEL_COLOR, CHANNEL_COLOR,
CHANNEL_LEVEL, CHANNEL_LEVEL,
CHANNEL_ON_OFF, CHANNEL_ON_OFF,
CONF_ALWAYS_PREFER_XY_COLOR_MODE,
CONF_DEFAULT_LIGHT_TRANSITION, CONF_DEFAULT_LIGHT_TRANSITION,
CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, CONF_ENABLE_ENHANCED_LIGHT_TRANSITION,
DATA_ZHA, DATA_ZHA,
@ -120,6 +121,7 @@ class BaseLight(LogMixin, light.LightEntity):
self._off_brightness: int | None = None self._off_brightness: int | None = None
self._zha_config_transition = self._DEFAULT_MIN_TRANSITION_TIME self._zha_config_transition = self._DEFAULT_MIN_TRANSITION_TIME
self._zha_config_enhanced_light_transition: bool = False self._zha_config_enhanced_light_transition: bool = False
self._zha_config_always_prefer_xy_color_mode: bool = True
self._on_off_channel = None self._on_off_channel = None
self._level_channel = None self._level_channel = None
self._color_channel = None self._color_channel = None
@ -280,7 +282,10 @@ class BaseLight(LogMixin, light.LightEntity):
self._attr_hs_color = None self._attr_hs_color = None
if hs_color is not None: if hs_color is not None:
if self._color_channel.enhanced_hue_supported: if (
not isinstance(self, LightGroup)
and self._color_channel.enhanced_hue_supported
):
result = await self._color_channel.enhanced_move_to_hue_and_saturation( result = await self._color_channel.enhanced_move_to_hue_and_saturation(
int(hs_color[0] * 65535 / 360), int(hs_color[0] * 65535 / 360),
int(hs_color[1] * 2.54), int(hs_color[1] * 2.54),
@ -418,6 +423,13 @@ class Light(BaseLight, ZhaEntity):
self._cancel_refresh_handle = None self._cancel_refresh_handle = None
effect_list = [] effect_list = []
self._zha_config_always_prefer_xy_color_mode = async_get_zha_config_value(
zha_device.gateway.config_entry,
ZHA_OPTIONS,
CONF_ALWAYS_PREFER_XY_COLOR_MODE,
True,
)
self._attr_supported_color_modes = {ColorMode.ONOFF} self._attr_supported_color_modes = {ColorMode.ONOFF}
if self._level_channel: if self._level_channel:
self._attr_supported_color_modes.add(ColorMode.BRIGHTNESS) self._attr_supported_color_modes.add(ColorMode.BRIGHTNESS)
@ -429,9 +441,9 @@ class Light(BaseLight, ZhaEntity):
self._attr_supported_color_modes.add(ColorMode.COLOR_TEMP) self._attr_supported_color_modes.add(ColorMode.COLOR_TEMP)
self._attr_color_temp = self._color_channel.color_temperature self._attr_color_temp = self._color_channel.color_temperature
if ( if self._color_channel.xy_supported and (
self._color_channel.xy_supported self._zha_config_always_prefer_xy_color_mode
and not self._color_channel.hs_supported or not self._color_channel.hs_supported
): ):
self._attr_supported_color_modes.add(ColorMode.XY) self._attr_supported_color_modes.add(ColorMode.XY)
curr_x = self._color_channel.current_x curr_x = self._color_channel.current_x
@ -441,7 +453,10 @@ class Light(BaseLight, ZhaEntity):
else: else:
self._attr_xy_color = (0, 0) self._attr_xy_color = (0, 0)
if self._color_channel.hs_supported: if (
self._color_channel.hs_supported
and not self._zha_config_always_prefer_xy_color_mode
):
self._attr_supported_color_modes.add(ColorMode.HS) self._attr_supported_color_modes.add(ColorMode.HS)
if self._color_channel.enhanced_hue_supported: if self._color_channel.enhanced_hue_supported:
curr_hue = self._color_channel.enhanced_current_hue * 65535 / 360 curr_hue = self._color_channel.enhanced_current_hue * 65535 / 360
@ -572,12 +587,16 @@ class Light(BaseLight, ZhaEntity):
"current_x", "current_x",
"current_y", "current_y",
] ]
if self._color_channel.enhanced_hue_supported: if (
not self._zha_config_always_prefer_xy_color_mode
and self._color_channel.enhanced_hue_supported
):
attributes.append("enhanced_current_hue") attributes.append("enhanced_current_hue")
attributes.append("current_saturation") attributes.append("current_saturation")
if ( if (
self._color_channel.hs_supported self._color_channel.hs_supported
and not self._color_channel.enhanced_hue_supported and not self._color_channel.enhanced_hue_supported
and not self._zha_config_always_prefer_xy_color_mode
): ):
attributes.append("current_hue") attributes.append("current_hue")
attributes.append("current_saturation") attributes.append("current_saturation")
@ -598,7 +617,10 @@ class Light(BaseLight, ZhaEntity):
self._attr_color_temp = color_temp self._attr_color_temp = color_temp
self._attr_xy_color = None self._attr_xy_color = None
self._attr_hs_color = None self._attr_hs_color = None
elif color_mode == Color.ColorMode.Hue_and_saturation: elif (
color_mode == Color.ColorMode.Hue_and_saturation
and not self._zha_config_always_prefer_xy_color_mode
):
self._attr_color_mode = ColorMode.HS self._attr_color_mode = ColorMode.HS
if self._color_channel.enhanced_hue_supported: if self._color_channel.enhanced_hue_supported:
current_hue = results.get("enhanced_current_hue") current_hue = results.get("enhanced_current_hue")
@ -610,12 +632,12 @@ class Light(BaseLight, ZhaEntity):
int(current_hue * 360 / 65535) int(current_hue * 360 / 65535)
if self._color_channel.enhanced_hue_supported if self._color_channel.enhanced_hue_supported
else int(current_hue * 360 / 254), else int(current_hue * 360 / 254),
int(current_saturation / 254), int(current_saturation / 2.54),
) )
self._attr_xy_color = None self._attr_xy_color = None
self._attr_color_temp = None self._attr_color_temp = None
else: else:
self._attr_color_mode = Color.ColorMode.X_and_Y self._attr_color_mode = ColorMode.XY
color_x = results.get("current_x") color_x = results.get("current_x")
color_y = results.get("current_y") color_y = results.get("current_y")
if color_x is not None and color_y is not None: if color_x is not None and color_y is not None:
@ -704,6 +726,12 @@ class LightGroup(BaseLight, ZhaGroupEntity):
CONF_DEFAULT_LIGHT_TRANSITION, CONF_DEFAULT_LIGHT_TRANSITION,
0, 0,
) )
self._zha_config_always_prefer_xy_color_mode = async_get_zha_config_value(
zha_device.gateway.config_entry,
ZHA_OPTIONS,
CONF_ALWAYS_PREFER_XY_COLOR_MODE,
True,
)
self._zha_config_enhanced_light_transition = False self._zha_config_enhanced_light_transition = False
self._attr_color_mode = None self._attr_color_mode = None
@ -753,9 +781,10 @@ class LightGroup(BaseLight, ZhaGroupEntity):
on_states, light.ATTR_XY_COLOR, reduce=helpers.mean_tuple on_states, light.ATTR_XY_COLOR, reduce=helpers.mean_tuple
) )
self._attr_hs_color = helpers.reduce_attribute( if not self._zha_config_always_prefer_xy_color_mode:
on_states, light.ATTR_HS_COLOR, reduce=helpers.mean_tuple self._attr_hs_color = helpers.reduce_attribute(
) on_states, light.ATTR_HS_COLOR, reduce=helpers.mean_tuple
)
self._attr_color_temp = helpers.reduce_attribute( self._attr_color_temp = helpers.reduce_attribute(
on_states, light.ATTR_COLOR_TEMP on_states, light.ATTR_COLOR_TEMP
@ -794,6 +823,11 @@ class LightGroup(BaseLight, ZhaGroupEntity):
if ColorMode.BRIGHTNESS in color_mode_count: if ColorMode.BRIGHTNESS in color_mode_count:
color_mode_count[ColorMode.BRIGHTNESS] = 0 color_mode_count[ColorMode.BRIGHTNESS] = 0
self._attr_color_mode = color_mode_count.most_common(1)[0][0] self._attr_color_mode = color_mode_count.most_common(1)[0][0]
if self._attr_color_mode == ColorMode.HS and (
color_mode_count[ColorMode.HS] != len(self._group.members)
or self._zha_config_always_prefer_xy_color_mode
): # switch to XY if all members do not support HS
self._attr_color_mode = ColorMode.XY
self._attr_supported_color_modes = None self._attr_supported_color_modes = None
all_supported_color_modes = list( all_supported_color_modes = list(

View File

@ -38,6 +38,7 @@
"zha_options": { "zha_options": {
"title": "Global Options", "title": "Global Options",
"enhanced_light_transition": "Enable enhanced light color/temperature transition from an off-state", "enhanced_light_transition": "Enable enhanced light color/temperature transition from an off-state",
"always_prefer_xy_color_mode": "Always prefer XY color mode",
"enable_identify_on_join": "Enable identify effect when devices join the network", "enable_identify_on_join": "Enable identify effect when devices join the network",
"default_light_transition": "Default light transition time (seconds)", "default_light_transition": "Default light transition time (seconds)",
"consider_unavailable_mains": "Consider mains powered devices unavailable after (seconds)", "consider_unavailable_mains": "Consider mains powered devices unavailable after (seconds)",

View File

@ -51,6 +51,7 @@
"default_light_transition": "Default light transition time (seconds)", "default_light_transition": "Default light transition time (seconds)",
"enable_identify_on_join": "Enable identify effect when devices join the network", "enable_identify_on_join": "Enable identify effect when devices join the network",
"enhanced_light_transition": "Enable enhanced light color/temperature transition from an off-state", "enhanced_light_transition": "Enable enhanced light color/temperature transition from an off-state",
"always_prefer_xy_color_mode": "Always prefer XY color mode",
"title": "Global Options" "title": "Global Options"
} }
}, },