📝 get_commands: say when redundant attributes are compared

With `skip_redundant_commands`, the calls leave out what the light has
when the service is called, so say to make them right away. Also test
a split call dropped after the first one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mathias Berglund 2026-09-28 11:53:13 +03:00
commit 8ba7aabf05
4 changed files with 14 additions and 7 deletions

View file

@ -256,7 +256,7 @@ Provide a switch in `entity_id`, a list of `lights`, or both.
<!-- OUTPUT:END -->
<!-- SECTION:get-commands:START -->
Call it with a `response_variable`. For each light, the response lists the calls in order, each with its `service_data` and the `delay` in seconds before it (only nonzero between split calls, see `separate_turn_on_commands`). Only the lights and light groups in a switch's `lights` can be passed; without `entity_id`, each uses the switch it's in (of several, the one that's on). A light group is replaced by its members if the switch expands light groups. Options you leave out follow the switch, with its `initial_transition`, and in sleep mode the calls are for the sleep settings, whatever the time. With `skip_redundant_commands`, what a light already has is left out, as when Adaptive Lighting adapts it. Manual control doesn't change the calls. In YAML files, quote the time (`"22:00:00"`), since `22:00:00` is read as a number.
Call it with a `response_variable`. For each light, the response lists the calls in order, each with its `service_data` and the `delay` in seconds before it (only nonzero between split calls, see `separate_turn_on_commands`). Only the lights and light groups in a switch's `lights` can be passed; without `entity_id`, each uses the switch it's in (of several, the one that's on). A light group is replaced by its members if the switch expands light groups. Options you leave out follow the switch, with its `initial_transition`, and in sleep mode the calls are for the sleep settings, whatever the time. With `skip_redundant_commands`, what a light already has is left out, as when Adaptive Lighting adapts it; that compares with the light's state when you call the service, so make the calls right away. Manual control doesn't change the calls. In YAML files, quote the time (`"22:00:00"`), since `22:00:00` is read as a number.
Making the calls is a manual change for Adaptive Lighting: with `take_over_control`, it stops adapting those lights until manual control is reset. A light that's off is adapted to the current settings as it turns on (unless `take_over_control` and `adapt_only_on_bare_turn_on` are enabled), so this example only changes lights that are on:

View file

@ -1503,7 +1503,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
For each light (all of the switch's if `lights` is `None`), the calls in
order, each with its service data and the time to wait before it, adapting
now or at the `time` of day today (lights without a state are left out). With
`skip_redundant_commands`, what a light already has is left out.
`skip_redundant_commands`, what a light already has (now) is left out.
Omitted options default to the switch's settings (unlike for `apply`), and
`transition` to its `initial_transition` (like for `apply`). Manual control
doesn't change the calls, and nothing is sent.

View file

@ -87,7 +87,7 @@ Provide a switch in `entity_id`, a list of `lights`, or both.
<!-- CODE:END -->
<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
Call it with a `response_variable`. For each light, the response lists the calls in order, each with its `service_data` and the `delay` in seconds before it (only nonzero between split calls, see `separate_turn_on_commands`). Only the lights and light groups in a switch's `lights` can be passed; without `entity_id`, each uses the switch it's in (of several, the one that's on). A light group is replaced by its members if the switch expands light groups. Options you leave out follow the switch, with its `initial_transition`, and in sleep mode the calls are for the sleep settings, whatever the time. With `skip_redundant_commands`, what a light already has is left out, as when Adaptive Lighting adapts it. Manual control doesn't change the calls. In YAML files, quote the time (`"22:00:00"`), since `22:00:00` is read as a number.
Call it with a `response_variable`. For each light, the response lists the calls in order, each with its `service_data` and the `delay` in seconds before it (only nonzero between split calls, see `separate_turn_on_commands`). Only the lights and light groups in a switch's `lights` can be passed; without `entity_id`, each uses the switch it's in (of several, the one that's on). A light group is replaced by its members if the switch expands light groups. Options you leave out follow the switch, with its `initial_transition`, and in sleep mode the calls are for the sleep settings, whatever the time. With `skip_redundant_commands`, what a light already has is left out, as when Adaptive Lighting adapts it; that compares with the light's state when you call the service, so make the calls right away. Manual control doesn't change the calls. In YAML files, quote the time (`"22:00:00"`), since `22:00:00` is read as a number.
Making the calls is a manual change for Adaptive Lighting: with `take_over_control`, it stops adapting those lights until manual control is reset. A light that's off is adapted to the current settings as it turns on (unless `take_over_control` and `adapt_only_on_bare_turn_on` are enabled), so this example only changes lights that are on:

View file

@ -775,7 +775,8 @@ async def test_get_commands_skip_redundant(hass, skip_redundant):
assert nothing == {ENTITY_LIGHT_1: []}
async def test_get_commands_skip_redundant_split(hass):
@pytest.mark.parametrize("already_has", ["brightness", "color"])
async def test_get_commands_skip_redundant_split(hass, already_has):
"""A split call with nothing left is dropped, and the rest isn't delayed."""
switch, _ = await setup_lights_and_switch(
hass,
@ -788,20 +789,26 @@ async def test_get_commands_skip_redundant_split(hass):
with patch(UTCNOW, return_value=_noon()):
full = await _get_commands(hass, switch, lights=[ENTITY_LIGHT_1])
brightness_call, color_call = full[ENTITY_LIGHT_1]
brightness = brightness_call["service_data"][ATTR_BRIGHTNESS]
color_temp = color_call["service_data"][ATTR_COLOR_TEMP_KELVIN]
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{
**brightness_call["service_data"],
ATTR_ENTITY_ID: ENTITY_LIGHT_1,
ATTR_BRIGHTNESS: (
brightness if already_has == "brightness" else brightness - 50
),
ATTR_COLOR_TEMP_KELVIN: (
color_call["service_data"][ATTR_COLOR_TEMP_KELVIN] - 1000
color_temp if already_has == "color" else color_temp - 1000
),
},
blocking=True,
)
commands = await _get_commands(hass, switch, lights=[ENTITY_LIGHT_1])
remaining = color_call if already_has == "brightness" else brightness_call
assert commands == {
ENTITY_LIGHT_1: [{"service_data": color_call["service_data"], "delay": 0}],
ENTITY_LIGHT_1: [{"service_data": remaining["service_data"], "delay": 0}],
}