Home Assistant Unofficial Reference 2024.12.1
entity.py
Go to the documentation of this file.
1 """Tasmota entity mixins."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from hatasmota.entity import (
9  TasmotaAvailability as HATasmotaAvailability,
10  TasmotaEntity as HATasmotaEntity,
11  TasmotaEntityConfig,
12 )
13 from hatasmota.models import DiscoveryHashType
14 
16  async_subscribe_connection_status,
17  is_connected as mqtt_connected,
18 )
19 from homeassistant.core import callback
20 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
21 from homeassistant.helpers.dispatcher import async_dispatcher_connect
22 from homeassistant.helpers.entity import Entity
23 
24 from .discovery import (
25  TASMOTA_DISCOVERY_ENTITY_UPDATED,
26  clear_discovery_hash,
27  set_discovery_hash,
28 )
29 
30 _LOGGER = logging.getLogger(__name__)
31 
32 
34  """Base class for Tasmota entities."""
35 
36  _attr_has_entity_name = True
37 
38  def __init__(self, tasmota_entity: HATasmotaEntity) -> None:
39  """Initialize."""
40  self._tasmota_entity_tasmota_entity = tasmota_entity
41  self._unique_id_unique_id = tasmota_entity.unique_id
42  self._attr_device_info_attr_device_info = DeviceInfo(
43  connections={(CONNECTION_NETWORK_MAC, tasmota_entity.mac)}
44  )
45 
46  async def async_added_to_hass(self) -> None:
47  """Subscribe to MQTT events."""
48  await self._subscribe_topics_subscribe_topics()
49 
50  async def async_will_remove_from_hass(self) -> None:
51  """Unsubscribe when removed."""
52  await self._tasmota_entity_tasmota_entity.unsubscribe_topics()
53  await super().async_will_remove_from_hass()
54 
55  async def discovery_update(
56  self, update: TasmotaEntityConfig, write_state: bool = True
57  ) -> None:
58  """Handle updated discovery message."""
59  self._tasmota_entity_tasmota_entity.config_update(update)
60  await self._subscribe_topics_subscribe_topics()
61  if write_state:
62  self.async_write_ha_stateasync_write_ha_state()
63 
64  async def _subscribe_topics(self) -> None:
65  """(Re)Subscribe to topics."""
66  await self._tasmota_entity_tasmota_entity.subscribe_topics()
67 
68  @property
69  def name(self) -> str | None:
70  """Return the name of the binary sensor."""
71  return self._tasmota_entity_tasmota_entity.name
72 
73  @property
74  def should_poll(self) -> bool:
75  """Return the polling state."""
76  return False
77 
78  @property
79  def unique_id(self) -> str:
80  """Return a unique ID."""
81  return self._unique_id_unique_id
82 
83 
85  """Base class for Tasmota entities which can be on or off."""
86 
87  def __init__(self, **kwds: Any) -> None:
88  """Initialize."""
89  self._on_off_state_on_off_state: bool = False
90  super().__init__(**kwds)
91 
92  async def async_added_to_hass(self) -> None:
93  """Subscribe to MQTT events."""
94  self._tasmota_entity_tasmota_entity.set_on_state_callback(self.state_updatedstate_updated)
95  await super().async_added_to_hass()
96 
97  @callback
98  def state_updated(self, state: bool, **kwargs: Any) -> None:
99  """Handle state updates."""
100  self._on_off_state_on_off_state = state
101  self.async_write_ha_stateasync_write_ha_state()
102 
103  @property
104  def is_on(self) -> bool:
105  """Return true if device is on."""
106  return self._on_off_state_on_off_state
107 
108 
110  """Mixin used for platforms that report availability."""
111 
112  _tasmota_entity: HATasmotaAvailability
113 
114  def __init__(self, **kwds: Any) -> None:
115  """Initialize the availability mixin."""
116  super().__init__(**kwds)
117  if self._tasmota_entity_tasmota_entity.deep_sleep_enabled:
118  self._available_available = True
119  else:
120  self._available_available = False
121 
122  async def async_added_to_hass(self) -> None:
123  """Subscribe to MQTT events."""
124  self._tasmota_entity_tasmota_entity.set_on_availability_callback(self.availability_updatedavailability_updated)
125  self.async_on_removeasync_on_remove(
126  async_subscribe_connection_status(self.hasshass, self.async_mqtt_connectedasync_mqtt_connected)
127  )
128  await super().async_added_to_hass()
129  if self._tasmota_entity_tasmota_entity.deep_sleep_enabled:
130  await self._tasmota_entity_tasmota_entity.poll_status()
131 
132  async def availability_updated(self, available: bool) -> None:
133  """Handle updated availability."""
134  await self._tasmota_entity_tasmota_entity.poll_status()
135  self._available_available = available
136  self.async_write_ha_stateasync_write_ha_state()
137 
138  @callback
139  def async_mqtt_connected(self, _: bool) -> None:
140  """Update state on connection/disconnection to MQTT broker."""
141  if not self.hasshass.is_stopping:
142  if not mqtt_connected(self.hasshass):
143  self._available_available = False
144  elif self._tasmota_entity_tasmota_entity.deep_sleep_enabled:
145  self._available_available = True
146  self.async_write_ha_stateasync_write_ha_state()
147 
148  @property
149  def available(self) -> bool:
150  """Return if the device is available."""
151  return self._available_available
152 
153 
155  """Mixin used to handle updated discovery message."""
156 
157  def __init__(self, discovery_hash: DiscoveryHashType, **kwds: Any) -> None:
158  """Initialize the discovery update mixin."""
159  self._discovery_hash_discovery_hash = discovery_hash
160  self._removed_from_hass_removed_from_hass = False
161  super().__init__(**kwds)
162 
163  async def async_added_to_hass(self) -> None:
164  """Subscribe to discovery updates."""
165  self._removed_from_hass_removed_from_hass = False
166  await super().async_added_to_hass()
167 
168  @callback
169  def discovery_callback(config: TasmotaEntityConfig) -> None:
170  """Handle discovery update.
171 
172  If the config has changed we will create a task to
173  do the discovery update.
174 
175  As this callback can fire when nothing has changed, this
176  is a normal function to avoid task creation until it is needed.
177  """
178  _LOGGER.debug(
179  "Got update for entity with hash: %s '%s'",
180  self._discovery_hash_discovery_hash,
181  config,
182  )
183  if not self._tasmota_entity_tasmota_entity.config_same(config):
184  # Changed payload: Notify component
185  _LOGGER.debug("Updating component: %s", self.entity_identity_id)
186  self.hasshass.async_create_task(self.discovery_updatediscovery_update(config))
187  else:
188  # Unchanged payload: Ignore to avoid changing states
189  _LOGGER.debug("Ignoring unchanged update for: %s", self.entity_identity_id)
190 
191  # Set in case the entity has been removed and is re-added,
192  # for example when changing entity_id
193  set_discovery_hash(self.hasshass, self._discovery_hash_discovery_hash)
194  self.async_on_removeasync_on_remove(
196  self.hasshass,
197  TASMOTA_DISCOVERY_ENTITY_UPDATED.format(*self._discovery_hash_discovery_hash),
198  discovery_callback,
199  )
200  )
201 
202  @callback
203  def add_to_platform_abort(self) -> None:
204  """Abort adding an entity to a platform."""
205  clear_discovery_hash(self.hasshass, self._discovery_hash_discovery_hash)
206  super().add_to_platform_abort()
207 
208  async def async_will_remove_from_hass(self) -> None:
209  """Stop listening to signal and cleanup discovery data.."""
210  if not self._removed_from_hass_removed_from_hass:
211  clear_discovery_hash(self.hasshass, self._discovery_hash_discovery_hash)
212  self._removed_from_hass_removed_from_hass = True
213  await super().async_will_remove_from_hass()
None __init__(self, DiscoveryHashType discovery_hash, **Any kwds)
Definition: entity.py:157
None discovery_update(self, TasmotaEntityConfig update, bool write_state=True)
Definition: entity.py:57
None __init__(self, HATasmotaEntity tasmota_entity)
Definition: entity.py:38
None state_updated(self, bool state, **Any kwargs)
Definition: entity.py:98
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None clear_discovery_hash(HomeAssistant hass, tuple[str, str] discovery_hash)
Definition: discovery.py:117
None set_discovery_hash(HomeAssistant hass, tuple[str, str] discovery_hash)
Definition: discovery.py:122
Callable[[], None] async_subscribe_connection_status(HomeAssistant hass, ConnectionStatusCallback connection_status_callback)
Definition: __init__.py:546
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103