Home Assistant Unofficial Reference 2024.12.1
device_condition.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.const import (
8  CONF_ABOVE,
9  CONF_BELOW,
10  CONF_CONDITION,
11  CONF_DEVICE_ID,
12  CONF_DOMAIN,
13  CONF_ENTITY_ID,
14  CONF_TYPE,
15 )
16 from homeassistant.core import HomeAssistant, callback
17 from homeassistant.helpers import (
18  condition,
19  config_validation as cv,
20  entity_registry as er,
21 )
22 from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
23 from homeassistant.helpers.entity import get_supported_features
24 from homeassistant.helpers.typing import ConfigType, TemplateVarsType
25 
26 from . import DOMAIN, CoverEntityFeature, CoverState
27 
28 # mypy: disallow-any-generics
29 
30 POSITION_CONDITION_TYPES = {"is_position", "is_tilt_position"}
31 STATE_CONDITION_TYPES = {"is_open", "is_closed", "is_opening", "is_closing"}
32 
33 POSITION_CONDITION_SCHEMA = vol.All(
34  DEVICE_CONDITION_BASE_SCHEMA.extend(
35  {
36  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
37  vol.Required(CONF_TYPE): vol.In(POSITION_CONDITION_TYPES),
38  vol.Optional(CONF_ABOVE): vol.All(
39  vol.Coerce(int), vol.Range(min=0, max=100)
40  ),
41  vol.Optional(CONF_BELOW): vol.All(
42  vol.Coerce(int), vol.Range(min=0, max=100)
43  ),
44  }
45  ),
46  cv.has_at_least_one_key(CONF_BELOW, CONF_ABOVE),
47 )
48 
49 STATE_CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend(
50  {
51  vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
52  vol.Required(CONF_TYPE): vol.In(STATE_CONDITION_TYPES),
53  }
54 )
55 
56 CONDITION_SCHEMA = vol.Any(POSITION_CONDITION_SCHEMA, STATE_CONDITION_SCHEMA)
57 
58 
60  hass: HomeAssistant, device_id: str
61 ) -> list[dict[str, str]]:
62  """List device conditions for Cover devices."""
63  registry = er.async_get(hass)
64  conditions: list[dict[str, str]] = []
65 
66  # Get all the integrations entities for this device
67  for entry in er.async_entries_for_device(registry, device_id):
68  if entry.domain != DOMAIN:
69  continue
70 
71  supported_features = get_supported_features(hass, entry.entity_id)
72  supports_open_close = supported_features & (
73  CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
74  )
75 
76  # Add conditions for each entity that belongs to this integration
77  base_condition = {
78  CONF_CONDITION: "device",
79  CONF_DEVICE_ID: device_id,
80  CONF_DOMAIN: DOMAIN,
81  CONF_ENTITY_ID: entry.id,
82  }
83 
84  if supports_open_close:
85  conditions += [
86  {**base_condition, CONF_TYPE: cond} for cond in STATE_CONDITION_TYPES
87  ]
88  if supported_features & CoverEntityFeature.SET_POSITION:
89  conditions.append({**base_condition, CONF_TYPE: "is_position"})
90  if supported_features & CoverEntityFeature.SET_TILT_POSITION:
91  conditions.append({**base_condition, CONF_TYPE: "is_tilt_position"})
92 
93  return conditions
94 
95 
97  hass: HomeAssistant, config: ConfigType
98 ) -> dict[str, vol.Schema]:
99  """List condition capabilities."""
100  if config[CONF_TYPE] not in ["is_position", "is_tilt_position"]:
101  return {}
102 
103  return {
104  "extra_fields": vol.Schema(
105  {
106  vol.Optional(CONF_ABOVE, default=0): vol.All(
107  vol.Coerce(int), vol.Range(min=0, max=100)
108  ),
109  vol.Optional(CONF_BELOW, default=100): vol.All(
110  vol.Coerce(int), vol.Range(min=0, max=100)
111  ),
112  }
113  )
114  }
115 
116 
117 @callback
119  hass: HomeAssistant, config: ConfigType
120 ) -> condition.ConditionCheckerType:
121  """Create a function to test a device condition."""
122  registry = er.async_get(hass)
123  entity_id = er.async_resolve_entity_id(registry, config[CONF_ENTITY_ID])
124 
125  if config[CONF_TYPE] in STATE_CONDITION_TYPES:
126  if config[CONF_TYPE] == "is_open":
127  state = CoverState.OPEN
128  elif config[CONF_TYPE] == "is_closed":
129  state = CoverState.CLOSED
130  elif config[CONF_TYPE] == "is_opening":
131  state = CoverState.OPENING
132  elif config[CONF_TYPE] == "is_closing":
133  state = CoverState.CLOSING
134 
135  def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
136  """Test if an entity is a certain state."""
137  return condition.state(hass, entity_id, state)
138 
139  return test_is_state
140 
141  if config[CONF_TYPE] == "is_position":
142  position_attr = "current_position"
143  if config[CONF_TYPE] == "is_tilt_position":
144  position_attr = "current_tilt_position"
145  min_pos = config.get(CONF_ABOVE)
146  max_pos = config.get(CONF_BELOW)
147 
148  @callback
149  def check_numeric_state(
150  hass: HomeAssistant, variables: TemplateVarsType = None
151  ) -> bool:
152  """Return whether the criteria are met."""
153  return condition.async_numeric_state(
154  hass, entity_id, max_pos, min_pos, attribute=position_attr
155  )
156 
157  return check_numeric_state
dict[str, vol.Schema] async_get_condition_capabilities(HomeAssistant hass, ConfigType config)
list[dict[str, str]] async_get_conditions(HomeAssistant hass, str device_id)
condition.ConditionCheckerType async_condition_from_config(HomeAssistant hass, ConfigType config)
int get_supported_features(HomeAssistant hass, str entity_id)
Definition: entity.py:169