mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-11 22:34:04 +02:00
Fix infinite loop when disabling SimpleSwitch entities (#1296)
* Add regression tests for SimpleSwitch initial state bug Adds tests that verify SimpleSwitch._state is set immediately in __init__ rather than waiting for async_added_to_hass(). These tests currently FAIL because _state is None after __init__, which causes an infinite loop in _setup_listeners when the entity is disabled (since async_added_to_hass is never called for disabled entities). Regression tests for: https://github.com/basnijholt/adaptive-lighting/issues/1264 * Fix infinite loop when disabling SimpleSwitch entities The issue was that SimpleSwitch._state was initialized to None in __init__, but only set to a boolean value in async_added_to_hass(). When an entity is disabled, async_added_to_hass() is never called, so _state stayed None. The _setup_listeners() method has a while loop that waits for _state is not None for all SimpleSwitch children (sleep_mode_switch, adapt_brightness_switch, adapt_color_switch). With _state stuck at None, this created an infinite loop. The fix sets _state to initial_state directly in __init__ instead of waiting for async_added_to_hass() to set it. The async_added_to_hass() will still properly restore state from the last session or set based on initial_state as before. Fixes: https://github.com/basnijholt/adaptive-lighting/issues/1264
This commit is contained in:
parent
1556de8c4f
commit
68e243c87e
2 changed files with 75 additions and 1 deletions
|
|
@ -1563,7 +1563,7 @@ class SimpleSwitch(SwitchEntity, RestoreEntity):
|
|||
self.hass = hass
|
||||
data = validate(config_entry)
|
||||
self._icon = icon
|
||||
self._state: bool | None = None
|
||||
self._state: bool = initial_state
|
||||
self._which = which
|
||||
self._config_name = data[CONF_NAME]
|
||||
self._unique_id = f"{self._config_name}_{slugify(self._which)}"
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ from homeassistant.components.adaptive_lighting.switch import (
|
|||
CONF_INTERCEPT,
|
||||
AdaptiveLightingManager,
|
||||
AdaptiveSwitch,
|
||||
SimpleSwitch,
|
||||
_attributes_have_changed,
|
||||
color_difference_redmean,
|
||||
create_context,
|
||||
|
|
@ -2275,3 +2276,76 @@ async def test_brightness_mode(hass, brightness_mode, dark, light):
|
|||
# After sunrise the brightness should be light_brightness
|
||||
await patch_time_and_update(after_sunrise)
|
||||
assert is_approx_equal(switch._settings[ATTR_BRIGHTNESS_PCT], light_brightness)
|
||||
|
||||
|
||||
async def test_simple_switch_initial_state_not_none(hass):
|
||||
"""Test that SimpleSwitch._state is not None after __init__.
|
||||
|
||||
Regression test for https://github.com/basnijholt/adaptive-lighting/issues/1264
|
||||
|
||||
When an entity is disabled in Home Assistant, async_added_to_hass() is never
|
||||
called. Previously, SimpleSwitch._state was initialized to None and only set
|
||||
to True/False in async_added_to_hass(). This caused an infinite loop in
|
||||
AdaptiveSwitch._setup_listeners() which waits for all SimpleSwitch._state
|
||||
to be not None.
|
||||
|
||||
The fix is to initialize _state to the initial_state value in __init__.
|
||||
"""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data={CONF_NAME: DEFAULT_NAME})
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Create a SimpleSwitch without calling async_added_to_hass
|
||||
# (simulating a disabled entity)
|
||||
switch = SimpleSwitch(
|
||||
which="Test",
|
||||
initial_state=True,
|
||||
hass=hass,
|
||||
config_entry=entry,
|
||||
icon="mdi:test",
|
||||
)
|
||||
|
||||
# Before the fix: _state would be None, causing infinite loop
|
||||
# After the fix: _state should be the initial_state value
|
||||
assert switch._state is not None, (
|
||||
"SimpleSwitch._state should not be None after __init__. "
|
||||
"This would cause an infinite loop in _setup_listeners when the entity is disabled."
|
||||
)
|
||||
assert switch._state is True # Should be the initial_state value
|
||||
|
||||
|
||||
async def test_simple_switch_state_after_async_added_to_hass(hass):
|
||||
"""Test that SimpleSwitch._state is properly set after async_added_to_hass.
|
||||
|
||||
This ensures the fix for #1264 doesn't break normal entity initialization.
|
||||
"""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data={CONF_NAME: DEFAULT_NAME})
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
# Create switches with different initial states
|
||||
switch_true = SimpleSwitch(
|
||||
which="Test True",
|
||||
initial_state=True,
|
||||
hass=hass,
|
||||
config_entry=entry,
|
||||
icon="mdi:test",
|
||||
)
|
||||
switch_false = SimpleSwitch(
|
||||
which="Test False",
|
||||
initial_state=False,
|
||||
hass=hass,
|
||||
config_entry=entry,
|
||||
icon="mdi:test",
|
||||
)
|
||||
|
||||
# Verify initial state is set correctly
|
||||
assert switch_true._state is True
|
||||
assert switch_false._state is False
|
||||
|
||||
# Call async_added_to_hass (simulating normal entity setup)
|
||||
# Since there's no last state, it should use the initial_state
|
||||
await switch_true.async_added_to_hass()
|
||||
await switch_false.async_added_to_hass()
|
||||
|
||||
# State should still be correct after async_added_to_hass
|
||||
assert switch_true._state is True
|
||||
assert switch_false._state is False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue