Home Assistant Unofficial Reference 2024.12.1
device_trigger.py
Go to the documentation of this file.
1 """Provides device automations for Cover."""
2 
3 from __future__ import annotations
4 
5 import voluptuous as vol
6 
7 from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
9  numeric_state as numeric_state_trigger,
10  state as state_trigger,
11 )
12 from homeassistant.const import (
13  CONF_ABOVE,
14  CONF_BELOW,
15  CONF_DEVICE_ID,
16  CONF_DOMAIN,
17  CONF_ENTITY_ID,
18  CONF_FOR,
19  CONF_PLATFORM,
20  CONF_TYPE,
21  CONF_VALUE_TEMPLATE,
22 )
23 from homeassistant.core import CALLBACK_TYPE, HomeAssistant
24 from homeassistant.helpers import config_validation as cv, entity_registry as er
25 from homeassistant.helpers.entity import get_supported_features
26 from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
27 from homeassistant.helpers.typing import ConfigType
28 
29 from . import DOMAIN, CoverEntityFeature, CoverState
30 
31 POSITION_TRIGGER_TYPES = {"position", "tilt_position"}
32 STATE_TRIGGER_TYPES = {"opened", "closed", "opening", "closing"}
33 
34 POSITION_TRIGGER_SCHEMA = vol.All(
35  DEVICE_TRIGGER_BASE_SCHEMA.extend(
36  {
37  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
38  vol.Required(CONF_TYPE): vol.In(POSITION_TRIGGER_TYPES),
39  vol.Optional(CONF_ABOVE): vol.All(
40  vol.Coerce(int), vol.Range(min=0, max=100)
41  ),
42  vol.Optional(CONF_BELOW): vol.All(
43  vol.Coerce(int), vol.Range(min=0, max=100)
44  ),
45  }
46  ),
47  cv.has_at_least_one_key(CONF_BELOW, CONF_ABOVE),
48 )
49 
50 STATE_TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
51  {
52  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
53  vol.Required(CONF_TYPE): vol.In(STATE_TRIGGER_TYPES),
54  vol.Optional(CONF_FOR): cv.positive_time_period_dict,
55  }
56 )
57 
58 TRIGGER_SCHEMA = vol.Any(POSITION_TRIGGER_SCHEMA, STATE_TRIGGER_SCHEMA)
59 
60 
62  hass: HomeAssistant, device_id: str
63 ) -> list[dict[str, str]]:
64  """List device triggers for Cover devices."""
65  registry = er.async_get(hass)
66  triggers = []
67 
68  # Get all the integrations entities for this device
69  for entry in er.async_entries_for_device(registry, device_id):
70  if entry.domain != DOMAIN:
71  continue
72 
73  supported_features = get_supported_features(hass, entry.entity_id)
74  supports_open_close = supported_features & (
75  CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
76  )
77 
78  # Add triggers for each entity that belongs to this integration
79  base_trigger = {
80  CONF_PLATFORM: "device",
81  CONF_DEVICE_ID: device_id,
82  CONF_DOMAIN: DOMAIN,
83  CONF_ENTITY_ID: entry.id,
84  }
85 
86  if supports_open_close:
87  triggers += [
88  {
89  **base_trigger,
90  CONF_TYPE: trigger,
91  }
92  for trigger in STATE_TRIGGER_TYPES
93  ]
94  if supported_features & CoverEntityFeature.SET_POSITION:
95  triggers.append(
96  {
97  **base_trigger,
98  CONF_TYPE: "position",
99  }
100  )
101  if supported_features & CoverEntityFeature.SET_TILT_POSITION:
102  triggers.append(
103  {
104  **base_trigger,
105  CONF_TYPE: "tilt_position",
106  }
107  )
108 
109  return triggers
110 
111 
113  hass: HomeAssistant, config: ConfigType
114 ) -> dict[str, vol.Schema]:
115  """List trigger capabilities."""
116  if config[CONF_TYPE] not in POSITION_TRIGGER_TYPES:
117  return {
118  "extra_fields": vol.Schema(
119  {vol.Optional(CONF_FOR): cv.positive_time_period_dict}
120  )
121  }
122 
123  return {
124  "extra_fields": vol.Schema(
125  {
126  vol.Optional(CONF_ABOVE, default=0): vol.All(
127  vol.Coerce(int), vol.Range(min=0, max=100)
128  ),
129  vol.Optional(CONF_BELOW, default=100): vol.All(
130  vol.Coerce(int), vol.Range(min=0, max=100)
131  ),
132  }
133  )
134  }
135 
136 
138  hass: HomeAssistant,
139  config: ConfigType,
140  action: TriggerActionType,
141  trigger_info: TriggerInfo,
142 ) -> CALLBACK_TYPE:
143  """Attach a trigger."""
144  if config[CONF_TYPE] in STATE_TRIGGER_TYPES:
145  if config[CONF_TYPE] == "opened":
146  to_state = CoverState.OPEN
147  elif config[CONF_TYPE] == "closed":
148  to_state = CoverState.CLOSED
149  elif config[CONF_TYPE] == "opening":
150  to_state = CoverState.OPENING
151  elif config[CONF_TYPE] == "closing":
152  to_state = CoverState.CLOSING
153 
154  state_config = {
155  CONF_PLATFORM: "state",
156  CONF_ENTITY_ID: config[CONF_ENTITY_ID],
157  state_trigger.CONF_TO: to_state,
158  }
159  if CONF_FOR in config:
160  state_config[CONF_FOR] = config[CONF_FOR]
161  state_config = await state_trigger.async_validate_trigger_config(
162  hass, state_config
163  )
164  return await state_trigger.async_attach_trigger(
165  hass, state_config, action, trigger_info, platform_type="device"
166  )
167 
168  if config[CONF_TYPE] == "position":
169  position = "current_position"
170  if config[CONF_TYPE] == "tilt_position":
171  position = "current_tilt_position"
172  min_pos = config.get(CONF_ABOVE, -1)
173  max_pos = config.get(CONF_BELOW, 101)
174  value_template = f"{{{{ state.attributes.{position} }}}}"
175 
176  numeric_state_config = {
177  CONF_PLATFORM: "numeric_state",
178  CONF_ENTITY_ID: config[CONF_ENTITY_ID],
179  CONF_BELOW: max_pos,
180  CONF_ABOVE: min_pos,
181  CONF_VALUE_TEMPLATE: value_template,
182  }
183  numeric_state_config = await numeric_state_trigger.async_validate_trigger_config(
184  hass, numeric_state_config
185  )
186  return await numeric_state_trigger.async_attach_trigger(
187  hass, numeric_state_config, action, trigger_info, platform_type="device"
188  )
dict[str, vol.Schema] async_get_trigger_capabilities(HomeAssistant hass, ConfigType config)
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
list[dict[str, str]] async_get_triggers(HomeAssistant hass, str device_id)
int get_supported_features(HomeAssistant hass, str entity_id)
Definition: entity.py:169