Skip to content

Commit

Permalink
Allow the ZHA default light transition time to be configured as a flo…
Browse files Browse the repository at this point in the history
…at (#92075)
  • Loading branch information
puddly committed Apr 26, 2023
1 parent 3c44c74 commit 4f660cc
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 27 deletions.
4 changes: 3 additions & 1 deletion homeassistant/components/zha/core/const.py
Expand Up @@ -151,7 +151,9 @@

CONF_ZHA_OPTIONS_SCHEMA = vol.Schema(
{
vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION, default=0): cv.positive_int,
vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION, default=0): vol.All(
vol.Coerce(float), vol.Range(min=0, max=2**16 / 10)
),
vol.Required(CONF_ENABLE_ENHANCED_LIGHT_TRANSITION, default=False): cv.boolean,
vol.Required(CONF_ENABLE_LIGHT_TRANSITIONING_FLAG, default=True): cv.boolean,
vol.Required(CONF_ALWAYS_PREFER_XY_COLOR_MODE, default=True): cv.boolean,
Expand Down
48 changes: 27 additions & 21 deletions homeassistant/components/zha/light.py
Expand Up @@ -113,7 +113,7 @@ class BaseLight(LogMixin, light.LightEntity):
"""Operations common to all light entities."""

_FORCE_ON = False
_DEFAULT_MIN_TRANSITION_TIME = 0
_DEFAULT_MIN_TRANSITION_TIME: float = 0

def __init__(self, *args, **kwargs):
"""Initialize the light."""
Expand Down Expand Up @@ -181,9 +181,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
transition = kwargs.get(light.ATTR_TRANSITION)
duration = (
transition * 10
if transition is not None
else self._zha_config_transition * 10
transition if transition is not None else self._zha_config_transition
) or (
# if 0 is passed in some devices still need the minimum default
self._DEFAULT_MIN_TRANSITION_TIME
Expand All @@ -210,7 +208,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
) and self._zha_config_enable_light_transitioning_flag
transition_time = (
(
duration / 10 + DEFAULT_EXTRA_TRANSITION_DELAY_SHORT
duration + DEFAULT_EXTRA_TRANSITION_DELAY_SHORT
if (
(brightness is not None or transition is not None)
and brightness_supported(self._attr_supported_color_modes)
Expand Down Expand Up @@ -297,7 +295,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
# After that, we set it to the desired color/temperature with no transition.
result = await self._level_cluster_handler.move_to_level_with_on_off(
level=DEFAULT_MIN_BRIGHTNESS,
transition_time=self._DEFAULT_MIN_TRANSITION_TIME,
transition_time=int(10 * self._DEFAULT_MIN_TRANSITION_TIME),
)
t_log["move_to_level_with_on_off"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand Down Expand Up @@ -337,7 +335,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
):
result = await self._level_cluster_handler.move_to_level_with_on_off(
level=level,
transition_time=duration,
transition_time=int(10 * duration),
)
t_log["move_to_level_with_on_off"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand Down Expand Up @@ -390,7 +388,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
# The light is has the correct color, so we can now transition
# it to the correct brightness level.
result = await self._level_cluster_handler.move_to_level(
level=level, transition_time=duration
level=level, transition_time=int(10 * duration)
)
t_log["move_to_level_if_color"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand Down Expand Up @@ -465,7 +463,9 @@ async def async_turn_off(self, **kwargs: Any) -> None:
if transition is not None and supports_level:
result = await self._level_cluster_handler.move_to_level_with_on_off(
level=0,
transition_time=(transition * 10 or self._DEFAULT_MIN_TRANSITION_TIME),
transition_time=int(
10 * (transition or self._DEFAULT_MIN_TRANSITION_TIME)
),
)
else:
result = await self._on_off_cluster_handler.off()
Expand Down Expand Up @@ -511,7 +511,7 @@ async def async_handle_color_commands(
if temperature is not None:
result = await self._color_cluster_handler.move_to_color_temp(
color_temp_mireds=temperature,
transition_time=transition_time,
transition_time=int(10 * transition_time),
)
t_log["move_to_color_temp"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand All @@ -529,14 +529,14 @@ async def async_handle_color_commands(
result = await self._color_cluster_handler.enhanced_move_to_hue_and_saturation(
enhanced_hue=int(hs_color[0] * 65535 / 360),
saturation=int(hs_color[1] * 2.54),
transition_time=transition_time,
transition_time=int(10 * transition_time),
)
t_log["enhanced_move_to_hue_and_saturation"] = result
else:
result = await self._color_cluster_handler.move_to_hue_and_saturation(
hue=int(hs_color[0] * 254 / 360),
saturation=int(hs_color[1] * 2.54),
transition_time=transition_time,
transition_time=int(10 * transition_time),
)
t_log["move_to_hue_and_saturation"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand All @@ -551,7 +551,7 @@ async def async_handle_color_commands(
result = await self._color_cluster_handler.move_to_color(
color_x=int(xy_color[0] * 65535),
color_y=int(xy_color[1] * 65535),
transition_time=transition_time,
transition_time=int(10 * transition_time),
)
t_log["move_to_color"] = result
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
Expand Down Expand Up @@ -1091,7 +1091,9 @@ class MinTransitionLight(Light):
"""Representation of a light which does not react to any "move to" calls with 0 as a transition."""

_attr_name: str = "Light"
_DEFAULT_MIN_TRANSITION_TIME = 1

# Transitions are counted in 1/10th of a second increments, so this is the smallest
_DEFAULT_MIN_TRANSITION_TIME = 0.1


@GROUP_MATCH()
Expand All @@ -1111,10 +1113,18 @@ def __init__(
group = self.zha_device.gateway.get_group(self._group_id)

self._GROUP_SUPPORTS_EXECUTE_IF_OFF = True # pylint: disable=invalid-name
# Check all group members to see if they support execute_if_off.
# If at least one member has a color cluster and doesn't support it,
# it's not used.

for member in group.members:
# Ensure we do not send group commands that violate the minimum transition
# time of any members.
if member.device.manufacturer in DEFAULT_MIN_TRANSITION_MANUFACTURERS:
self._DEFAULT_MIN_TRANSITION_TIME = ( # pylint: disable=invalid-name
MinTransitionLight._DEFAULT_MIN_TRANSITION_TIME
)

# Check all group members to see if they support execute_if_off.
# If at least one member has a color cluster and doesn't support it,
# it's not used.
for endpoint in member.device._endpoints.values():
for cluster_handler in endpoint.all_cluster_handlers.values():
if (
Expand All @@ -1124,10 +1134,6 @@ def __init__(
self._GROUP_SUPPORTS_EXECUTE_IF_OFF = False
break

self._DEFAULT_MIN_TRANSITION_TIME = any( # pylint: disable=invalid-name
member.device.manufacturer in DEFAULT_MIN_TRANSITION_MANUFACTURERS
for member in group.members
)
self._on_off_cluster_handler = group.endpoint[OnOff.cluster_id]
self._level_cluster_handler = group.endpoint[LevelControl.cluster_id]
self._color_cluster_handler = group.endpoint[Color.cluster_id]
Expand Down
6 changes: 4 additions & 2 deletions tests/components/zha/data.py
Expand Up @@ -4,8 +4,9 @@
"schemas": {
"zha_options": [
{
"type": "integer",
"type": "float",
"valueMin": 0,
"valueMax": 6553.6,
"name": "default_light_transition",
"optional": True,
"default": 0,
Expand Down Expand Up @@ -74,8 +75,9 @@
"schemas": {
"zha_options": [
{
"type": "integer",
"type": "float",
"valueMin": 0,
"valueMax": 6553.6,
"name": "default_light_transition",
"optional": True,
"default": 0,
Expand Down
6 changes: 3 additions & 3 deletions tests/components/zha/test_light.py
Expand Up @@ -569,7 +569,7 @@ async def test_transitions(
"turn_on",
{
"entity_id": device_1_entity_id,
"transition": 3,
"transition": 3.5,
"brightness": 18,
"color_temp": 432,
},
Expand All @@ -586,7 +586,7 @@ async def test_transitions(
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].id,
dev1_cluster_level.commands_by_name["move_to_level_with_on_off"].schema,
level=18,
transition_time=30,
transition_time=35,
expect_reply=True,
manufacturer=None,
tries=1,
Expand All @@ -597,7 +597,7 @@ async def test_transitions(
dev1_cluster_color.commands_by_name["move_to_color_temp"].id,
dev1_cluster_color.commands_by_name["move_to_color_temp"].schema,
color_temp_mireds=432,
transition_time=30.0,
transition_time=35,
expect_reply=True,
manufacturer=None,
tries=1,
Expand Down

0 comments on commit 4f660cc

Please sign in to comment.