Home Assistant Unofficial Reference 2024.12.1
registry.py
Go to the documentation of this file.
1 """Provide the functionality to group entities.
2 
3 Legacy group support will not be extended for new domains.
4 """
5 
6 from __future__ import annotations
7 
8 from dataclasses import dataclass
9 from typing import Protocol
10 
11 from homeassistant.components.alarm_control_panel import AlarmControlPanelState
12 from homeassistant.components.climate import HVACMode
13 from homeassistant.components.lock import LockState
14 from homeassistant.components.vacuum import STATE_CLEANING, STATE_ERROR, STATE_RETURNING
16  STATE_ECO,
17  STATE_ELECTRIC,
18  STATE_GAS,
19  STATE_HEAT_PUMP,
20  STATE_HIGH_DEMAND,
21  STATE_PERFORMANCE,
22 )
23 from homeassistant.const import (
24  STATE_CLOSED,
25  STATE_HOME,
26  STATE_IDLE,
27  STATE_NOT_HOME,
28  STATE_OFF,
29  STATE_OK,
30  STATE_ON,
31  STATE_OPEN,
32  STATE_PAUSED,
33  STATE_PLAYING,
34  STATE_PROBLEM,
35  Platform,
36 )
37 from homeassistant.core import HomeAssistant, callback
39  async_process_integration_platforms,
40 )
41 
42 from .const import DOMAIN, REG_KEY
43 
44 # EXCLUDED_DOMAINS and ON_OFF_STATES are considered immutable
45 # in respect that new platforms should not be added.
46 # The only maintenance allowed here is
47 # if existing platforms add new ON or OFF states.
48 EXCLUDED_DOMAINS: set[Platform | str] = {
49  Platform.AIR_QUALITY,
50  Platform.SENSOR,
51  Platform.WEATHER,
52 }
53 
54 ON_OFF_STATES: dict[Platform | str, tuple[set[str], str, str]] = {
55  Platform.ALARM_CONTROL_PANEL: (
56  {
57  STATE_ON,
58  AlarmControlPanelState.ARMED_AWAY,
59  AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
60  AlarmControlPanelState.ARMED_HOME,
61  AlarmControlPanelState.ARMED_NIGHT,
62  AlarmControlPanelState.ARMED_VACATION,
63  AlarmControlPanelState.TRIGGERED,
64  },
65  STATE_ON,
66  STATE_OFF,
67  ),
68  Platform.CLIMATE: (
69  {
70  STATE_ON,
71  HVACMode.HEAT,
72  HVACMode.COOL,
73  HVACMode.HEAT_COOL,
74  HVACMode.AUTO,
75  HVACMode.FAN_ONLY,
76  },
77  STATE_ON,
78  STATE_OFF,
79  ),
80  Platform.COVER: ({STATE_OPEN}, STATE_OPEN, STATE_CLOSED),
81  Platform.DEVICE_TRACKER: ({STATE_HOME}, STATE_HOME, STATE_NOT_HOME),
82  Platform.LOCK: (
83  {
84  LockState.LOCKING,
85  LockState.OPEN,
86  LockState.OPENING,
87  LockState.UNLOCKED,
88  LockState.UNLOCKING,
89  },
90  LockState.UNLOCKED,
91  LockState.LOCKED,
92  ),
93  Platform.MEDIA_PLAYER: (
94  {
95  STATE_ON,
96  STATE_PAUSED,
97  STATE_PLAYING,
98  STATE_IDLE,
99  },
100  STATE_ON,
101  STATE_OFF,
102  ),
103  "person": ({STATE_HOME}, STATE_HOME, STATE_NOT_HOME),
104  "plant": ({STATE_PROBLEM}, STATE_PROBLEM, STATE_OK),
105  Platform.VACUUM: (
106  {
107  STATE_ON,
108  STATE_CLEANING,
109  STATE_RETURNING,
110  STATE_ERROR,
111  },
112  STATE_ON,
113  STATE_OFF,
114  ),
115  Platform.WATER_HEATER: (
116  {
117  STATE_ON,
118  STATE_ECO,
119  STATE_ELECTRIC,
120  STATE_PERFORMANCE,
121  STATE_HIGH_DEMAND,
122  STATE_HEAT_PUMP,
123  STATE_GAS,
124  },
125  STATE_ON,
126  STATE_OFF,
127  ),
128 }
129 
130 
131 async def async_setup(hass: HomeAssistant) -> None:
132  """Set up the Group integration registry of integration platforms."""
133  hass.data[REG_KEY] = GroupIntegrationRegistry(hass)
134 
136  hass, DOMAIN, _process_group_platform, wait_for_platforms=True
137  )
138 
139 
140 class GroupProtocol(Protocol):
141  """Define the format of group platforms."""
142 
144  self, hass: HomeAssistant, registry: GroupIntegrationRegistry
145  ) -> None:
146  """Describe group on off states."""
147 
148 
149 @callback
151  hass: HomeAssistant, domain: str, platform: GroupProtocol
152 ) -> None:
153  """Process a group platform."""
154  platform.async_describe_on_off_states(hass, hass.data[REG_KEY])
155 
156 
157 @dataclass(frozen=True, slots=True)
159  """Dataclass to store a single state type."""
160 
161  on_state: str
162  off_state: str
163 
164 
166  """Class to hold a registry of integrations."""
167 
168  def __init__(self, hass: HomeAssistant) -> None:
169  """Imitialize registry."""
170  self.hasshass = hass
171  self.on_off_mapping: dict[str, str] = {STATE_ON: STATE_OFF}
172  self.off_on_mapping: dict[str, str] = {STATE_OFF: STATE_ON}
173  self.on_states_by_domain: dict[str, set[str]] = {}
174  self.exclude_domainsexclude_domains = EXCLUDED_DOMAINS.copy()
175  self.state_group_mapping: dict[str, SingleStateType] = {}
176  for domain, on_off_states in ON_OFF_STATES.items():
177  self.on_off_stateson_off_states(domain, *on_off_states)
178 
179  @callback
180  def exclude_domain(self, domain: str) -> None:
181  """Exclude the current domain."""
182  self.exclude_domainsexclude_domains.add(domain)
183 
184  @callback
186  self,
187  domain: Platform | str,
188  on_states: set[str],
189  default_on_state: str,
190  off_state: str,
191  ) -> None:
192  """Register on and off states for the current domain.
193 
194  Legacy group support will not be extended for new domains.
195  """
196  for on_state in on_states:
197  if on_state not in self.on_off_mapping:
198  self.on_off_mapping[on_state] = off_state
199 
200  if off_state not in self.off_on_mapping:
201  self.off_on_mapping[off_state] = default_on_state
202  self.state_group_mapping[domain] = SingleStateType(default_on_state, off_state)
203 
204  self.on_states_by_domain[domain] = on_states
None on_off_states(self, Platform|str domain, set[str] on_states, str default_on_state, str off_state)
Definition: registry.py:191
None async_describe_on_off_states(self, HomeAssistant hass, GroupIntegrationRegistry registry)
Definition: registry.py:145
bool add(self, _T matcher)
Definition: match.py:185
None _process_group_platform(HomeAssistant hass, str domain, GroupProtocol platform)
Definition: registry.py:152
None async_setup(HomeAssistant hass)
Definition: registry.py:131
None async_process_integration_platforms(HomeAssistant hass, str platform_name, Callable[[HomeAssistant, str, Any], Awaitable[None]|None] process_platform, bool wait_for_platforms=False)