Add ability to get callback when a config entry state changes (#138943)

* Add entry_on_state_change_helper

* undo black

* remove unload

* no coro

* Add tests

* Don't accept coro

* Review feedback

* Add error test

* Make it callback type

* Make it callback type

* Removal test

* change type
This commit is contained in:
Josef Zweck
2025-02-21 11:55:56 +01:00
committed by GitHub
parent b35d252549
commit e59ec8f867
2 changed files with 158 additions and 0 deletions

View File

@@ -402,6 +402,7 @@ class ConfigEntry[_DataT = Any]:
update_listeners: list[UpdateListenerType]
_async_cancel_retry_setup: Callable[[], Any] | None
_on_unload: list[Callable[[], Coroutine[Any, Any, None] | None]] | None
_on_state_change: list[CALLBACK_TYPE] | None
setup_lock: asyncio.Lock
_reauth_lock: asyncio.Lock
_tasks: set[asyncio.Future[Any]]
@@ -526,6 +527,9 @@ class ConfigEntry[_DataT = Any]:
# Hold list for actions to call on unload.
_setter(self, "_on_unload", None)
# Hold list for actions to call on state change.
_setter(self, "_on_state_change", None)
# Reload lock to prevent conflicting reloads
_setter(self, "setup_lock", asyncio.Lock())
# Reauth lock to prevent concurrent reauth flows
@@ -1058,6 +1062,8 @@ class ConfigEntry[_DataT = Any]:
hass, SIGNAL_CONFIG_ENTRY_CHANGED, ConfigEntryChange.UPDATED, self
)
self._async_process_on_state_change()
async def async_migrate(self, hass: HomeAssistant) -> bool:
"""Migrate an entry.
@@ -1172,6 +1178,28 @@ class ConfigEntry[_DataT = Any]:
task,
)
@callback
def async_on_state_change(self, func: CALLBACK_TYPE) -> CALLBACK_TYPE:
"""Add a function to call when a config entry changes its state."""
if self._on_state_change is None:
self._on_state_change = []
self._on_state_change.append(func)
return lambda: cast(list, self._on_state_change).remove(func)
def _async_process_on_state_change(self) -> None:
"""Process the on_state_change callbacks and wait for pending tasks."""
if self._on_state_change is None:
return
for func in self._on_state_change:
try:
func()
except Exception:
_LOGGER.exception(
"Error calling on_state_change callback for %s (%s)",
self.title,
self.domain,
)
@callback
def async_start_reauth(
self,