Compare commits

...

3 commits

Author SHA1 Message Date
Mario Guggenberger
4319a6a524
fix: skipped adaptation of intercepted turn-on with attributes 2026-01-13 18:34:09 +00:00
Mario Guggenberger
4c7786bbc6
refactor: use manual control methods for state access 2026-01-13 16:54:27 +00:00
Mario Guggenberger
b732eed970
refactor: consolidate adapt_only_on_bare_turn_on check 2026-01-13 16:35:09 +00:00
2 changed files with 128 additions and 71 deletions

View file

@ -1004,6 +1004,11 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
entry_type=DeviceEntryType.SERVICE, entry_type=DeviceEntryType.SERVICE,
) )
@property
def is_adapt_only_on_bare_turn_on_enabled(self) -> bool:
"""Return whether adapt_only_on_bare_turn_on is enabled."""
return self._take_over_control and self._adapt_only_on_bare_turn_on
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Call when entity about to be added to hass.""" """Call when entity about to be added to hass."""
if self.hass.is_running: if self.hass.is_running:
@ -1122,7 +1127,9 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
extra_state_attributes[key] = None extra_state_attributes[key] = None
return extra_state_attributes return extra_state_attributes
extra_state_attributes["manual_control"] = [ extra_state_attributes["manual_control"] = [
light for light in self.lights if self.manager.manual_control.get(light) light
for light in self.lights
if self.manager.get_manual_control_attributes(light).has_any()
] ]
extra_state_attributes.update(self._settings) extra_state_attributes.update(self._settings)
timers = self.manager.auto_reset_manual_control_timers timers = self.manager.auto_reset_manual_control_timers
@ -1533,8 +1540,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
return return
if ( if (
self._take_over_control self.is_adapt_only_on_bare_turn_on_enabled
and self._adapt_only_on_bare_turn_on
and from_turn_on and from_turn_on
# adaptive_lighting.apply can turn on light, so check this is not our context # adaptive_lighting.apply can turn on light, so check this is not our context
and not is_our_context(event.context) and not is_our_context(event.context)
@ -1709,7 +1715,7 @@ class AdaptiveLightingManager:
# Locks that prevent light adjusting when waiting for a light to 'turn_off' # Locks that prevent light adjusting when waiting for a light to 'turn_off'
self.turn_off_locks: dict[str, asyncio.Lock] = {} self.turn_off_locks: dict[str, asyncio.Lock] = {}
# Tracks which lights are manually controlled # Tracks which lights are manually controlled
self.manual_control: dict[str, LightControlAttributes] = {} self._manual_control: dict[str, LightControlAttributes] = {}
# Track 'state_changed' events of self.lights resulting from this integration # Track 'state_changed' events of self.lights resulting from this integration
self.our_last_state_on_change: dict[str, list[State]] = {} self.our_last_state_on_change: dict[str, list[State]] = {}
# Track last 'service_data' to 'light.turn_on' resulting from this integration # Track last 'service_data' to 'light.turn_on' resulting from this integration
@ -1842,22 +1848,32 @@ class AdaptiveLightingManager:
skipped, skipped,
) )
else: else:
is_light_group = (
e := self.hass.states.get(entity_id)
) and _is_light_group(e)
is_already_on = self.hass.states.is_state(entity_id, STATE_ON)
is_manually_controlled = self.get_manual_control_attributes(
entity_id,
).has_all()
if ( if (
not switch.is_on not switch.is_on
or not switch._intercept or not switch._intercept
# Never adapt on light groups, because HA will make a separate light.turn_on # Never adapt on light groups, because HA will make a separate light.turn_on
or ((e := self.hass.states.get(entity_id)) and _is_light_group(e)) or is_light_group
# Prevent adaptation of TURN_ON calls when light is already on, # Prevent adaptation of TURN_ON calls when light is already on,
# and of TOGGLE calls when toggling off. # and of TOGGLE calls when toggling off.
or self.hass.states.is_state(entity_id, STATE_ON) or is_already_on
or self.manual_control.get(entity_id, False) or is_manually_controlled
or ( or (
switch._take_over_control switch.is_adapt_only_on_bare_turn_on_enabled
and switch._adapt_only_on_bare_turn_on
and self._mark_manual_control_if_non_bare_turn_on( and self._mark_manual_control_if_non_bare_turn_on(
entity_id, entity_id,
data[CONF_PARAMS], data[CONF_PARAMS],
) )
# Skip adaptation only if all attributes are manually controlled, otherwise
# we have to assume that some attribute(s) need adaptation.
and self.get_manual_control_attributes(entity_id).has_all()
) )
): ):
_LOGGER.debug( _LOGGER.debug(
@ -1867,7 +1883,7 @@ class AdaptiveLightingManager:
skipped, skipped,
switch.is_on, switch.is_on,
self.hass.states.is_state(entity_id, STATE_ON), self.hass.states.is_state(entity_id, STATE_ON),
self.manual_control.get(entity_id, False), self.get_manual_control_attributes(entity_id),
switch._intercept, switch._intercept,
) )
skipped.append(entity_id) skipped.append(entity_id)
@ -2069,7 +2085,8 @@ class AdaptiveLightingManager:
data: ServiceData, data: ServiceData,
): ):
_LOGGER.debug( _LOGGER.debug(
"Intercepted TURN_ON call with data %s (%s)", "%s: Intercepted TURN_ON call with data %s (%s)",
entity_ids,
data, data,
call.context.id, call.context.id,
) )
@ -2104,6 +2121,7 @@ class AdaptiveLightingManager:
# lack of a bijective mapping.) # lack of a bijective mapping.)
preprocess_turn_on_alternatives(self.hass, first_service_data) preprocess_turn_on_alternatives(self.hass, first_service_data)
data[CONF_PARAMS].update(first_service_data) data[CONF_PARAMS].update(first_service_data)
switch.manager.last_service_data[entity_ids[0]] = first_service_data
# Schedule additional service calls for the remaining adaptation data. # Schedule additional service calls for the remaining adaptation data.
# We cannot know here whether there is another call to follow (since the # We cannot know here whether there is another call to follow (since the
@ -2204,7 +2222,7 @@ class AdaptiveLightingManager:
light: str, light: str,
) -> LightControlAttributes: ) -> LightControlAttributes:
"""Get the attributes for a light that are manually controlled.""" """Get the attributes for a light that are manually controlled."""
return self.manual_control.get(light, LightControlAttributes.NONE) return self._manual_control.get(light, LightControlAttributes.NONE)
def set_manual_control_attributes( def set_manual_control_attributes(
self, self,
@ -2218,7 +2236,7 @@ class AdaptiveLightingManager:
attributes, attributes,
self.get_manual_control_attributes(light), self.get_manual_control_attributes(light),
) )
self.manual_control[light] = attributes self._manual_control[light] = attributes
delay = self.auto_reset_manual_control_times.get(light) delay = self.auto_reset_manual_control_times.get(light)
async def reset() -> None: async def reset() -> None:
@ -2239,7 +2257,7 @@ class AdaptiveLightingManager:
transition=switch.initial_transition, transition=switch.initial_transition,
force=True, force=True,
) )
assert self.manual_control[light] == LightControlAttributes.NONE assert self.get_manual_control_attributes(light).has_none()
self._handle_timer(light, self.auto_reset_manual_control_timers, delay, reset) self._handle_timer(light, self.auto_reset_manual_control_timers, delay, reset)
@ -2336,7 +2354,7 @@ class AdaptiveLightingManager:
"Light %s: Clearing manual control attributes.", "Light %s: Clearing manual control attributes.",
light, light,
) )
self.manual_control[light] = LightControlAttributes.NONE self.set_manual_control_attributes(light, LightControlAttributes.NONE)
if timer := self.auto_reset_manual_control_timers.pop(light, None): if timer := self.auto_reset_manual_control_timers.pop(light, None):
timer.cancel() timer.cancel()
self.our_last_state_on_change.pop(light, None) self.our_last_state_on_change.pop(light, None)

View file

@ -20,6 +20,7 @@ from homeassistant.components.adaptive_lighting.adaptation_utils import (
AdaptationData, AdaptationData,
LightControlAttributes, LightControlAttributes,
_create_service_call_data_iterator, _create_service_call_data_iterator,
has_color_attribute,
) )
from homeassistant.components.adaptive_lighting.color_and_brightness import ( from homeassistant.components.adaptive_lighting.color_and_brightness import (
lerp_color_hsv, lerp_color_hsv,
@ -632,7 +633,6 @@ async def test_manual_control(
assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON
context = switch.create_context("test") # needs to be passed to update method context = switch.create_context("test") # needs to be passed to update method
manual_control = switch.manager.manual_control
async def update(): async def update():
await switch._update_attrs_and_maybe_adapt_lights(context=context, transition=0) await switch._update_attrs_and_maybe_adapt_lights(context=context, transition=0)
@ -661,7 +661,7 @@ async def test_manual_control(
async def change_manual_control(set_to, extra_service_data=None): async def change_manual_control(set_to, extra_service_data=None):
if extra_service_data is None: if extra_service_data is None:
extra_service_data = {CONF_LIGHTS: [ENTITY_LIGHT_1]} extra_service_data = {CONF_LIGHTS: [ENTITY_LIGHT_1]}
_LOGGER.debug(f"{switch.manager.manual_control=}") _LOGGER.debug(f"{switch.manager._manual_control=}")
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
SERVICE_SET_MANUAL_CONTROL, SERVICE_SET_MANUAL_CONTROL,
@ -672,7 +672,7 @@ async def test_manual_control(
}, },
blocking=True, blocking=True,
) )
_LOGGER.debug(f"{switch.manager.manual_control=}") _LOGGER.debug(f"{switch.manager._manual_control=}")
_LOGGER.debug("Called set_manual_control with %s", set_to) _LOGGER.debug("Called set_manual_control with %s", set_to)
await hass.async_block_till_done() await hass.async_block_till_done()
await update() await update()
@ -689,21 +689,24 @@ async def test_manual_control(
# Nothing is manually controlled # Nothing is manually controlled
await update() await update()
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Call light.turn_on for ENTITY_LIGHT_1 # Call light.turn_on for ENTITY_LIGHT_1
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
# Check that ENTITY_LIGHT_1 is manually controlled # Check that ENTITY_LIGHT_1 is manually controlled
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS
)
# Test adaptive_lighting.set_manual_control # Test adaptive_lighting.set_manual_control
await change_manual_control(False) await change_manual_control(False)
# Check that ENTITY_LIGHT_1 is not manually controlled # Check that ENTITY_LIGHT_1 is not manually controlled
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Check that toggling light off to on resets manual control # Check that toggling light off to on resets manual control
await change_manual_control(True) await change_manual_control(True)
assert manual_control[ENTITY_LIGHT_1] assert switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await turn_light(False) await turn_light(False)
assert not manual_control[ENTITY_LIGHT_1], manual_control assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON
# Turning on from OFF with brightness: # Turning on from OFF with brightness:
@ -711,44 +714,52 @@ async def test_manual_control(
# - With adapt_only_on_bare_turn_on=False: should NOT mark (fix for issue #1378) # - With adapt_only_on_bare_turn_on=False: should NOT mark (fix for issue #1378)
if adapt_only_on_bare_turn_on: if adapt_only_on_bare_turn_on:
assert ( assert (
manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
), manual_control == LightControlAttributes.BRIGHTNESS
)
else: else:
assert not manual_control[ENTITY_LIGHT_1], manual_control assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Reset for next test # Reset for next test
await turn_light(False) await turn_light(False)
await turn_light(True) await turn_light(True)
assert not manual_control[ENTITY_LIGHT_1], manual_control assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Now change brightness while ON - this should always be manual control # Now change brightness while ON - this should always be manual control
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
assert ( assert (
manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
), manual_control == LightControlAttributes.BRIGHTNESS
)
# Check that toggling (sleep mode) switch resets manual control # Check that toggling (sleep mode) switch resets manual control
for entity_id in [ENTITY_SWITCH, ENTITY_SLEEP_MODE_SWITCH]: for entity_id in [ENTITY_SWITCH, ENTITY_SLEEP_MODE_SWITCH]:
await change_manual_control(True) await change_manual_control(True)
assert manual_control[ENTITY_LIGHT_1] assert switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await turn_switch(False, entity_id) await turn_switch(False, entity_id)
await turn_switch(True, entity_id) await turn_switch(True, entity_id)
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Check that manual control is still enabled if set while bulb is off. # Check that manual control is still enabled if set while bulb is off.
# Test issue #37 # Test issue #37
await turn_light(False) await turn_light(False)
await change_manual_control(True) await change_manual_control(True)
await turn_light(True) await turn_light(True)
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.ALL assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.ALL
)
# Check that when 'adapt_brightness' is off, changing the brightness # Check that when 'adapt_brightness' is off, changing the brightness
# doesn't mark it as manually controlled but changing color_temp # doesn't mark it as manually controlled but changing color_temp
# does # does
await turn_light(False) await turn_light(False)
await turn_light(True) # reset manually controlled status await turn_light(True) # reset manually controlled status
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await switch.adapt_brightness_switch.async_turn_off() await switch.adapt_brightness_switch.async_turn_off()
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS
)
mired_range = (light.min_color_temp_kelvin, light.max_color_temp_kelvin) mired_range = (light.min_color_temp_kelvin, light.max_color_temp_kelvin)
kelvin_range = ( kelvin_range = (
color_temperature_mired_to_kelvin(mired_range[1]), color_temperature_mired_to_kelvin(mired_range[1]),
@ -759,7 +770,10 @@ async def test_manual_control(
True, True,
color_temp_kelvin=(light._attr_color_temp + 100) % ptp_kelvin, color_temp_kelvin=(light._attr_color_temp + 100) % ptp_kelvin,
) )
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.ALL assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.ALL
)
await switch.adapt_brightness_switch.async_turn_on() # turn on again await switch.adapt_brightness_switch.async_turn_on() # turn on again
# Check that when 'adapt_color' is off, changing the color # Check that when 'adapt_color' is off, changing the color
@ -767,12 +781,18 @@ async def test_manual_control(
# does # does
await turn_light(False) # reset manually controlled status await turn_light(False) # reset manually controlled status
await turn_light(True) await turn_light(True)
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await switch.adapt_color_switch.async_turn_off() await switch.adapt_color_switch.async_turn_off()
await turn_light(True, color_temp_kelvin=increased_color_temp()) await turn_light(True, color_temp_kelvin=increased_color_temp())
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.COLOR assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.COLOR
)
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.ALL assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.ALL
)
# Check that when 'adapt_color' adapt_brightness are both off # Check that when 'adapt_color' adapt_brightness are both off
# nothing marks it as manually controlled # nothing marks it as manually controlled
@ -780,7 +800,7 @@ async def test_manual_control(
await turn_light(True) await turn_light(True)
await switch.adapt_color_switch.async_turn_off() await switch.adapt_color_switch.async_turn_off()
await switch.adapt_brightness_switch.async_turn_off() await switch.adapt_brightness_switch.async_turn_off()
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await turn_light(True, color_temp_kelvin=increased_color_temp()) await turn_light(True, color_temp_kelvin=increased_color_temp())
await turn_light(True, brightness=increased_brightness()) await turn_light(True, brightness=increased_brightness())
await turn_light( await turn_light(
@ -788,17 +808,26 @@ async def test_manual_control(
color_temp_kelvin=increased_color_temp(), color_temp_kelvin=increased_color_temp(),
brightness=increased_brightness(), brightness=increased_brightness(),
) )
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.ALL assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.ALL
)
# Turn switches on again # Turn switches on again
await switch.adapt_color_switch.async_turn_on() await switch.adapt_color_switch.async_turn_on()
await switch.adapt_brightness_switch.async_turn_on() await switch.adapt_brightness_switch.async_turn_on()
# Check that when no lights are specified, all are reset # Check that when no lights are specified, all are reset
await change_manual_control(True, {CONF_LIGHTS: switch.lights}) await change_manual_control(True, {CONF_LIGHTS: switch.lights})
assert all(manual_control[eid] for eid in switch.lights) assert all(
switch.manager.get_manual_control_attributes(eid).has_all()
for eid in switch.lights
)
# do not pass "lights" so reset all # do not pass "lights" so reset all
await change_manual_control(False, {}) await change_manual_control(False, {})
assert all(not manual_control[eid] for eid in switch.lights) assert all(
switch.manager.get_manual_control_attributes(eid).has_none()
for eid in switch.lights
)
# Turn off light and turn on using adaptive_lighting.apply # Turn off light and turn on using adaptive_lighting.apply
await turn_light(False) await turn_light(False)
@ -814,23 +843,32 @@ async def test_manual_control(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Check that manual control `True` sets all attributes # Check that manual control `True` sets all attributes
await change_manual_control(False) await change_manual_control(False)
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await change_manual_control(True) await change_manual_control(True)
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.ALL assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.ALL
)
# Check that manual control `False` unsets all attributes # Check that manual control `False` unsets all attributes
await change_manual_control(False) await change_manual_control(False)
assert not manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Check that manual control attributes can be selectively set # Check that manual control attributes can be selectively set
await change_manual_control("brightness") await change_manual_control("brightness")
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS
)
await change_manual_control("color") await change_manual_control("color")
assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.COLOR assert (
switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.COLOR
)
@flaky(max_runs=3, min_passes=1) @flaky(max_runs=3, min_passes=1)
@ -840,7 +878,6 @@ async def test_auto_reset_manual_control(hass):
{CONF_AUTORESET_CONTROL: 0.1}, {CONF_AUTORESET_CONTROL: 0.1},
) )
context = switch.create_context("test") # needs to be passed to update method context = switch.create_context("test") # needs to be passed to update method
manual_control = switch.manager.manual_control
async def update(): async def update():
await switch._update_attrs_and_maybe_adapt_lights(context=context, transition=0) await switch._update_attrs_and_maybe_adapt_lights(context=context, transition=0)
@ -865,13 +902,16 @@ async def test_auto_reset_manual_control(hass):
_LOGGER.debug("Start test auto reset manual control") _LOGGER.debug("Start test auto reset manual control")
await turn_light(True, brightness=1) await turn_light(True, brightness=1)
await turn_light(True, brightness=10) await turn_light(True, brightness=10)
assert manual_control[light.entity_id] == LightControlAttributes.BRIGHTNESS assert (
switch.manager.get_manual_control_attributes(light.entity_id)
== LightControlAttributes.BRIGHTNESS
)
assert ( assert (
switch.extra_state_attributes["autoreset_time_remaining"][light.entity_id] > 0 switch.extra_state_attributes["autoreset_time_remaining"][light.entity_id] > 0
) )
await update() await update()
await asyncio.sleep(0.3) # Should be enough time for auto reset await asyncio.sleep(0.3) # Should be enough time for auto reset
assert not manual_control[light.entity_id], (light, manual_control) assert not switch.manager.get_manual_control_attributes(light.entity_id)
assert ( assert (
light.entity_id not in switch.extra_state_attributes["autoreset_time_remaining"] light.entity_id not in switch.extra_state_attributes["autoreset_time_remaining"]
) )
@ -881,11 +921,11 @@ async def test_auto_reset_manual_control(hass):
_LOGGER.debug("Quick change %s", i) _LOGGER.debug("Quick change %s", i)
await turn_light(True, brightness=(i + 1) * 20) await turn_light(True, brightness=(i + 1) * 20)
await asyncio.sleep(0.05) # Less than 0.1 await asyncio.sleep(0.05) # Less than 0.1
assert manual_control[light.entity_id] assert switch.manager.get_manual_control_attributes(light.entity_id)
await update() await update()
await asyncio.sleep(0.3) # Wait the auto reset time await asyncio.sleep(0.3) # Wait the auto reset time
assert not manual_control[light.entity_id] assert not switch.manager.get_manual_control_attributes(light.entity_id)
async def test_adaptation_attribute_selection(hass): async def test_adaptation_attribute_selection(hass):
@ -1116,7 +1156,7 @@ async def test_switch_off_on_off(hass):
# Turn light off with transition # Turn light off with transition
await turn_light(False, transition=1) await turn_light(False, transition=1)
assert not switch.manager.manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
# Set state to on after a second (like happens IRL) # Set state to on after a second (like happens IRL)
await asyncio.sleep(1e-3) await asyncio.sleep(1e-3)
hass.states.async_set(ENTITY_LIGHT_1, STATE_ON) hass.states.async_set(ENTITY_LIGHT_1, STATE_ON)
@ -1371,9 +1411,9 @@ async def test_state_change_handlers(hass):
assert timer.is_running() assert timer.is_running()
last_service_data = deepcopy(current_service_data) last_service_data = deepcopy(current_service_data)
await update() await update()
assert not switch.manager.manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await update() await update()
assert not switch.manager.manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
timer = listener.transition_timers.get(ENTITY_LIGHT_1) timer = listener.transition_timers.get(ENTITY_LIGHT_1)
assert timer assert timer
assert timer.is_running() assert timer.is_running()
@ -1397,19 +1437,19 @@ async def test_state_change_handlers(hass):
await turn_light(True, brightness=20) await turn_light(True, brightness=20)
await update(force=False) await update(force=False)
assert ( assert (
switch.manager.manual_control[ENTITY_LIGHT_1] switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS == LightControlAttributes.BRIGHTNESS
) )
await update(force=True) await update(force=True)
assert ( assert (
switch.manager.manual_control[ENTITY_LIGHT_1] switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS == LightControlAttributes.BRIGHTNESS
) )
# turn light off then on should reset manual control. # turn light off then on should reset manual control.
await turn_light(False) await turn_light(False)
await turn_light(True) await turn_light(True)
assert not switch.manager.manual_control[ENTITY_LIGHT_1] assert not switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
await turn_light(True, brightness=50) await turn_light(True, brightness=50)
_LOGGER.debug("Test: Brightness set to %s", 50) _LOGGER.debug("Test: Brightness set to %s", 50)
@ -1419,7 +1459,7 @@ async def test_state_change_handlers(hass):
assert switch.manager.last_service_data.get(ENTITY_LIGHT_1) is not None assert switch.manager.last_service_data.get(ENTITY_LIGHT_1) is not None
assert switch.manager.our_last_state_on_change.get(ENTITY_LIGHT_1) is not None assert switch.manager.our_last_state_on_change.get(ENTITY_LIGHT_1) is not None
assert ( assert (
switch.manager.manual_control[ENTITY_LIGHT_1] switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
== LightControlAttributes.BRIGHTNESS == LightControlAttributes.BRIGHTNESS
) )
@ -2288,12 +2328,11 @@ async def test_light_group(
await hass.async_block_till_done() await hass.async_block_till_done()
if take_over_control: if take_over_control:
assert switch.manager.manual_control["light.light_4"] assert switch.manager.get_manual_control_attributes("light.light_4")
assert switch.manager.manual_control["light.light_5"] assert switch.manager.get_manual_control_attributes("light.light_5")
else: else:
assert not switch.manager.manual_control["light.light_4"] assert not switch.manager.get_manual_control_attributes("light.light_4")
assert not switch.manager.manual_control["light.light_5"] assert not switch.manager.get_manual_control_attributes("light.light_5")
await hass.services.async_call( await hass.services.async_call(
LIGHT_DOMAIN, LIGHT_DOMAIN,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
@ -2302,8 +2341,8 @@ async def test_light_group(
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert not switch.manager.manual_control["light.light_4"] assert not switch.manager.get_manual_control_attributes("light.light_4")
assert not switch.manager.manual_control["light.light_5"] assert not switch.manager.get_manual_control_attributes("light.light_5")
events = await _turn_on_and_track_event_contexts( events = await _turn_on_and_track_event_contexts(
hass, hass,
"testing", "testing",
@ -2796,7 +2835,7 @@ async def test_automation_turn_on_from_off_not_marked_as_manual_control(hass):
assert hass.states.get(ENTITY_LIGHT_1).state == STATE_OFF assert hass.states.get(ENTITY_LIGHT_1).state == STATE_OFF
# Verify light is not manually controlled # Verify light is not manually controlled
assert not switch.manager.manual_control.get( assert not switch.manager.get_manual_control_attributes(
ENTITY_LIGHT_1, ENTITY_LIGHT_1,
), "Light should not be manually controlled before test" ), "Light should not be manually controlled before test"
@ -2821,7 +2860,7 @@ async def test_automation_turn_on_from_off_not_marked_as_manual_control(hass):
# CRITICAL: The light should NOT be marked as manually controlled! # CRITICAL: The light should NOT be marked as manually controlled!
# The bug in v1.30.0 would incorrectly mark this as manual control because # The bug in v1.30.0 would incorrectly mark this as manual control because
# the turn_on had a brightness attribute. # the turn_on had a brightness attribute.
manual_control_attrs = switch.manager.manual_control.get(ENTITY_LIGHT_1) manual_control_attrs = switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
assert not manual_control_attrs, ( assert not manual_control_attrs, (
f"Bug confirmed: Light was incorrectly marked as manually controlled " f"Bug confirmed: Light was incorrectly marked as manually controlled "
f"(attributes: {manual_control_attrs}) when turned on from OFF state. " f"(attributes: {manual_control_attrs}) when turned on from OFF state. "
@ -2895,7 +2934,7 @@ async def test_adapt_only_on_bare_turn_on_respects_pause_changed_mode(hass, inte
assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON assert hass.states.get(ENTITY_LIGHT_1).state == STATE_ON
# 1. Verify that ONLY brightness is marked as manually controlled # 1. Verify that ONLY brightness is marked as manually controlled
manual_control_attrs = switch.manager.manual_control.get(ENTITY_LIGHT_1) manual_control_attrs = switch.manager.get_manual_control_attributes(ENTITY_LIGHT_1)
assert manual_control_attrs == LightControlAttributes.BRIGHTNESS, ( assert manual_control_attrs == LightControlAttributes.BRIGHTNESS, (
f"Expected only BRIGHTNESS to be marked as manually controlled, " f"Expected only BRIGHTNESS to be marked as manually controlled, "
f"but got: {manual_control_attrs}. With adapt_only_on_bare_turn_on=True, " f"but got: {manual_control_attrs}. With adapt_only_on_bare_turn_on=True, "
@ -2909,7 +2948,7 @@ async def test_adapt_only_on_bare_turn_on_respects_pause_changed_mode(hass, inte
"With PAUSE_CHANGED mode, color should still be adapted since only brightness " "With PAUSE_CHANGED mode, color should still be adapted since only brightness "
"was marked as manually controlled." "was marked as manually controlled."
) )
assert ATTR_COLOR_TEMP_KELVIN in last_service_data, ( assert has_color_attribute(last_service_data), (
f"Bug: Color was not adapted. last_service_data={last_service_data}. " f"Bug: Color was not adapted. last_service_data={last_service_data}. "
f"With take_over_control_mode=PAUSE_CHANGED and only brightness marked " f"With take_over_control_mode=PAUSE_CHANGED and only brightness marked "
f"as manually controlled, color_temp should still be adapted." f"as manually controlled, color_temp should still be adapted."