Home Assistant Unofficial Reference 2024.12.1
button.py
Go to the documentation of this file.
1 """Button for Shelly."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Coroutine
6 from dataclasses import dataclass
7 from functools import partial
8 from typing import TYPE_CHECKING, Any, Final
9 
10 from aioshelly.const import RPC_GENERATIONS
11 
13  ButtonDeviceClass,
14  ButtonEntity,
15  ButtonEntityDescription,
16 )
17 from homeassistant.const import EntityCategory
18 from homeassistant.core import HomeAssistant, callback
19 from homeassistant.helpers import entity_registry as er
20 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 from homeassistant.helpers.update_coordinator import CoordinatorEntity
23 from homeassistant.util import slugify
24 
25 from .const import LOGGER, SHELLY_GAS_MODELS
26 from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
27 from .utils import get_device_entry_gen
28 
29 
30 @dataclass(frozen=True, kw_only=True)
32  _ShellyCoordinatorT: ShellyBlockCoordinator | ShellyRpcCoordinator
33 ](ButtonEntityDescription):
34  """Class to describe a Button entity."""
35 
36  press_action: Callable[[_ShellyCoordinatorT], Coroutine[Any, Any, None]]
37 
38  supported: Callable[[_ShellyCoordinatorT], bool] = lambda _: True
39 
40 
41 BUTTONS: Final[list[ShellyButtonDescription[Any]]] = [
42  ShellyButtonDescription[ShellyBlockCoordinator | ShellyRpcCoordinator](
43  key="reboot",
44  name="Reboot",
45  device_class=ButtonDeviceClass.RESTART,
46  entity_category=EntityCategory.CONFIG,
47  press_action=lambda coordinator: coordinator.device.trigger_reboot(),
48  ),
49  ShellyButtonDescription[ShellyBlockCoordinator](
50  key="self_test",
51  name="Self test",
52  translation_key="self_test",
53  entity_category=EntityCategory.DIAGNOSTIC,
54  press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_self_test(),
55  supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS,
56  ),
57  ShellyButtonDescription[ShellyBlockCoordinator](
58  key="mute",
59  name="Mute",
60  translation_key="mute",
61  entity_category=EntityCategory.CONFIG,
62  press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_mute(),
63  supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS,
64  ),
65  ShellyButtonDescription[ShellyBlockCoordinator](
66  key="unmute",
67  name="Unmute",
68  translation_key="unmute",
69  entity_category=EntityCategory.CONFIG,
70  press_action=lambda coordinator: coordinator.device.trigger_shelly_gas_unmute(),
71  supported=lambda coordinator: coordinator.device.model in SHELLY_GAS_MODELS,
72  ),
73 ]
74 
75 
76 @callback
78  coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator,
79  entity_entry: er.RegistryEntry,
80 ) -> dict[str, Any] | None:
81  """Migrate button unique IDs."""
82  if not entity_entry.entity_id.startswith("button"):
83  return None
84 
85  device_name = slugify(coordinator.device.name)
86 
87  for key in ("reboot", "self_test", "mute", "unmute"):
88  old_unique_id = f"{device_name}_{key}"
89  if entity_entry.unique_id == old_unique_id:
90  new_unique_id = f"{coordinator.mac}_{key}"
91  LOGGER.debug(
92  "Migrating unique_id for %s entity from [%s] to [%s]",
93  entity_entry.entity_id,
94  old_unique_id,
95  new_unique_id,
96  )
97  return {
98  "new_unique_id": entity_entry.unique_id.replace(
99  old_unique_id, new_unique_id
100  )
101  }
102 
103  return None
104 
105 
107  hass: HomeAssistant,
108  config_entry: ShellyConfigEntry,
109  async_add_entities: AddEntitiesCallback,
110 ) -> None:
111  """Set buttons for device."""
112  entry_data = config_entry.runtime_data
113  coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator | None
114  if get_device_entry_gen(config_entry) in RPC_GENERATIONS:
115  coordinator = entry_data.rpc
116  else:
117  coordinator = entry_data.block
118 
119  if TYPE_CHECKING:
120  assert coordinator is not None
121 
122  await er.async_migrate_entries(
123  hass, config_entry.entry_id, partial(async_migrate_unique_ids, coordinator)
124  )
125 
127  ShellyButton(coordinator, button)
128  for button in BUTTONS
129  if button.supported(coordinator)
130  )
131 
132 
134  CoordinatorEntity[ShellyRpcCoordinator | ShellyBlockCoordinator], ButtonEntity
135 ):
136  """Defines a Shelly base button."""
137 
138  entity_description: ShellyButtonDescription[
139  ShellyRpcCoordinator | ShellyBlockCoordinator
140  ]
141 
142  def __init__(
143  self,
144  coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator,
145  description: ShellyButtonDescription[
146  ShellyRpcCoordinator | ShellyBlockCoordinator
147  ],
148  ) -> None:
149  """Initialize Shelly button."""
150  super().__init__(coordinator)
151  self.entity_descriptionentity_description = description
152 
153  self._attr_name_attr_name = f"{coordinator.device.name} {description.name}"
154  self._attr_unique_id_attr_unique_id = f"{coordinator.mac}_{description.key}"
155  self._attr_device_info_attr_device_info = DeviceInfo(
156  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
157  )
158 
159  async def async_press(self) -> None:
160  """Triggers the Shelly button press service."""
161  await self.entity_descriptionentity_description.press_action(self.coordinator)
None __init__(self, ShellyRpcCoordinator|ShellyBlockCoordinator coordinator, ShellyButtonDescription[ShellyRpcCoordinator|ShellyBlockCoordinator] description)
Definition: button.py:148
None async_setup_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: button.py:110
dict[str, Any]|None async_migrate_unique_ids(ShellyRpcCoordinator|ShellyBlockCoordinator coordinator, er.RegistryEntry entity_entry)
Definition: button.py:80
int get_device_entry_gen(ConfigEntry entry)
Definition: utils.py:353