Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Data update coordinator for trigger based template entities."""
2 
3 from collections.abc import Callable, Mapping
4 import logging
5 from typing import TYPE_CHECKING, Any
6 
7 from homeassistant.const import EVENT_HOMEASSISTANT_START
8 from homeassistant.core import Context, CoreState, Event, HomeAssistant, callback
9 from homeassistant.helpers import condition, discovery, trigger as trigger_helper
10 from homeassistant.helpers.script import Script
11 from homeassistant.helpers.trace import trace_get
12 from homeassistant.helpers.typing import ConfigType, TemplateVarsType
13 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
14 
15 from .const import CONF_ACTION, CONF_CONDITION, CONF_TRIGGER, DOMAIN, PLATFORMS
16 
17 _LOGGER = logging.getLogger(__name__)
18 
19 
21  """Data update coordinator for trigger based template entities."""
22 
23  REMOVE_TRIGGER = object()
24 
25  def __init__(self, hass: HomeAssistant, config: dict[str, Any]) -> None:
26  """Instantiate trigger data."""
27  super().__init__(
28  hass, _LOGGER, config_entry=None, name="Trigger Update Coordinator"
29  )
30  self.configconfig = config
31  self._cond_func_cond_func: Callable[[Mapping[str, Any] | None], bool] | None = None
32  self._unsub_start_unsub_start: Callable[[], None] | None = None
33  self._unsub_trigger_unsub_trigger: Callable[[], None] | None = None
34  self._script_script: Script | None = None
35 
36  @property
37  def unique_id(self) -> str | None:
38  """Return unique ID for the entity."""
39  return self.configconfig.get("unique_id")
40 
41  @callback
42  def async_remove(self) -> None:
43  """Signal that the entities need to remove themselves."""
44  if self._unsub_start_unsub_start:
45  self._unsub_start_unsub_start()
46  if self._unsub_trigger_unsub_trigger:
47  self._unsub_trigger_unsub_trigger()
48 
49  async def async_setup(self, hass_config: ConfigType) -> None:
50  """Set up the trigger and create entities."""
51  if self.hasshass.state is CoreState.running:
52  await self._attach_triggers_attach_triggers()
53  else:
54  self._unsub_start_unsub_start = self.hasshass.bus.async_listen_once(
55  EVENT_HOMEASSISTANT_START, self._attach_triggers_attach_triggers
56  )
57 
58  for platform_domain in PLATFORMS:
59  if platform_domain in self.configconfig:
60  self.hasshass.async_create_task(
61  discovery.async_load_platform(
62  self.hasshass,
63  platform_domain,
64  DOMAIN,
65  {"coordinator": self, "entities": self.configconfig[platform_domain]},
66  hass_config,
67  ),
68  eager_start=True,
69  )
70 
71  async def _attach_triggers(self, start_event: Event | None = None) -> None:
72  """Attach the triggers."""
73  if CONF_ACTION in self.configconfig:
74  self._script_script = Script(
75  self.hasshass,
76  self.configconfig[CONF_ACTION],
77  self.namename,
78  DOMAIN,
79  )
80 
81  if CONF_CONDITION in self.configconfig:
82  self._cond_func_cond_func = await condition.async_conditions_from_config(
83  self.hasshass, self.configconfig[CONF_CONDITION], _LOGGER, "template entity"
84  )
85 
86  if start_event is not None:
87  self._unsub_start_unsub_start = None
88 
89  if self._script_script:
90  action: Callable = self._handle_triggered_with_script_handle_triggered_with_script
91  else:
92  action = self._handle_triggered_handle_triggered
93 
94  self._unsub_trigger_unsub_trigger = await trigger_helper.async_initialize_triggers(
95  self.hasshass,
96  self.configconfig[CONF_TRIGGER],
97  action,
98  DOMAIN,
99  self.namename,
100  self.loggerlogger.log,
101  start_event is not None,
102  )
103 
105  self, run_variables: TemplateVarsType, context: Context | None = None
106  ) -> None:
107  if not self._check_condition_check_condition(run_variables):
108  return
109  # Create a context referring to the trigger context.
110  trigger_context_id = None if context is None else context.id
111  script_context = Context(parent_id=trigger_context_id)
112  if TYPE_CHECKING:
113  # This method is only called if there's a script
114  assert self._script_script is not None
115  if script_result := await self._script_script.async_run(run_variables, script_context):
116  run_variables = script_result.variables
117  self._execute_update_execute_update(run_variables, context)
118 
119  async def _handle_triggered(
120  self, run_variables: TemplateVarsType, context: Context | None = None
121  ) -> None:
122  if not self._check_condition_check_condition(run_variables):
123  return
124  self._execute_update_execute_update(run_variables, context)
125 
126  def _check_condition(self, run_variables: TemplateVarsType) -> bool:
127  if not self._cond_func_cond_func:
128  return True
129  condition_result = self._cond_func_cond_func(run_variables)
130  if condition_result is False:
131  _LOGGER.debug(
132  "Conditions not met, aborting template trigger update. Condition summary: %s",
133  trace_get(clear=False),
134  )
135  return condition_result
136 
137  @callback
139  self, run_variables: TemplateVarsType, context: Context | None = None
140  ) -> None:
141  self.async_set_updated_dataasync_set_updated_data(
142  {"run_variables": run_variables, "context": context}
143  )
None _handle_triggered_with_script(self, TemplateVarsType run_variables, Context|None context=None)
Definition: coordinator.py:106
bool _check_condition(self, TemplateVarsType run_variables)
Definition: coordinator.py:126
None _handle_triggered(self, TemplateVarsType run_variables, Context|None context=None)
Definition: coordinator.py:121
None _execute_update(self, TemplateVarsType run_variables, Context|None context=None)
Definition: coordinator.py:140
None __init__(self, HomeAssistant hass, dict[str, Any] config)
Definition: coordinator.py:25
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
dict[str, deque[TraceElement]]|None trace_get(bool clear=True)
Definition: trace.py:194