Home Assistant Unofficial Reference 2024.12.1
entity.py
Go to the documentation of this file.
1 """Base class for deCONZ devices."""
2 
3 from __future__ import annotations
4 
5 from pydeconz.models.deconz_device import DeconzDevice as PydeconzDevice
6 from pydeconz.models.group import Group as PydeconzGroup
7 from pydeconz.models.light import LightBase as PydeconzLightBase
8 from pydeconz.models.scene import Scene as PydeconzScene
9 from pydeconz.models.sensor import SensorBase as PydeconzSensorBase
10 
11 from homeassistant.core import callback
12 from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo
13 from homeassistant.helpers.dispatcher import async_dispatcher_connect
14 from homeassistant.helpers.entity import Entity
15 
16 from .const import DOMAIN as DECONZ_DOMAIN
17 from .hub import DeconzHub
18 from .util import serial_from_unique_id
19 
20 type _DeviceType = (
21  PydeconzGroup | PydeconzLightBase | PydeconzSensorBase | PydeconzScene
22 )
23 
24 
25 class DeconzBase[_DeviceT: _DeviceType]:
26  """Common base for deconz entities and events."""
27 
28  unique_id_suffix: str | None = None
29 
30  def __init__(
31  self,
32  device: _DeviceT,
33  hub: DeconzHub,
34  ) -> None:
35  """Set up device and add update callback to get data from websocket."""
36  self._device: _DeviceT = device
37  self.hub = hub
38 
39  @property
40  def unique_id(self) -> str:
41  """Return a unique identifier for this device."""
42  assert isinstance(self._device, PydeconzDevice)
43  if self.unique_id_suffix is not None:
44  return f"{self._device.unique_id}-{self.unique_id_suffix}"
45  return self._device.unique_id
46 
47  @property
48  def serial(self) -> str | None:
49  """Return a serial number for this device."""
50  assert isinstance(self._device, PydeconzDevice)
51  return serial_from_unique_id(self._device.unique_id)
52 
53  @property
54  def device_info(self) -> DeviceInfo | None:
55  """Return a device description for device registry."""
56  assert isinstance(self._device, PydeconzDevice)
57  if self.serial is None:
58  return None
59 
60  return DeviceInfo(
61  connections={(CONNECTION_ZIGBEE, self.serial)},
62  identifiers={(DECONZ_DOMAIN, self.serial)},
63  manufacturer=self._device.manufacturer,
64  model=self._device.model_id,
65  name=self._device.name,
66  sw_version=self._device.software_version,
67  via_device=(DECONZ_DOMAIN, self.hub.api.config.bridge_id),
68  )
69 
70 
71 class DeconzDevice[_DeviceT: _DeviceType](DeconzBase[_DeviceT], Entity):
72  """Representation of a deCONZ device."""
73 
74  _attr_should_poll = False
75 
76  _name_suffix: str | None = None
77  _update_key: str | None = None
78  _update_keys: set[str] | None = None
79 
80  TYPE = ""
81 
82  def __init__(
83  self,
84  device: _DeviceT,
85  hub: DeconzHub,
86  ) -> None:
87  """Set up device and add update callback to get data from websocket."""
88  super().__init__(device, hub)
89  self.hub.entities[self.TYPE].add(self.unique_id)
90 
91  self._attr_name = self._device.name
92  if self._name_suffix is not None:
93  self._attr_name += f" {self._name_suffix}"
94 
95  if self._update_key is not None:
96  self._update_keys = {self._update_key}
97  if self._update_keys is not None:
98  self._update_keys |= {"reachable"}
99 
100  async def async_added_to_hass(self) -> None:
101  """Subscribe to device events."""
102  self._device.register_callback(self.async_update_callback)
103  self.hub.deconz_ids[self.entity_id] = self._device.deconz_id
104  self.async_on_remove(
106  self.hass,
107  self.hub.signal_reachable,
108  self.async_update_connection_state,
109  )
110  )
111 
112  async def async_will_remove_from_hass(self) -> None:
113  """Disconnect device object when removed."""
114  self._device.remove_callback(self.async_update_callback)
115  del self.hub.deconz_ids[self.entity_id]
116  self.hub.entities[self.TYPE].remove(self.unique_id)
117 
118  @callback
119  def async_update_connection_state(self) -> None:
120  """Update the device's available state."""
121  self.async_write_ha_state()
122 
123  @callback
124  def async_update_callback(self) -> None:
125  """Update the device's state."""
126  if self.hub.ignore_state_updates:
127  return
128 
129  if self._update_keys is not None and not self._device.changed_keys.intersection(
130  self._update_keys
131  ):
132  return
133 
134  self.async_write_ha_state()
135 
136  @property
137  def available(self) -> bool:
138  """Return True if device is available."""
139  if isinstance(self._device, PydeconzScene):
140  return self.hub.available
141  return self.hub.available and self._device.reachable
142 
143 
144 class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
145  """Representation of a deCONZ scene."""
146 
147  _attr_has_entity_name = True
148 
149  def __init__(
150  self,
151  device: PydeconzScene,
152  hub: DeconzHub,
153  ) -> None:
154  """Set up a scene."""
155  super().__init__(device, hub)
156 
157  self.groupgroup = self.hub.api.groups[device.group_id]
158 
159  self._attr_name_attr_name = device.name
160  self._group_identifier_group_identifier = self.get_parent_identifierget_parent_identifier()
161 
162  def get_device_identifier(self) -> str:
163  """Describe a unique identifier for this scene."""
164  return f"{self.hub.bridgeid}{self._device.deconz_id}"
165 
166  def get_parent_identifier(self) -> str:
167  """Describe a unique identifier for group this scene belongs to."""
168  return f"{self.hub.bridgeid}-{self.group.deconz_id}"
169 
170  @property
171  def unique_id(self) -> str:
172  """Return a unique identifier for this scene."""
173  return self.get_device_identifierget_device_identifier()
174 
175  @property
176  def device_info(self) -> DeviceInfo:
177  """Return a device description for device registry."""
178  return DeviceInfo(
179  identifiers={(DECONZ_DOMAIN, self._group_identifier_group_identifier)},
180  manufacturer="Dresden Elektronik",
181  model="deCONZ group",
182  name=self.groupgroup.name,
183  via_device=(DECONZ_DOMAIN, self.hub.api.config.bridge_id),
184  )
None __init__(self, PydeconzScene device, DeconzHub hub)
Definition: entity.py:153
bool add(self, _T matcher)
Definition: match.py:185
bool remove(self, _T matcher)
Definition: match.py:214
DeviceInfo|None device_info(self)
Definition: entity.py:54
None __init__(self, _DeviceT device, DeconzHub hub)
Definition: entity.py:34
str|None serial_from_unique_id(str|None unique_id)
Definition: util.py:6
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103