Home Assistant Unofficial Reference 2024.12.1
device_condition.py
Go to the documentation of this file.
1 """Implement device conditions for binary sensor."""
2 
3 from __future__ import annotations
4 
5 import voluptuous as vol
6 
7 from homeassistant.components.device_automation import CONF_IS_OFF, CONF_IS_ON
8 from homeassistant.const import (
9  CONF_CONDITION,
10  CONF_ENTITY_ID,
11  CONF_FOR,
12  CONF_STATE,
13  CONF_TYPE,
14 )
15 from homeassistant.core import HomeAssistant, callback
16 from homeassistant.helpers import (
17  condition,
18  config_validation as cv,
19  entity_registry as er,
20 )
21 from homeassistant.helpers.entity import get_device_class
22 from homeassistant.helpers.typing import ConfigType
23 
24 from . import DOMAIN, BinarySensorDeviceClass
25 
26 # mypy: disallow-any-generics
27 
28 DEVICE_CLASS_NONE = "none"
29 
30 CONF_IS_BAT_LOW = "is_bat_low"
31 CONF_IS_NOT_BAT_LOW = "is_not_bat_low"
32 CONF_IS_CHARGING = "is_charging"
33 CONF_IS_NOT_CHARGING = "is_not_charging"
34 CONF_IS_CO = "is_co"
35 CONF_IS_NO_CO = "is_no_co"
36 CONF_IS_COLD = "is_cold"
37 CONF_IS_NOT_COLD = "is_not_cold"
38 CONF_IS_CONNECTED = "is_connected"
39 CONF_IS_NOT_CONNECTED = "is_not_connected"
40 CONF_IS_GAS = "is_gas"
41 CONF_IS_NO_GAS = "is_no_gas"
42 CONF_IS_HOT = "is_hot"
43 CONF_IS_NOT_HOT = "is_not_hot"
44 CONF_IS_LIGHT = "is_light"
45 CONF_IS_NO_LIGHT = "is_no_light"
46 CONF_IS_LOCKED = "is_locked"
47 CONF_IS_NOT_LOCKED = "is_not_locked"
48 CONF_IS_MOIST = "is_moist"
49 CONF_IS_NOT_MOIST = "is_not_moist"
50 CONF_IS_MOTION = "is_motion"
51 CONF_IS_NO_MOTION = "is_no_motion"
52 CONF_IS_MOVING = "is_moving"
53 CONF_IS_NOT_MOVING = "is_not_moving"
54 CONF_IS_OCCUPIED = "is_occupied"
55 CONF_IS_NOT_OCCUPIED = "is_not_occupied"
56 CONF_IS_PLUGGED_IN = "is_plugged_in"
57 CONF_IS_NOT_PLUGGED_IN = "is_not_plugged_in"
58 CONF_IS_POWERED = "is_powered"
59 CONF_IS_NOT_POWERED = "is_not_powered"
60 CONF_IS_PRESENT = "is_present"
61 CONF_IS_NOT_PRESENT = "is_not_present"
62 CONF_IS_PROBLEM = "is_problem"
63 CONF_IS_NO_PROBLEM = "is_no_problem"
64 CONF_IS_RUNNING = "is_running"
65 CONF_IS_NOT_RUNNING = "is_not_running"
66 CONF_IS_UNSAFE = "is_unsafe"
67 CONF_IS_NOT_UNSAFE = "is_not_unsafe"
68 CONF_IS_SMOKE = "is_smoke"
69 CONF_IS_NO_SMOKE = "is_no_smoke"
70 CONF_IS_SOUND = "is_sound"
71 CONF_IS_NO_SOUND = "is_no_sound"
72 CONF_IS_TAMPERED = "is_tampered"
73 CONF_IS_NOT_TAMPERED = "is_not_tampered"
74 CONF_IS_UPDATE = "is_update"
75 CONF_IS_NO_UPDATE = "is_no_update"
76 CONF_IS_VIBRATION = "is_vibration"
77 CONF_IS_NO_VIBRATION = "is_no_vibration"
78 CONF_IS_OPEN = "is_open"
79 CONF_IS_NOT_OPEN = "is_not_open"
80 
81 IS_ON = [
82  CONF_IS_BAT_LOW,
83  CONF_IS_CHARGING,
84  CONF_IS_CO,
85  CONF_IS_COLD,
86  CONF_IS_CONNECTED,
87  CONF_IS_GAS,
88  CONF_IS_HOT,
89  CONF_IS_LIGHT,
90  CONF_IS_NOT_LOCKED,
91  CONF_IS_MOIST,
92  CONF_IS_MOTION,
93  CONF_IS_MOVING,
94  CONF_IS_OCCUPIED,
95  CONF_IS_OPEN,
96  CONF_IS_PLUGGED_IN,
97  CONF_IS_POWERED,
98  CONF_IS_PRESENT,
99  CONF_IS_PROBLEM,
100  CONF_IS_RUNNING,
101  CONF_IS_SMOKE,
102  CONF_IS_SOUND,
103  CONF_IS_TAMPERED,
104  CONF_IS_UPDATE,
105  CONF_IS_UNSAFE,
106  CONF_IS_VIBRATION,
107  CONF_IS_ON,
108 ]
109 
110 IS_OFF = [
111  CONF_IS_NOT_BAT_LOW,
112  CONF_IS_NOT_CHARGING,
113  CONF_IS_NOT_COLD,
114  CONF_IS_NOT_CONNECTED,
115  CONF_IS_NOT_HOT,
116  CONF_IS_LOCKED,
117  CONF_IS_NOT_MOIST,
118  CONF_IS_NOT_MOVING,
119  CONF_IS_NOT_OCCUPIED,
120  CONF_IS_NOT_OPEN,
121  CONF_IS_NOT_PLUGGED_IN,
122  CONF_IS_NOT_POWERED,
123  CONF_IS_NOT_PRESENT,
124  CONF_IS_NOT_TAMPERED,
125  CONF_IS_NOT_UNSAFE,
126  CONF_IS_NO_CO,
127  CONF_IS_NO_GAS,
128  CONF_IS_NO_LIGHT,
129  CONF_IS_NO_MOTION,
130  CONF_IS_NO_PROBLEM,
131  CONF_IS_NOT_RUNNING,
132  CONF_IS_NO_SMOKE,
133  CONF_IS_NO_SOUND,
134  CONF_IS_NO_UPDATE,
135  CONF_IS_NO_VIBRATION,
136  CONF_IS_OFF,
137 ]
138 
139 ENTITY_CONDITIONS = {
140  BinarySensorDeviceClass.BATTERY: [
141  {CONF_TYPE: CONF_IS_BAT_LOW},
142  {CONF_TYPE: CONF_IS_NOT_BAT_LOW},
143  ],
144  BinarySensorDeviceClass.BATTERY_CHARGING: [
145  {CONF_TYPE: CONF_IS_CHARGING},
146  {CONF_TYPE: CONF_IS_NOT_CHARGING},
147  ],
148  BinarySensorDeviceClass.CO: [
149  {CONF_TYPE: CONF_IS_CO},
150  {CONF_TYPE: CONF_IS_NO_CO},
151  ],
152  BinarySensorDeviceClass.COLD: [
153  {CONF_TYPE: CONF_IS_COLD},
154  {CONF_TYPE: CONF_IS_NOT_COLD},
155  ],
156  BinarySensorDeviceClass.CONNECTIVITY: [
157  {CONF_TYPE: CONF_IS_CONNECTED},
158  {CONF_TYPE: CONF_IS_NOT_CONNECTED},
159  ],
160  BinarySensorDeviceClass.DOOR: [
161  {CONF_TYPE: CONF_IS_OPEN},
162  {CONF_TYPE: CONF_IS_NOT_OPEN},
163  ],
164  BinarySensorDeviceClass.GARAGE_DOOR: [
165  {CONF_TYPE: CONF_IS_OPEN},
166  {CONF_TYPE: CONF_IS_NOT_OPEN},
167  ],
168  BinarySensorDeviceClass.GAS: [
169  {CONF_TYPE: CONF_IS_GAS},
170  {CONF_TYPE: CONF_IS_NO_GAS},
171  ],
172  BinarySensorDeviceClass.HEAT: [
173  {CONF_TYPE: CONF_IS_HOT},
174  {CONF_TYPE: CONF_IS_NOT_HOT},
175  ],
176  BinarySensorDeviceClass.LIGHT: [
177  {CONF_TYPE: CONF_IS_LIGHT},
178  {CONF_TYPE: CONF_IS_NO_LIGHT},
179  ],
180  BinarySensorDeviceClass.LOCK: [
181  {CONF_TYPE: CONF_IS_LOCKED},
182  {CONF_TYPE: CONF_IS_NOT_LOCKED},
183  ],
184  BinarySensorDeviceClass.MOISTURE: [
185  {CONF_TYPE: CONF_IS_MOIST},
186  {CONF_TYPE: CONF_IS_NOT_MOIST},
187  ],
188  BinarySensorDeviceClass.MOTION: [
189  {CONF_TYPE: CONF_IS_MOTION},
190  {CONF_TYPE: CONF_IS_NO_MOTION},
191  ],
192  BinarySensorDeviceClass.MOVING: [
193  {CONF_TYPE: CONF_IS_MOVING},
194  {CONF_TYPE: CONF_IS_NOT_MOVING},
195  ],
196  BinarySensorDeviceClass.OCCUPANCY: [
197  {CONF_TYPE: CONF_IS_OCCUPIED},
198  {CONF_TYPE: CONF_IS_NOT_OCCUPIED},
199  ],
200  BinarySensorDeviceClass.OPENING: [
201  {CONF_TYPE: CONF_IS_OPEN},
202  {CONF_TYPE: CONF_IS_NOT_OPEN},
203  ],
204  BinarySensorDeviceClass.PLUG: [
205  {CONF_TYPE: CONF_IS_PLUGGED_IN},
206  {CONF_TYPE: CONF_IS_NOT_PLUGGED_IN},
207  ],
208  BinarySensorDeviceClass.POWER: [
209  {CONF_TYPE: CONF_IS_POWERED},
210  {CONF_TYPE: CONF_IS_NOT_POWERED},
211  ],
212  BinarySensorDeviceClass.PRESENCE: [
213  {CONF_TYPE: CONF_IS_PRESENT},
214  {CONF_TYPE: CONF_IS_NOT_PRESENT},
215  ],
216  BinarySensorDeviceClass.PROBLEM: [
217  {CONF_TYPE: CONF_IS_PROBLEM},
218  {CONF_TYPE: CONF_IS_NO_PROBLEM},
219  ],
220  BinarySensorDeviceClass.RUNNING: [
221  {CONF_TYPE: CONF_IS_RUNNING},
222  {CONF_TYPE: CONF_IS_NOT_RUNNING},
223  ],
224  BinarySensorDeviceClass.SAFETY: [
225  {CONF_TYPE: CONF_IS_UNSAFE},
226  {CONF_TYPE: CONF_IS_NOT_UNSAFE},
227  ],
228  BinarySensorDeviceClass.SMOKE: [
229  {CONF_TYPE: CONF_IS_SMOKE},
230  {CONF_TYPE: CONF_IS_NO_SMOKE},
231  ],
232  BinarySensorDeviceClass.SOUND: [
233  {CONF_TYPE: CONF_IS_SOUND},
234  {CONF_TYPE: CONF_IS_NO_SOUND},
235  ],
236  BinarySensorDeviceClass.TAMPER: [
237  {CONF_TYPE: CONF_IS_TAMPERED},
238  {CONF_TYPE: CONF_IS_NOT_TAMPERED},
239  ],
240  BinarySensorDeviceClass.UPDATE: [
241  {CONF_TYPE: CONF_IS_UPDATE},
242  {CONF_TYPE: CONF_IS_NO_UPDATE},
243  ],
244  BinarySensorDeviceClass.VIBRATION: [
245  {CONF_TYPE: CONF_IS_VIBRATION},
246  {CONF_TYPE: CONF_IS_NO_VIBRATION},
247  ],
248  BinarySensorDeviceClass.WINDOW: [
249  {CONF_TYPE: CONF_IS_OPEN},
250  {CONF_TYPE: CONF_IS_NOT_OPEN},
251  ],
252  DEVICE_CLASS_NONE: [
253  {CONF_TYPE: CONF_IS_ON},
254  {CONF_TYPE: CONF_IS_OFF},
255  ],
256 }
257 
258 CONDITION_SCHEMA = cv.DEVICE_CONDITION_BASE_SCHEMA.extend(
259  {
260  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
261  vol.Required(CONF_TYPE): vol.In(IS_OFF + IS_ON),
262  vol.Optional(CONF_FOR): cv.positive_time_period_dict,
263  }
264 )
265 
266 
268  hass: HomeAssistant, device_id: str
269 ) -> list[dict[str, str]]:
270  """List device conditions."""
271  conditions: list[dict[str, str]] = []
272  entity_registry = er.async_get(hass)
273  entries = [
274  entry
275  for entry in er.async_entries_for_device(entity_registry, device_id)
276  if entry.domain == DOMAIN
277  ]
278 
279  for entry in entries:
280  device_class = get_device_class(hass, entry.entity_id) or DEVICE_CLASS_NONE
281 
282  templates = ENTITY_CONDITIONS.get(
283  device_class, ENTITY_CONDITIONS[DEVICE_CLASS_NONE]
284  )
285 
286  conditions.extend(
287  {
288  **template,
289  "condition": "device",
290  "device_id": device_id,
291  "entity_id": entry.id,
292  "domain": DOMAIN,
293  }
294  for template in templates
295  )
296 
297  return conditions
298 
299 
300 @callback
302  hass: HomeAssistant, config: ConfigType
303 ) -> condition.ConditionCheckerType:
304  """Evaluate state based on configuration."""
305  condition_type = config[CONF_TYPE]
306  if condition_type in IS_ON:
307  stat = "on"
308  else:
309  stat = "off"
310  state_config = {
311  CONF_CONDITION: "state",
312  CONF_ENTITY_ID: config[CONF_ENTITY_ID],
313  CONF_STATE: stat,
314  }
315  if CONF_FOR in config:
316  state_config[CONF_FOR] = config[CONF_FOR]
317  state_config = cv.STATE_CONDITION_SCHEMA(state_config)
318  state_config = condition.state_validate_config(hass, state_config)
319 
320  return condition.state_from_config(state_config)
321 
322 
324  hass: HomeAssistant, config: ConfigType
325 ) -> dict[str, vol.Schema]:
326  """List condition capabilities."""
327  return {
328  "extra_fields": vol.Schema(
329  {vol.Optional(CONF_FOR): cv.positive_time_period_dict}
330  )
331  }
dict[str, vol.Schema] async_get_condition_capabilities(HomeAssistant hass, ConfigType config)
condition.ConditionCheckerType async_condition_from_config(HomeAssistant hass, ConfigType config)
list[dict[str, str]] async_get_conditions(HomeAssistant hass, str device_id)
str|None get_device_class(HomeAssistant hass, str entity_id)
Definition: entity.py:154