Home Assistant Unofficial Reference 2024.12.1
entity.py
Go to the documentation of this file.
1 """Base entity for the Switch as X integration."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from homeassistant.components.homeassistant import exposed_entities
8 from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
9 from homeassistant.const import (
10  ATTR_ENTITY_ID,
11  SERVICE_TURN_OFF,
12  SERVICE_TURN_ON,
13  STATE_ON,
14  STATE_UNAVAILABLE,
15 )
16 from homeassistant.core import Event, EventStateChangedData, HomeAssistant, callback
17 from homeassistant.helpers import device_registry as dr, entity_registry as er
18 from homeassistant.helpers.device_registry import DeviceInfo
19 from homeassistant.helpers.entity import Entity, ToggleEntity
20 from homeassistant.helpers.event import async_track_state_change_event
21 
22 from .const import DOMAIN as SWITCH_AS_X_DOMAIN
23 
24 
26  """Represents a Switch as an X."""
27 
28  _attr_should_poll = False
29  _is_new_entity: bool
30 
31  def __init__(
32  self,
33  hass: HomeAssistant,
34  config_entry_title: str,
35  domain: str,
36  switch_entity_id: str,
37  unique_id: str,
38  ) -> None:
39  """Initialize Switch as an X."""
40  registry = er.async_get(hass)
41  device_registry = dr.async_get(hass)
42  wrapped_switch = registry.async_get(switch_entity_id)
43  device_id = wrapped_switch.device_id if wrapped_switch else None
44  entity_category = wrapped_switch.entity_category if wrapped_switch else None
45  has_entity_name = wrapped_switch.has_entity_name if wrapped_switch else False
46 
47  name: str | None = config_entry_title
48  if wrapped_switch:
49  name = wrapped_switch.original_name
50 
51  self._device_id_device_id = device_id
52  if device_id and (device := device_registry.async_get(device_id)):
53  self._attr_device_info_attr_device_info = DeviceInfo(
54  connections=device.connections,
55  identifiers=device.identifiers,
56  )
57  self._attr_entity_category_attr_entity_category = entity_category
58  self._attr_has_entity_name_attr_has_entity_name = has_entity_name
59  self._attr_name_attr_name = name
60  self._attr_unique_id_attr_unique_id = unique_id
61  self._switch_entity_id_switch_entity_id = switch_entity_id
62 
63  self._is_new_entity_is_new_entity = (
64  registry.async_get_entity_id(domain, SWITCH_AS_X_DOMAIN, unique_id) is None
65  )
66 
67  @callback
69  self, event: Event[EventStateChangedData] | None = None
70  ) -> None:
71  """Handle child updates."""
72  if (
73  state := self.hasshass.states.get(self._switch_entity_id_switch_entity_id)
74  ) is None or state.state == STATE_UNAVAILABLE:
75  self._attr_available_attr_available = False
76  return
77 
78  self._attr_available_attr_available = True
79 
80  async def async_added_to_hass(self) -> None:
81  """Register callbacks and copy the wrapped entity's custom name if set."""
82 
83  @callback
84  def _async_state_changed_listener(
85  event: Event[EventStateChangedData] | None = None,
86  ) -> None:
87  """Handle child updates."""
88  self.async_state_changed_listenerasync_state_changed_listener(event)
89  self.async_write_ha_stateasync_write_ha_state()
90 
91  self.async_on_removeasync_on_remove(
93  self.hasshass, [self._switch_entity_id_switch_entity_id], _async_state_changed_listener
94  )
95  )
96 
97  # Call once on adding
98  _async_state_changed_listener()
99 
100  # Update entity options
101  registry = er.async_get(self.hasshass)
102  if registry.async_get(self.entity_identity_id) is not None:
103  registry.async_update_entity_options(
104  self.entity_identity_id,
105  SWITCH_AS_X_DOMAIN,
106  self.async_generate_entity_optionsasync_generate_entity_options(),
107  )
108 
109  if not self._is_new_entity_is_new_entity or not (
110  wrapped_switch := registry.async_get(self._switch_entity_id_switch_entity_id)
111  ):
112  return
113 
114  def copy_custom_name(wrapped_switch: er.RegistryEntry) -> None:
115  """Copy the name set by user from the wrapped entity."""
116  if wrapped_switch.name is None:
117  return
118  registry.async_update_entity(self.entity_identity_id, name=wrapped_switch.name)
119 
120  def copy_expose_settings() -> None:
121  """Copy assistant expose settings from the wrapped entity.
122 
123  Also unexpose the wrapped entity if exposed.
124  """
125  expose_settings = exposed_entities.async_get_entity_settings(
126  self.hasshass, self._switch_entity_id_switch_entity_id
127  )
128  for assistant, settings in expose_settings.items():
129  if (should_expose := settings.get("should_expose")) is None:
130  continue
131  exposed_entities.async_expose_entity(
132  self.hasshass, assistant, self.entity_identity_id, should_expose
133  )
134  exposed_entities.async_expose_entity(
135  self.hasshass, assistant, self._switch_entity_id_switch_entity_id, False
136  )
137 
138  copy_custom_name(wrapped_switch)
139  copy_expose_settings()
140 
141  @callback
142  def async_generate_entity_options(self) -> dict[str, Any]:
143  """Generate entity options."""
144  return {"entity_id": self._switch_entity_id_switch_entity_id, "invert": False}
145 
146 
148  """Represents a Switch as a ToggleEntity."""
149 
150  async def async_turn_on(self, **kwargs: Any) -> None:
151  """Forward the turn_on command to the switch in this light switch."""
152  await self.hasshass.services.async_call(
153  SWITCH_DOMAIN,
154  SERVICE_TURN_ON,
155  {ATTR_ENTITY_ID: self._switch_entity_id_switch_entity_id},
156  blocking=True,
157  context=self._context_context,
158  )
159 
160  async def async_turn_off(self, **kwargs: Any) -> None:
161  """Forward the turn_off command to the switch in this light switch."""
162  await self.hasshass.services.async_call(
163  SWITCH_DOMAIN,
164  SERVICE_TURN_OFF,
165  {ATTR_ENTITY_ID: self._switch_entity_id_switch_entity_id},
166  blocking=True,
167  context=self._context_context,
168  )
169 
170  @callback
172  self, event: Event[EventStateChangedData] | None = None
173  ) -> None:
174  """Handle child updates."""
175  super().async_state_changed_listener(event)
176  if (
177  not self.availableavailable
178  or (state := self.hasshass.states.get(self._switch_entity_id_switch_entity_id)) is None
179  ):
180  return
181 
182  self._attr_is_on_attr_is_on = state.state == STATE_ON
183 
184 
186  """Represents a Switch as an X."""
187 
188  def __init__(
189  self,
190  hass: HomeAssistant,
191  config_entry_title: str,
192  domain: str,
193  invert: bool,
194  switch_entity_id: str,
195  unique_id: str,
196  ) -> None:
197  """Initialize Switch as an X."""
198  super().__init__(hass, config_entry_title, domain, switch_entity_id, unique_id)
199  self._invert_state_invert_state = invert
200 
201  @callback
202  def async_generate_entity_options(self) -> dict[str, Any]:
203  """Generate entity options."""
204  return super().async_generate_entity_options() | {"invert": self._invert_state_invert_state}
None __init__(self, HomeAssistant hass, str config_entry_title, str domain, str switch_entity_id, str unique_id)
Definition: entity.py:38
None async_state_changed_listener(self, Event[EventStateChangedData]|None event=None)
Definition: entity.py:70
None __init__(self, HomeAssistant hass, str config_entry_title, str domain, bool invert, str switch_entity_id, str unique_id)
Definition: entity.py:196
None async_state_changed_listener(self, Event[EventStateChangedData]|None event=None)
Definition: entity.py:173
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
CALLBACK_TYPE async_track_state_change_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventStateChangedData]], Any] action, HassJobType|None job_type=None)
Definition: event.py:314