1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Unload scripts and conditions created by template entities (#169366)

This commit is contained in:
Erik Montnemery
2026-05-06 14:11:37 +02:00
committed by GitHub
parent 26714c6d9f
commit 923e099467
4 changed files with 43 additions and 12 deletions
@@ -1,8 +1,8 @@
"""Data update coordinator for trigger based template entities."""
from collections.abc import Callable, Mapping
from collections.abc import Callable
import logging
from typing import TYPE_CHECKING, Any, cast
from typing import TYPE_CHECKING, cast
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import (
@@ -37,7 +37,7 @@ class TriggerUpdateCoordinator(DataUpdateCoordinator):
hass, _LOGGER, config_entry=None, name="Trigger Update Coordinator"
)
self.config = config
self._cond_func: Callable[[Mapping[str, Any] | None], bool] | None = None
self._cond_func: condition.ConditionsChecker | None = None
self._unsub_start: Callable[[], None] | None = None
self._unsub_trigger: Callable[[], None] | None = None
self._script: Script | None = None
@@ -69,7 +69,9 @@ class TriggerUpdateCoordinator(DataUpdateCoordinator):
self._unsub_trigger()
self._unsub_trigger = None
if self._script is not None:
await self._script.async_stop()
await self._script.async_unload()
if self._cond_func is not None:
self._cond_func.async_unload()
async def async_setup(self, hass_config: ConfigType) -> None:
"""Set up the trigger and create entities."""
@@ -158,7 +160,7 @@ class TriggerUpdateCoordinator(DataUpdateCoordinator):
def _check_condition(self, run_variables: TemplateVarsType) -> bool:
if not self._cond_func:
return True
condition_result = self._cond_func(run_variables)
condition_result = self._cond_func.async_check(variables=run_variables)
if condition_result is False:
_LOGGER.debug(
"Conditions not met, aborting template trigger update. Condition summary: %s",
+9 -3
View File
@@ -169,9 +169,15 @@ class AbstractTemplateEntity(Entity):
)
async def async_will_remove_from_hass(self) -> None:
"""Stop scripts when removing from Home Assistant."""
for action_script in self._action_scripts.values():
await action_script.async_stop()
"""Clean up scripts when removing from Home Assistant."""
if not self.registry_entry or self.registry_entry.entity_id == self.entity_id:
# Entity ID not changed, unload scripts as they will not be reused.
for action_script in self._action_scripts.values():
await action_script.async_unload()
else:
# Entity ID changed, just stop scripts
for action_script in self._action_scripts.values():
await action_script.async_stop()
async def async_run_script(
self,
+1
View File
@@ -82,4 +82,5 @@ async def test_reload_stops_entity_action_scripts(
await hass.async_block_till_done()
assert not turn_on_script.is_running
assert turn_on_script._unloaded
assert hass.data["light"].get_entity("light.test_light") is None
@@ -1,7 +1,7 @@
"""Test trigger template entity."""
import asyncio
from unittest.mock import patch
from unittest.mock import Mock, patch
import pytest
@@ -17,7 +17,8 @@ from homeassistant.const import (
STATE_ON,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import template
from homeassistant.helpers import condition, template
from homeassistant.helpers.script import Script
from homeassistant.helpers.trigger_template_entity import CONF_PICTURE
from homeassistant.setup import async_setup_component
@@ -243,6 +244,23 @@ async def test_multiple_template_validators(hass: HomeAssistant) -> None:
assert state.attributes["current_tilt_position"] == 49
async def test_coordinator_shutdown_unloads_script_and_condition(
hass: HomeAssistant,
) -> None:
"""Test that coordinator shutdown stops and unloads script and condition."""
coordinator = TriggerUpdateCoordinator(hass, {})
mock_script = Mock(spec=Script)
mock_cond = Mock(spec=condition.ConditionsChecker)
coordinator._script = mock_script
coordinator._cond_func = mock_cond
await coordinator.async_shutdown()
mock_script.async_unload.assert_called_once()
mock_cond.async_unload.assert_called_once()
async def test_shutdown_stops_script_and_keeps_triggers_subscribed(
hass: HomeAssistant,
) -> None:
@@ -281,13 +299,15 @@ async def test_shutdown_stops_script_and_keeps_triggers_subscribed(
coordinators = hass.data[DATA_COORDINATORS]
assert len(coordinators) == 1
assert coordinators[0]._script.is_running
assert not coordinators[0]._script._unloaded
# Fire shutdown
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
# Script should be stopped - this is handled by the script helper
# Script should be stopped but not unloaded - this is handled by the script helper
assert not coordinators[0]._script.is_running
assert not coordinators[0]._script._unloaded
# Triggers are not unsubscribed on shutdown
listeners = hass.bus.async_listeners()
@@ -332,6 +352,7 @@ async def test_reload_stops_script_and_unsubscribes_triggers(
assert len(coordinators) == 1
coordinator = coordinators[0]
assert coordinator._script.is_running
assert not coordinator._script._unloaded
# Reload with empty config
with patch(
@@ -342,8 +363,9 @@ async def test_reload_stops_script_and_unsubscribes_triggers(
await hass.services.async_call("template", SERVICE_RELOAD, blocking=True)
await hass.async_block_till_done()
# Script should be stopped
# Script should be stopped and unloaded
assert not coordinator._script.is_running
assert coordinator._script._unloaded
# Old trigger should be unsubscribed
listeners = hass.bus.async_listeners()