Home Assistant Unofficial Reference 2024.12.1
toggle_entity.py
Go to the documentation of this file.
1 """Device automation helpers for toggle entity."""
2 
3 from __future__ import annotations
4 
5 import voluptuous as vol
6 
7 from homeassistant.components.homeassistant.triggers import state as state_trigger
8 from homeassistant.const import (
9  ATTR_ENTITY_ID,
10  CONF_CONDITION,
11  CONF_ENTITY_ID,
12  CONF_FOR,
13  CONF_PLATFORM,
14  CONF_STATE,
15  CONF_TYPE,
16 )
17 from homeassistant.core import CALLBACK_TYPE, Context, HomeAssistant, callback
18 from homeassistant.helpers import (
19  condition,
20  config_validation as cv,
21  entity_registry as er,
22 )
23 from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
24 from homeassistant.helpers.typing import ConfigType, TemplateVarsType
25 
26 from . import DEVICE_TRIGGER_BASE_SCHEMA, entity
27 from .const import (
28  CONF_IS_OFF,
29  CONF_IS_ON,
30  CONF_TOGGLE,
31  CONF_TURN_OFF,
32  CONF_TURN_ON,
33  CONF_TURNED_OFF,
34  CONF_TURNED_ON,
35 )
36 
37 ENTITY_ACTIONS = [
38  {
39  # Turn entity off
40  CONF_TYPE: CONF_TURN_OFF
41  },
42  {
43  # Turn entity on
44  CONF_TYPE: CONF_TURN_ON
45  },
46  {
47  # Toggle entity
48  CONF_TYPE: CONF_TOGGLE
49  },
50 ]
51 
52 ENTITY_CONDITIONS = [
53  {
54  # True when entity is turned off
55  CONF_CONDITION: "device",
56  CONF_TYPE: CONF_IS_OFF,
57  },
58  {
59  # True when entity is turned on
60  CONF_CONDITION: "device",
61  CONF_TYPE: CONF_IS_ON,
62  },
63 ]
64 
65 ENTITY_TRIGGERS = [
66  {
67  # Trigger when entity is turned off
68  CONF_PLATFORM: "device",
69  CONF_TYPE: CONF_TURNED_OFF,
70  },
71  {
72  # Trigger when entity is turned on
73  CONF_PLATFORM: "device",
74  CONF_TYPE: CONF_TURNED_ON,
75  },
76 ]
77 
78 DEVICE_ACTION_TYPES = [CONF_TOGGLE, CONF_TURN_OFF, CONF_TURN_ON]
79 
80 ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
81  {
82  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
83  vol.Required(CONF_TYPE): vol.In(DEVICE_ACTION_TYPES),
84  }
85 )
86 
87 CONDITION_SCHEMA = cv.DEVICE_CONDITION_BASE_SCHEMA.extend(
88  {
89  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
90  vol.Required(CONF_TYPE): vol.In([CONF_IS_OFF, CONF_IS_ON]),
91  vol.Optional(CONF_FOR): cv.positive_time_period_dict,
92  }
93 )
94 
95 _TOGGLE_TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
96  {
97  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
98  vol.Required(CONF_TYPE): vol.In([CONF_TURNED_OFF, CONF_TURNED_ON]),
99  vol.Optional(CONF_FOR): cv.positive_time_period_dict,
100  }
101 )
102 
103 TRIGGER_SCHEMA = vol.Any(entity.TRIGGER_SCHEMA, _TOGGLE_TRIGGER_SCHEMA)
104 
105 
107  hass: HomeAssistant,
108  config: ConfigType,
109  variables: TemplateVarsType,
110  context: Context | None,
111  domain: str,
112 ) -> None:
113  """Change state based on configuration."""
114  action_type = config[CONF_TYPE]
115  if action_type == CONF_TURN_ON:
116  action = "turn_on"
117  elif action_type == CONF_TURN_OFF:
118  action = "turn_off"
119  else:
120  action = "toggle"
121 
122  service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]}
123 
124  await hass.services.async_call(
125  domain, action, service_data, blocking=True, context=context
126  )
127 
128 
129 @callback
131  hass: HomeAssistant, config: ConfigType
132 ) -> condition.ConditionCheckerType:
133  """Evaluate state based on configuration."""
134  if config[CONF_TYPE] == CONF_IS_ON:
135  stat = "on"
136  else:
137  stat = "off"
138  state_config = {
139  CONF_CONDITION: "state",
140  CONF_ENTITY_ID: config[CONF_ENTITY_ID],
141  CONF_STATE: stat,
142  }
143  if CONF_FOR in config:
144  state_config[CONF_FOR] = config[CONF_FOR]
145 
146  state_config = cv.STATE_CONDITION_SCHEMA(state_config)
147  state_config = condition.state_validate_config(hass, state_config)
148  return condition.state_from_config(state_config)
149 
150 
152  hass: HomeAssistant,
153  config: ConfigType,
154  action: TriggerActionType,
155  trigger_info: TriggerInfo,
156 ) -> CALLBACK_TYPE:
157  """Listen for state changes based on configuration."""
158  if config[CONF_TYPE] not in [CONF_TURNED_ON, CONF_TURNED_OFF]:
159  return await entity.async_attach_trigger(hass, config, action, trigger_info)
160 
161  if config[CONF_TYPE] == CONF_TURNED_ON:
162  to_state = "on"
163  else:
164  to_state = "off"
165  state_config = {
166  CONF_PLATFORM: "state",
167  state_trigger.CONF_ENTITY_ID: config[CONF_ENTITY_ID],
168  state_trigger.CONF_TO: to_state,
169  }
170  if CONF_FOR in config:
171  state_config[CONF_FOR] = config[CONF_FOR]
172 
173  state_config = await state_trigger.async_validate_trigger_config(hass, state_config)
174  return await state_trigger.async_attach_trigger(
175  hass, state_config, action, trigger_info, platform_type="device"
176  )
177 
178 
180  hass: HomeAssistant,
181  device_id: str,
182  automation_templates: list[dict[str, str]],
183  domain: str,
184 ) -> list[dict[str, str]]:
185  """List device automations."""
186  automations: list[dict[str, str]] = []
187  entity_registry = er.async_get(hass)
188 
189  entries = [
190  entry
191  for entry in er.async_entries_for_device(entity_registry, device_id)
192  if entry.domain == domain
193  ]
194 
195  for entry in entries:
196  automations.extend(
197  {
198  **template,
199  "device_id": device_id,
200  "entity_id": entry.id,
201  "domain": domain,
202  }
203  for template in automation_templates
204  )
205 
206  return automations
207 
208 
210  hass: HomeAssistant, device_id: str, domain: str
211 ) -> list[dict[str, str]]:
212  """List device actions."""
213  return await _async_get_automations(hass, device_id, ENTITY_ACTIONS, domain)
214 
215 
217  hass: HomeAssistant, device_id: str, domain: str
218 ) -> list[dict[str, str]]:
219  """List device conditions."""
220  return await _async_get_automations(hass, device_id, ENTITY_CONDITIONS, domain)
221 
222 
224  hass: HomeAssistant, device_id: str, domain: str
225 ) -> list[dict[str, str]]:
226  """List device triggers."""
227  triggers = await entity.async_get_triggers(hass, device_id, domain)
228  triggers.extend(
229  await _async_get_automations(hass, device_id, ENTITY_TRIGGERS, domain)
230  )
231  return triggers
232 
233 
235  hass: HomeAssistant, config: ConfigType
236 ) -> dict[str, vol.Schema]:
237  """List condition capabilities."""
238  return {
239  "extra_fields": vol.Schema(
240  {vol.Optional(CONF_FOR): cv.positive_time_period_dict}
241  )
242  }
243 
244 
246  hass: HomeAssistant, config: ConfigType
247 ) -> dict[str, vol.Schema]:
248  """List trigger capabilities."""
249  if config[CONF_TYPE] not in [CONF_TURNED_ON, CONF_TURNED_OFF]:
250  return await entity.async_get_trigger_capabilities(hass, config)
251 
252  return {
253  "extra_fields": vol.Schema(
254  {vol.Optional(CONF_FOR): cv.positive_time_period_dict}
255  )
256  }
dict[str, vol.Schema] async_get_condition_capabilities(HomeAssistant hass, ConfigType config)
list[dict[str, str]] async_get_conditions(HomeAssistant hass, str device_id, str domain)
list[dict[str, str]] async_get_triggers(HomeAssistant hass, str device_id, str domain)
list[dict[str, str]] async_get_actions(HomeAssistant hass, str device_id, str domain)
CALLBACK_TYPE async_attach_trigger(HomeAssistant hass, ConfigType config, TriggerActionType action, TriggerInfo trigger_info)
dict[str, vol.Schema] async_get_trigger_capabilities(HomeAssistant hass, ConfigType config)
list[dict[str, str]] _async_get_automations(HomeAssistant hass, str device_id, list[dict[str, str]] automation_templates, str domain)
None async_call_action_from_config(HomeAssistant hass, ConfigType config, TemplateVarsType variables, Context|None context, str domain)
condition.ConditionCheckerType async_condition_from_config(HomeAssistant hass, ConfigType config)