Home Assistant Unofficial Reference 2024.12.1
entity.py
Go to the documentation of this file.
1 """Entity for Zigbee Home Automation."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Callable
7 from functools import partial
8 import logging
9 from typing import Any
10 
11 from propcache import cached_property
12 from zha.mixins import LogMixin
13 
14 from homeassistant.const import ATTR_MANUFACTURER, ATTR_MODEL, ATTR_NAME, EntityCategory
15 from homeassistant.core import State, callback
16 from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo
17 from homeassistant.helpers.dispatcher import async_dispatcher_connect
18 from homeassistant.helpers.entity import Entity
19 from homeassistant.helpers.restore_state import RestoreEntity
20 from homeassistant.helpers.typing import UNDEFINED, UndefinedType
21 
22 from .const import DOMAIN
23 from .helpers import SIGNAL_REMOVE_ENTITIES, EntityData, convert_zha_error_to_ha_error
24 
25 _LOGGER = logging.getLogger(__name__)
26 
27 
28 class ZHAEntity(LogMixin, RestoreEntity, Entity):
29  """ZHA eitity."""
30 
31  _attr_has_entity_name = True
32  _attr_should_poll = False
33  remove_future: asyncio.Future[Any]
34 
35  def __init__(self, entity_data: EntityData, *args, **kwargs) -> None:
36  """Init ZHA entity."""
37  super().__init__(*args, **kwargs)
38  self.entity_data: EntityData = entity_data
39  self._unsubs: list[Callable[[], None]] = []
40 
41  if self.entity_data.entity.icon is not None:
42  # Only custom quirks will realistically set an icon
43  self._attr_icon_attr_icon = self.entity_data.entity.icon
44 
45  meta = self.entity_data.entity.info_object
46  self._attr_unique_id_attr_unique_id = meta.unique_id
47 
48  if meta.entity_category is not None:
49  self._attr_entity_category_attr_entity_category = EntityCategory(meta.entity_category)
50 
51  self._attr_entity_registry_enabled_default_attr_entity_registry_enabled_default = (
52  meta.entity_registry_enabled_default
53  )
54 
55  if meta.translation_key is not None:
56  self._attr_translation_key_attr_translation_key = meta.translation_key
57 
58  @cached_property
59  def name(self) -> str | UndefinedType | None:
60  """Return the name of the entity."""
61  meta = self.entity_data.entity.info_object
62  original_name = super().name
63 
64  if original_name not in (UNDEFINED, None) or meta.fallback_name is None:
65  return original_name
66 
67  # This is to allow local development and to register niche devices, since
68  # their translation_key will probably never be added to `zha/strings.json`.
69  self._attr_name_attr_name = meta.fallback_name
70  return super().name
71 
72  @property
73  def available(self) -> bool:
74  """Return entity availability."""
75  return self.entity_data.entity.available
76 
77  @property
78  def device_info(self) -> DeviceInfo:
79  """Return a device description for device registry."""
80  zha_device_info = self.entity_data.device_proxy.device_info
81  ieee = zha_device_info["ieee"]
82  zha_gateway = self.entity_data.device_proxy.gateway_proxy.gateway
83 
84  return DeviceInfo(
85  connections={(CONNECTION_ZIGBEE, ieee)},
86  identifiers={(DOMAIN, ieee)},
87  manufacturer=zha_device_info[ATTR_MANUFACTURER],
88  model=zha_device_info[ATTR_MODEL],
89  name=zha_device_info[ATTR_NAME],
90  via_device=(DOMAIN, zha_gateway.state.node_info.ieee),
91  )
92 
93  @callback
94  def _handle_entity_events(self, event: Any) -> None:
95  """Entity state changed."""
96  self.debug("Handling event from entity: %s", event)
97  self.async_write_ha_stateasync_write_ha_state()
98 
99  async def async_added_to_hass(self) -> None:
100  """Run when about to be added to hass."""
101  self.remove_futureremove_future = self.hasshass.loop.create_future()
102  self._unsubs.append(
103  self.entity_data.entity.on_all_events(self._handle_entity_events_handle_entity_events)
104  )
105  remove_signal = (
106  f"{SIGNAL_REMOVE_ENTITIES}_group_{self.entity_data.group_proxy.group.group_id}"
107  if self.entity_data.is_group_entity
108  and self.entity_data.group_proxy is not None
109  else f"{SIGNAL_REMOVE_ENTITIES}_{self.entity_data.device_proxy.device.ieee}"
110  )
111  self._unsubs.append(
113  self.hasshass,
114  remove_signal,
115  partial(self.async_removeasync_remove, force_remove=True),
116  )
117  )
118  self.entity_data.device_proxy.gateway_proxy.register_entity_reference(
119  self.entity_identity_id,
120  self.entity_data,
121  self.device_infodevice_infodevice_info,
122  self.remove_futureremove_future,
123  )
124 
125  if (state := await self.async_get_last_stateasync_get_last_state()) is None:
126  return
127 
128  self.restore_external_state_attributesrestore_external_state_attributes(state)
129 
130  @callback
131  def restore_external_state_attributes(self, state: State) -> None:
132  """Restore ephemeral external state from Home Assistant back into ZHA."""
133 
134  # Some operations rely on extra state that is not maintained in the ZCL
135  # attribute cache. Until ZHA is able to maintain its own persistent state (or
136  # provides a more generic hook to utilize HA to do this), we directly restore
137  # them.
138 
139  async def async_will_remove_from_hass(self) -> None:
140  """Disconnect entity object when removed."""
141  for unsub in self._unsubs[:]:
142  unsub()
143  self._unsubs.remove(unsub)
144  await super().async_will_remove_from_hass()
145  self.remove_futureremove_future.set_result(True)
146 
147  @convert_zha_error_to_ha_error
148  async def async_update(self) -> None:
149  """Update the entity."""
150  await self.entity_data.entity.async_update()
151  self.async_write_ha_stateasync_write_ha_state()
152 
153  def log(self, level: int, msg: str, *args, **kwargs):
154  """Log a message."""
155  msg = f"%s: {msg}"
156  args = (self.entity_identity_id, *args)
157  _LOGGER.log(level, msg, *args, **kwargs)
None __init__(self, EntityData entity_data, *args, **kwargs)
Definition: entity.py:35
str|UndefinedType|None name(self)
Definition: entity.py:59
None _handle_entity_events(self, Any event)
Definition: entity.py:94
def log(self, int level, str msg, *args, **kwargs)
Definition: entity.py:153
None restore_external_state_attributes(self, State state)
Definition: entity.py:131
None async_remove(self, *bool force_remove=False)
Definition: entity.py:1387
DeviceInfo|None device_info(self)
Definition: entity.py:798
bool remove(self, _T matcher)
Definition: match.py:214
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103