Home Assistant Unofficial Reference 2024.12.1
trigger.py
Go to the documentation of this file.
1 """Offer zone automation rules."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 import voluptuous as vol
8 
9 from homeassistant.const import (
10  ATTR_FRIENDLY_NAME,
11  CONF_ENTITY_ID,
12  CONF_EVENT,
13  CONF_PLATFORM,
14  CONF_ZONE,
15 )
16 from homeassistant.core import (
17  CALLBACK_TYPE,
18  Event,
19  EventStateChangedData,
20  HassJob,
21  HomeAssistant,
22  callback,
23 )
24 from homeassistant.helpers import (
25  condition,
26  config_validation as cv,
27  entity_registry as er,
28  location,
29 )
30 from homeassistant.helpers.event import async_track_state_change_event
31 from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
32 from homeassistant.helpers.typing import ConfigType
33 
34 EVENT_ENTER = "enter"
35 EVENT_LEAVE = "leave"
36 DEFAULT_EVENT = EVENT_ENTER
37 
38 _LOGGER = logging.getLogger(__name__)
39 
40 _EVENT_DESCRIPTION = {EVENT_ENTER: "entering", EVENT_LEAVE: "leaving"}
41 
42 _TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
43  {
44  vol.Required(CONF_PLATFORM): "zone",
45  vol.Required(CONF_ENTITY_ID): cv.entity_ids_or_uuids,
46  vol.Required(CONF_ZONE): cv.entity_id,
47  vol.Required(CONF_EVENT, default=DEFAULT_EVENT): vol.Any(
48  EVENT_ENTER, EVENT_LEAVE
49  ),
50  }
51 )
52 
53 
55  hass: HomeAssistant, config: ConfigType
56 ) -> ConfigType:
57  """Validate trigger config."""
58  config = _TRIGGER_SCHEMA(config)
59  registry = er.async_get(hass)
60  config[CONF_ENTITY_ID] = er.async_validate_entity_ids(
61  registry, config[CONF_ENTITY_ID]
62  )
63  return config
64 
65 
67  hass: HomeAssistant,
68  config: ConfigType,
69  action: TriggerActionType,
70  trigger_info: TriggerInfo,
71  *,
72  platform_type: str = "zone",
73 ) -> CALLBACK_TYPE:
74  """Listen for state changes based on configuration."""
75  trigger_data = trigger_info["trigger_data"]
76  entity_id: list[str] = config[CONF_ENTITY_ID]
77  zone_entity_id: str = config[CONF_ZONE]
78  event: str = config[CONF_EVENT]
79  job = HassJob(action)
80 
81  @callback
82  def zone_automation_listener(zone_event: Event[EventStateChangedData]) -> None:
83  """Listen for state changes and calls action."""
84  entity = zone_event.data["entity_id"]
85  from_s = zone_event.data["old_state"]
86  to_s = zone_event.data["new_state"]
87 
88  if (
89  from_s
90  and not location.has_location(from_s)
91  or to_s
92  and not location.has_location(to_s)
93  ):
94  return
95 
96  if not (zone_state := hass.states.get(zone_entity_id)):
97  _LOGGER.warning(
98  (
99  "Automation '%s' is referencing non-existing zone '%s' in a zone"
100  " trigger"
101  ),
102  trigger_info["name"],
103  zone_entity_id,
104  )
105  return
106 
107  from_match = condition.zone(hass, zone_state, from_s) if from_s else False
108  to_match = condition.zone(hass, zone_state, to_s) if to_s else False
109 
110  if (
111  event == EVENT_ENTER
112  and not from_match
113  and to_match
114  or event == EVENT_LEAVE
115  and from_match
116  and not to_match
117  ):
118  description = f"{entity} {_EVENT_DESCRIPTION[event]} {zone_state.attributes[ATTR_FRIENDLY_NAME]}"
119  hass.async_run_hass_job(
120  job,
121  {
122  "trigger": {
123  **trigger_data,
124  "platform": platform_type,
125  "entity_id": entity,
126  "from_state": from_s,
127  "to_state": to_s,
128  "zone": zone_state,
129  "event": event,
130  "description": description,
131  }
132  },
133  to_s.context if to_s else None,
134  )
135 
136  return async_track_state_change_event(hass, entity_id, zone_automation_listener)
ConfigType async_validate_trigger_config(HomeAssistant hass, ConfigType config)
Definition: trigger.py:56
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info, *str platform_type="zone")
Definition: trigger.py:73
CALLBACK_TYPE async_track_state_change_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventStateChangedData]], Any] action, HassJobType|None job_type=None)
Definition: event.py:314