Home Assistant Unofficial Reference 2024.12.1
entity.py
Go to the documentation of this file.
1 """Shelly entity helper."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Mapping
6 from dataclasses import dataclass
7 from typing import Any, cast
8 
9 from aioshelly.block_device import Block
10 from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
11 
12 from homeassistant.core import HomeAssistant, State, callback
13 from homeassistant.exceptions import HomeAssistantError
14 from homeassistant.helpers import entity_registry as er
15 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
16 from homeassistant.helpers.entity import Entity, EntityDescription
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 from homeassistant.helpers.entity_registry import RegistryEntry
19 from homeassistant.helpers.typing import StateType
20 from homeassistant.helpers.update_coordinator import CoordinatorEntity
21 
22 from .const import CONF_SLEEP_PERIOD, LOGGER
23 from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
24 from .utils import (
25  async_remove_shelly_entity,
26  get_block_entity_name,
27  get_rpc_entity_name,
28  get_rpc_key_instances,
29 )
30 
31 
32 @callback
34  hass: HomeAssistant,
35  config_entry: ShellyConfigEntry,
36  async_add_entities: AddEntitiesCallback,
37  sensors: Mapping[tuple[str, str], BlockEntityDescription],
38  sensor_class: Callable,
39 ) -> None:
40  """Set up entities for attributes."""
41  coordinator = config_entry.runtime_data.block
42  assert coordinator
43  if coordinator.device.initialized:
45  hass, async_add_entities, coordinator, sensors, sensor_class
46  )
47  else:
49  hass,
50  config_entry,
51  async_add_entities,
52  coordinator,
53  sensors,
54  sensor_class,
55  )
56 
57 
58 @callback
60  hass: HomeAssistant,
61  async_add_entities: AddEntitiesCallback,
62  coordinator: ShellyBlockCoordinator,
63  sensors: Mapping[tuple[str, str], BlockEntityDescription],
64  sensor_class: Callable,
65 ) -> None:
66  """Set up entities for block attributes."""
67  entities = []
68 
69  assert coordinator.device.blocks
70 
71  for block in coordinator.device.blocks:
72  for sensor_id in block.sensor_ids:
73  description = sensors.get((cast(str, block.type), sensor_id))
74  if description is None:
75  continue
76 
77  # Filter out non-existing sensors and sensors without a value
78  if getattr(block, sensor_id, None) is None:
79  continue
80 
81  # Filter and remove entities that according to settings
82  # should not create an entity
83  if description.removal_condition and description.removal_condition(
84  coordinator.device.settings, block
85  ):
86  domain = sensor_class.__module__.split(".")[-1]
87  unique_id = f"{coordinator.mac}-{block.description}-{sensor_id}"
88  async_remove_shelly_entity(hass, domain, unique_id)
89  else:
90  entities.append(
91  sensor_class(coordinator, block, sensor_id, description)
92  )
93 
94  if not entities:
95  return
96 
97  async_add_entities(entities)
98 
99 
100 @callback
102  hass: HomeAssistant,
103  config_entry: ShellyConfigEntry,
104  async_add_entities: AddEntitiesCallback,
105  coordinator: ShellyBlockCoordinator,
106  sensors: Mapping[tuple[str, str], BlockEntityDescription],
107  sensor_class: Callable,
108 ) -> None:
109  """Restore block attributes entities."""
110  entities = []
111 
112  ent_reg = er.async_get(hass)
113  entries = er.async_entries_for_config_entry(ent_reg, config_entry.entry_id)
114 
115  domain = sensor_class.__module__.split(".")[-1]
116 
117  for entry in entries:
118  if entry.domain != domain:
119  continue
120 
121  attribute = entry.unique_id.split("-")[-1]
122  block_type = entry.unique_id.split("-")[-2].split("_")[0]
123 
124  if description := sensors.get((block_type, attribute)):
125  entities.append(
126  sensor_class(coordinator, None, attribute, description, entry)
127  )
128 
129  if not entities:
130  return
131 
132  async_add_entities(entities)
133 
134 
135 @callback
137  hass: HomeAssistant,
138  config_entry: ShellyConfigEntry,
139  async_add_entities: AddEntitiesCallback,
140  sensors: Mapping[str, RpcEntityDescription],
141  sensor_class: Callable,
142 ) -> None:
143  """Set up entities for RPC sensors."""
144  coordinator = config_entry.runtime_data.rpc
145  assert coordinator
146 
147  if coordinator.device.initialized:
149  hass, config_entry, async_add_entities, sensors, sensor_class
150  )
151  else:
153  hass, config_entry, async_add_entities, coordinator, sensors, sensor_class
154  )
155 
156 
157 @callback
159  hass: HomeAssistant,
160  config_entry: ShellyConfigEntry,
161  async_add_entities: AddEntitiesCallback,
162  sensors: Mapping[str, RpcEntityDescription],
163  sensor_class: Callable,
164 ) -> None:
165  """Set up entities for RPC attributes."""
166  coordinator = config_entry.runtime_data.rpc
167  assert coordinator
168 
169  polling_coordinator = None
170  if not (sleep_period := config_entry.data[CONF_SLEEP_PERIOD]):
171  polling_coordinator = config_entry.runtime_data.rpc_poll
172  assert polling_coordinator
173 
174  entities = []
175  for sensor_id in sensors:
176  description = sensors[sensor_id]
177  key_instances = get_rpc_key_instances(
178  coordinator.device.status, description.key
179  )
180 
181  for key in key_instances:
182  # Filter non-existing sensors
183  if description.sub_key not in coordinator.device.status[
184  key
185  ] and not description.supported(coordinator.device.status[key]):
186  continue
187 
188  # Filter and remove entities that according to settings/status
189  # should not create an entity
190  if description.removal_condition and description.removal_condition(
191  coordinator.device.config, coordinator.device.status, key
192  ):
193  domain = sensor_class.__module__.split(".")[-1]
194  unique_id = f"{coordinator.mac}-{key}-{sensor_id}"
195  async_remove_shelly_entity(hass, domain, unique_id)
196  elif description.use_polling_coordinator:
197  if not sleep_period:
198  entities.append(
199  sensor_class(polling_coordinator, key, sensor_id, description)
200  )
201  else:
202  entities.append(sensor_class(coordinator, key, sensor_id, description))
203  if not entities:
204  return
205 
206  async_add_entities(entities)
207 
208 
209 @callback
211  hass: HomeAssistant,
212  config_entry: ShellyConfigEntry,
213  async_add_entities: AddEntitiesCallback,
214  coordinator: ShellyRpcCoordinator,
215  sensors: Mapping[str, RpcEntityDescription],
216  sensor_class: Callable,
217 ) -> None:
218  """Restore block attributes entities."""
219  entities = []
220 
221  ent_reg = er.async_get(hass)
222  entries = er.async_entries_for_config_entry(ent_reg, config_entry.entry_id)
223 
224  domain = sensor_class.__module__.split(".")[-1]
225 
226  for entry in entries:
227  if entry.domain != domain:
228  continue
229 
230  key = entry.unique_id.split("-")[-2]
231  attribute = entry.unique_id.split("-")[-1]
232 
233  if description := sensors.get(attribute):
234  entities.append(
235  sensor_class(coordinator, key, attribute, description, entry)
236  )
237 
238  if not entities:
239  return
240 
241  async_add_entities(entities)
242 
243 
244 @callback
246  hass: HomeAssistant,
247  config_entry: ShellyConfigEntry,
248  async_add_entities: AddEntitiesCallback,
249  sensors: Mapping[str, RestEntityDescription],
250  sensor_class: Callable,
251 ) -> None:
252  """Set up entities for REST sensors."""
253  coordinator = config_entry.runtime_data.rest
254  assert coordinator
255 
257  sensor_class(coordinator, sensor_id, sensors[sensor_id])
258  for sensor_id in sensors
259  )
260 
261 
262 @dataclass(frozen=True)
264  """Class to describe a BLOCK entity."""
265 
266  # BlockEntity does not support UNDEFINED or None,
267  # restrict the type to str.
268  name: str = ""
269 
270  unit_fn: Callable[[dict], str] | None = None
271  value: Callable[[Any], Any] = lambda val: val
272  available: Callable[[Block], bool] | None = None
273  # Callable (settings, block), return true if entity should be removed
274  removal_condition: Callable[[dict, Block], bool] | None = None
275  extra_state_attributes: Callable[[Block], dict | None] | None = None
276 
277 
278 @dataclass(frozen=True, kw_only=True)
280  """Class to describe a RPC entity."""
281 
282  # BlockEntity does not support UNDEFINED or None,
283  # restrict the type to str.
284  name: str = ""
285 
286  sub_key: str
287 
288  value: Callable[[Any, Any], Any] | None = None
289  available: Callable[[dict], bool] | None = None
290  removal_condition: Callable[[dict, dict, str], bool] | None = None
291  extra_state_attributes: Callable[[dict, dict], dict | None] | None = None
292  use_polling_coordinator: bool = False
293  supported: Callable = lambda _: False
294  unit: Callable[[dict], str | None] | None = None
295  options_fn: Callable[[dict], list[str]] | None = None
296 
297 
298 @dataclass(frozen=True)
300  """Class to describe a REST entity."""
301 
302  # BlockEntity does not support UNDEFINED or None,
303  # restrict the type to str.
304  name: str = ""
305 
306  value: Callable[[dict, Any], Any] | None = None
307  extra_state_attributes: Callable[[dict], dict | None] | None = None
308 
309 
310 class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
311  """Helper class to represent a block entity."""
312 
313  def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None:
314  """Initialize Shelly entity."""
315  super().__init__(coordinator)
316  self.blockblock = block
317  self._attr_name_attr_name = get_block_entity_name(coordinator.device, block)
318  self._attr_device_info_attr_device_info = DeviceInfo(
319  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
320  )
321  self._attr_unique_id_attr_unique_id = f"{coordinator.mac}-{block.description}"
322 
323  # pylint: disable-next=hass-missing-super-call
324  async def async_added_to_hass(self) -> None:
325  """When entity is added to HASS."""
326  self.async_on_remove(self.coordinator.async_add_listener(self._update_callback_update_callback))
327 
328  @callback
329  def _update_callback(self) -> None:
330  """Handle device update."""
331  self.async_write_ha_state()
332 
333  async def set_state(self, **kwargs: Any) -> Any:
334  """Set block state (HTTP request)."""
335  LOGGER.debug("Setting state for entity %s, state: %s", self.name, kwargs)
336  try:
337  return await self.blockblock.set_state(**kwargs)
338  except DeviceConnectionError as err:
339  self.coordinator.last_update_success = False
340  raise HomeAssistantError(
341  f"Setting state for entity {self.name} failed, state: {kwargs}, error:"
342  f" {err!r}"
343  ) from err
344  except InvalidAuthError:
345  await self.coordinator.async_shutdown_device_and_start_reauth()
346 
347 
348 class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]):
349  """Helper class to represent a rpc entity."""
350 
351  def __init__(self, coordinator: ShellyRpcCoordinator, key: str) -> None:
352  """Initialize Shelly entity."""
353  super().__init__(coordinator)
354  self.keykey = key
355  self._attr_device_info_attr_device_info = {
356  "connections": {(CONNECTION_NETWORK_MAC, coordinator.mac)}
357  }
358  self._attr_unique_id_attr_unique_id = f"{coordinator.mac}-{key}"
359  self._attr_name_attr_name = get_rpc_entity_name(coordinator.device, key)
360 
361  @property
362  def available(self) -> bool:
363  """Check if device is available and initialized or sleepy."""
364  coordinator = self.coordinator
365  return super().available and (
366  coordinator.device.initialized or bool(coordinator.sleep_period)
367  )
368 
369  @property
370  def status(self) -> dict:
371  """Device status by entity key."""
372  return cast(dict, self.coordinator.device.status[self.keykey])
373 
374  # pylint: disable-next=hass-missing-super-call
375  async def async_added_to_hass(self) -> None:
376  """When entity is added to HASS."""
377  self.async_on_remove(self.coordinator.async_add_listener(self._update_callback_update_callback))
378 
379  @callback
380  def _update_callback(self) -> None:
381  """Handle device update."""
382  self.async_write_ha_state()
383 
384  async def call_rpc(self, method: str, params: Any) -> Any:
385  """Call RPC method."""
386  LOGGER.debug(
387  "Call RPC for entity %s, method: %s, params: %s",
388  self.name,
389  method,
390  params,
391  )
392  try:
393  return await self.coordinator.device.call_rpc(method, params)
394  except DeviceConnectionError as err:
395  self.coordinator.last_update_success = False
396  raise HomeAssistantError(
397  f"Call RPC for {self.name} connection error, method: {method}, params:"
398  f" {params}, error: {err!r}"
399  ) from err
400  except RpcCallError as err:
401  raise HomeAssistantError(
402  f"Call RPC for {self.name} request error, method: {method}, params:"
403  f" {params}, error: {err!r}"
404  ) from err
405  except InvalidAuthError:
406  await self.coordinator.async_shutdown_device_and_start_reauth()
407 
408 
410  """Helper class to represent a block attribute."""
411 
412  entity_description: BlockEntityDescription
413 
414  def __init__(
415  self,
416  coordinator: ShellyBlockCoordinator,
417  block: Block,
418  attribute: str,
419  description: BlockEntityDescription,
420  ) -> None:
421  """Initialize sensor."""
422  super().__init__(coordinator, block)
423  self.attributeattribute = attribute
424  self.entity_descriptionentity_description = description
425 
426  self._attr_unique_id_attr_unique_id: str = f"{super().unique_id}-{self.attribute}"
428  coordinator.device, block, description.name
429  )
430 
431  @property
432  def attribute_value(self) -> StateType:
433  """Value of sensor."""
434  if (value := getattr(self.blockblock, self.attributeattribute)) is None:
435  return None
436 
437  return cast(StateType, self.entity_descriptionentity_description.value(value))
438 
439  @property
440  def available(self) -> bool:
441  """Available."""
442  available = super().available
443 
444  if not available or not self.entity_descriptionentity_description.available or self.blockblock is None:
445  return available
446 
447  return self.entity_descriptionentity_description.available(self.blockblock)
448 
449  @property
450  def extra_state_attributes(self) -> dict[str, Any] | None:
451  """Return the state attributes."""
452  if self.entity_descriptionentity_description.extra_state_attributes is None:
453  return None
454 
455  return self.entity_descriptionentity_description.extra_state_attributes(self.blockblock)
456 
457 
458 class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]):
459  """Class to load info from REST."""
460 
461  entity_description: RestEntityDescription
462 
463  def __init__(
464  self,
465  coordinator: ShellyBlockCoordinator,
466  attribute: str,
467  description: RestEntityDescription,
468  ) -> None:
469  """Initialize sensor."""
470  super().__init__(coordinator)
471  self.block_coordinatorblock_coordinator = coordinator
472  self.attributeattribute = attribute
473  self.entity_descriptionentity_description = description
475  coordinator.device, None, description.name
476  )
477  self._attr_unique_id_attr_unique_id = f"{coordinator.mac}-{attribute}"
478  self._attr_device_info_attr_device_info = DeviceInfo(
479  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
480  )
481  self._last_value_last_value = None
482 
483  @property
484  def available(self) -> bool:
485  """Available."""
486  return self.block_coordinatorblock_coordinator.last_update_success
487 
488  @property
489  def attribute_value(self) -> StateType:
490  """Value of sensor."""
491  if self.entity_descriptionentity_description.value is not None:
492  self._last_value_last_value = self.entity_descriptionentity_description.value(
493  self.block_coordinatorblock_coordinator.device.status, self._last_value_last_value
494  )
495  return self._last_value_last_value
496 
497 
499  """Helper class to represent a rpc attribute."""
500 
501  entity_description: RpcEntityDescription
502 
503  def __init__(
504  self,
505  coordinator: ShellyRpcCoordinator,
506  key: str,
507  attribute: str,
508  description: RpcEntityDescription,
509  ) -> None:
510  """Initialize sensor."""
511  super().__init__(coordinator, key)
512  self.attributeattribute = attribute
513  self.entity_descriptionentity_description = description
514 
515  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{super().unique_id}-{attribute}"
516  self._attr_name_attr_name_attr_name = get_rpc_entity_name(coordinator.device, key, description.name)
517  self._last_value_last_value = None
518  id_key = key.split(":")[-1]
519  self._id_id = int(id_key) if id_key.isnumeric() else None
520 
521  if description.unit is not None:
522  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = description.unit(
523  coordinator.device.config[key]
524  )
525 
526  self.option_mapoption_map: dict[str, str] = {}
527  self.reversed_option_mapreversed_option_map: dict[str, str] = {}
528  if "enum" in key:
529  titles = self.coordinator.device.config[key]["meta"]["ui"]["titles"]
530  options = self.coordinator.device.config[key]["options"]
531  self.option_mapoption_map = {
532  opt: (titles[opt] if titles.get(opt) is not None else opt)
533  for opt in options
534  }
535  self.reversed_option_mapreversed_option_map = {
536  tit: opt for opt, tit in self.option_mapoption_map.items()
537  }
538 
539  @property
540  def sub_status(self) -> Any:
541  """Device status by entity key."""
542  return self.statusstatus[self.entity_descriptionentity_description.sub_key]
543 
544  @property
545  def attribute_value(self) -> StateType:
546  """Value of sensor."""
547  if self.entity_descriptionentity_description.value is not None:
548  # using "get" here since subkey might not exist (e.g. "errors" sub_key)
549  self._last_value_last_value = self.entity_descriptionentity_description.value(
550  self.statusstatus.get(self.entity_descriptionentity_description.sub_key), self._last_value_last_value
551  )
552  else:
553  self._last_value_last_value = self.sub_statussub_status
554 
555  return self._last_value_last_value
556 
557  @property
558  def available(self) -> bool:
559  """Available."""
560  available = super().available
561 
562  if not available or not self.entity_descriptionentity_description.available:
563  return available
564 
565  return self.entity_descriptionentity_description.available(self.sub_statussub_status)
566 
567 
569  """Represent a shelly sleeping block attribute entity."""
570 
571  # pylint: disable-next=super-init-not-called
572  def __init__(
573  self,
574  coordinator: ShellyBlockCoordinator,
575  block: Block | None,
576  attribute: str,
577  description: BlockEntityDescription,
578  entry: RegistryEntry | None = None,
579  ) -> None:
580  """Initialize the sleeping sensor."""
581  self.last_state: State | None = None
582  self.coordinatorcoordinator = coordinator
583  self.attributeattributeattribute = attribute
584  self.blockblockblock: Block | None = block # type: ignore[assignment]
585  self.entity_descriptionentity_descriptionentity_description = description
586 
588  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
589  )
590 
591  if block is not None:
592  self._attr_unique_id_attr_unique_id_attr_unique_id = (
593  f"{self.coordinator.mac}-{block.description}-{attribute}"
594  )
596  self.coordinatorcoordinator.device, block, self.entity_descriptionentity_descriptionentity_description.name
597  )
598  elif entry is not None:
599  self._attr_unique_id_attr_unique_id_attr_unique_id = entry.unique_id
600  self._attr_name_attr_name_attr_name_attr_name = cast(str, entry.original_name)
601 
602  @callback
603  def _update_callback(self) -> None:
604  """Handle device update."""
605  if self.blockblockblock is not None or not self.coordinatorcoordinator.device.initialized:
606  super()._update_callback()
607  return
608 
609  _, entity_block, entity_sensor = self._attr_unique_id_attr_unique_id_attr_unique_id.split("-")
610 
611  assert self.coordinatorcoordinator.device.blocks
612 
613  for block in self.coordinatorcoordinator.device.blocks:
614  if block.description != entity_block:
615  continue
616 
617  for sensor_id in block.sensor_ids:
618  if sensor_id != entity_sensor:
619  continue
620 
621  self.blockblockblock = block
622  LOGGER.debug("Entity %s attached to block", self.namename)
623  super()._update_callback()
624  return
625 
626  async def async_update(self) -> None:
627  """Update the entity."""
628  LOGGER.info(
629  "Entity %s comes from a sleeping device, update is not possible",
630  self.entity_identity_id,
631  )
632 
633 
635  """Helper class to represent a sleeping rpc attribute."""
636 
637  entity_description: RpcEntityDescription
638 
639  # pylint: disable-next=super-init-not-called
640  def __init__(
641  self,
642  coordinator: ShellyRpcCoordinator,
643  key: str,
644  attribute: str,
645  description: RpcEntityDescription,
646  entry: RegistryEntry | None = None,
647  ) -> None:
648  """Initialize the sleeping sensor."""
649  self.last_state: State | None = None
650  self.coordinatorcoordinator = coordinator
651  self.keykeykey = key
652  self.attributeattributeattribute = attribute
653  self.entity_descriptionentity_descriptionentity_description = description
654 
656  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
657  )
659  f"{coordinator.mac}-{key}-{attribute}"
660  )
661  self._last_value_last_value_last_value = None
662 
663  if coordinator.device.initialized:
665  coordinator.device, key, description.name
666  )
667  elif entry is not None:
668  self._attr_name_attr_name_attr_name_attr_name = cast(str, entry.original_name)
669 
670  async def async_update(self) -> None:
671  """Update the entity."""
672  LOGGER.info(
673  "Entity %s comes from a sleeping device, update is not possible",
674  self.entity_identity_id,
675  )
None __init__(self, ShellyBlockCoordinator coordinator, Block block, str attribute, BlockEntityDescription description)
Definition: entity.py:420
None __init__(self, ShellyBlockCoordinator coordinator, Block block)
Definition: entity.py:313
None __init__(self, ShellyBlockCoordinator coordinator, str attribute, RestEntityDescription description)
Definition: entity.py:468
None __init__(self, ShellyRpcCoordinator coordinator, str key, str attribute, RpcEntityDescription description)
Definition: entity.py:509
None __init__(self, ShellyRpcCoordinator coordinator, str key)
Definition: entity.py:351
Any call_rpc(self, str method, Any params)
Definition: entity.py:384
None __init__(self, ShellyBlockCoordinator coordinator, Block|None block, str attribute, BlockEntityDescription description, RegistryEntry|None entry=None)
Definition: entity.py:579
None __init__(self, ShellyRpcCoordinator coordinator, str key, str attribute, RpcEntityDescription description, RegistryEntry|None entry=None)
Definition: entity.py:647
str|UndefinedType|None name(self)
Definition: entity.py:738
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_add_listener(HomeAssistant hass, Callable[[], None] listener)
Definition: __init__.py:82
None async_restore_block_attribute_entities(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, ShellyBlockCoordinator coordinator, Mapping[tuple[str, str], BlockEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:108
None async_setup_rpc_attribute_entities(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[str, RpcEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:164
None async_setup_entry_rpc(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[str, RpcEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:142
None async_setup_entry_rest(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[str, RestEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:251
None async_setup_block_attribute_entities(HomeAssistant hass, AddEntitiesCallback async_add_entities, ShellyBlockCoordinator coordinator, Mapping[tuple[str, str], BlockEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:65
None async_restore_rpc_attribute_entities(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, ShellyRpcCoordinator coordinator, Mapping[str, RpcEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:217
None async_setup_entry_attribute_entities(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities, Mapping[tuple[str, str], BlockEntityDescription] sensors, Callable sensor_class)
Definition: entity.py:39
list[str] get_rpc_key_instances(dict[str, Any] keys_dict, str key)
Definition: utils.py:358
str get_block_entity_name(BlockDevice device, Block|None block, str|None description=None)
Definition: utils.py:104
None async_remove_shelly_entity(HomeAssistant hass, str domain, str unique_id)
Definition: utils.py:67
str get_rpc_entity_name(RpcDevice device, str key, str|None description=None)
Definition: utils.py:343