Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the ZHA default light transition time to be configured as a float #92075

Merged
merged 1 commit into from Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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