Home Assistant Unofficial Reference 2024.12.1
event.py
Go to the documentation of this file.
1 """Event for Shelly."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import TYPE_CHECKING, Any, Final
8 
9 from aioshelly.block_device import Block
10 from aioshelly.const import MODEL_I3, RPC_GENERATIONS
11 
13  DOMAIN as EVENT_DOMAIN,
14  EventDeviceClass,
15  EventEntity,
16  EventEntityDescription,
17 )
18 from homeassistant.core import HomeAssistant, callback
19 from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 from homeassistant.helpers.update_coordinator import CoordinatorEntity
22 
23 from .const import (
24  BASIC_INPUTS_EVENTS_TYPES,
25  RPC_INPUTS_EVENTS_TYPES,
26  SHIX3_1_INPUTS_EVENTS_TYPES,
27 )
28 from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
29 from .entity import ShellyBlockEntity
30 from .utils import (
31  async_remove_shelly_entity,
32  get_device_entry_gen,
33  get_rpc_entity_name,
34  get_rpc_key_instances,
35  is_block_momentary_input,
36  is_rpc_momentary_input,
37 )
38 
39 
40 @dataclass(frozen=True, kw_only=True)
42  """Class to describe Shelly event."""
43 
44  removal_condition: Callable[[dict, Block], bool] | None = None
45 
46 
47 @dataclass(frozen=True, kw_only=True)
49  """Class to describe Shelly event."""
50 
51  removal_condition: Callable[[dict, dict, str], bool] | None = None
52 
53 
54 BLOCK_EVENT: Final = ShellyBlockEventDescription(
55  key="input",
56  translation_key="input",
57  device_class=EventDeviceClass.BUTTON,
58  removal_condition=lambda settings, block: not is_block_momentary_input(
59  settings, block, True
60  ),
61 )
62 RPC_EVENT: Final = ShellyRpcEventDescription(
63  key="input",
64  translation_key="input",
65  device_class=EventDeviceClass.BUTTON,
66  event_types=list(RPC_INPUTS_EVENTS_TYPES),
67  removal_condition=lambda config, status, key: not is_rpc_momentary_input(
68  config, status, key
69  ),
70 )
71 
72 
74  hass: HomeAssistant,
75  config_entry: ShellyConfigEntry,
76  async_add_entities: AddEntitiesCallback,
77 ) -> None:
78  """Set up sensors for device."""
79  entities: list[ShellyBlockEvent | ShellyRpcEvent] = []
80 
81  coordinator: ShellyRpcCoordinator | ShellyBlockCoordinator | None = None
82 
83  if get_device_entry_gen(config_entry) in RPC_GENERATIONS:
84  coordinator = config_entry.runtime_data.rpc
85  if TYPE_CHECKING:
86  assert coordinator
87 
88  key_instances = get_rpc_key_instances(coordinator.device.status, RPC_EVENT.key)
89 
90  for key in key_instances:
91  if RPC_EVENT.removal_condition and RPC_EVENT.removal_condition(
92  coordinator.device.config, coordinator.device.status, key
93  ):
94  unique_id = f"{coordinator.mac}-{key}"
95  async_remove_shelly_entity(hass, EVENT_DOMAIN, unique_id)
96  else:
97  entities.append(ShellyRpcEvent(coordinator, key, RPC_EVENT))
98  else:
99  coordinator = config_entry.runtime_data.block
100  if TYPE_CHECKING:
101  assert coordinator
102  assert coordinator.device.blocks
103 
104  for block in coordinator.device.blocks:
105  if (
106  "inputEvent" not in block.sensor_ids
107  or "inputEventCnt" not in block.sensor_ids
108  ):
109  continue
110 
111  if BLOCK_EVENT.removal_condition and BLOCK_EVENT.removal_condition(
112  coordinator.device.settings, block
113  ):
114  channel = int(block.channel or 0) + 1
115  unique_id = f"{coordinator.mac}-{block.description}-{channel}"
116  async_remove_shelly_entity(hass, EVENT_DOMAIN, unique_id)
117  else:
118  entities.append(ShellyBlockEvent(coordinator, block, BLOCK_EVENT))
119 
120  async_add_entities(entities)
121 
122 
124  """Represent Block event entity."""
125 
126  entity_description: ShellyBlockEventDescription
127 
128  def __init__(
129  self,
130  coordinator: ShellyBlockCoordinator,
131  block: Block,
132  description: ShellyBlockEventDescription,
133  ) -> None:
134  """Initialize Shelly entity."""
135  super().__init__(coordinator, block)
136  self.channelchannel = channel = int(block.channel or 0) + 1
137  self._attr_unique_id_attr_unique_id_attr_unique_id = f"{super().unique_id}-{channel}"
138 
139  if coordinator.model == MODEL_I3:
140  self._attr_event_types_attr_event_types = list(SHIX3_1_INPUTS_EVENTS_TYPES)
141  else:
142  self._attr_event_types_attr_event_types = list(BASIC_INPUTS_EVENTS_TYPES)
143  self.entity_descriptionentity_description = description
144 
145  async def async_added_to_hass(self) -> None:
146  """When entity is added to hass."""
147  await super().async_added_to_hass()
148  self.async_on_removeasync_on_remove(
149  self.coordinator.async_subscribe_input_events(self._async_handle_event_async_handle_event)
150  )
151 
152  @callback
153  def _async_handle_event(self, event: dict[str, Any]) -> None:
154  """Handle the demo button event."""
155  if event["channel"] == self.channelchannel:
156  self._trigger_event_trigger_event(event["event"])
157  self.async_write_ha_stateasync_write_ha_state()
158 
159 
160 class ShellyRpcEvent(CoordinatorEntity[ShellyRpcCoordinator], EventEntity):
161  """Represent RPC event entity."""
162 
163  entity_description: ShellyRpcEventDescription
164 
165  def __init__(
166  self,
167  coordinator: ShellyRpcCoordinator,
168  key: str,
169  description: ShellyRpcEventDescription,
170  ) -> None:
171  """Initialize Shelly entity."""
172  super().__init__(coordinator)
173  self.input_indexinput_index = int(key.split(":")[-1])
174  self._attr_device_info_attr_device_info = DeviceInfo(
175  connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
176  )
177  self._attr_unique_id_attr_unique_id = f"{coordinator.mac}-{key}"
178  self._attr_name_attr_name = get_rpc_entity_name(coordinator.device, key)
179  self.entity_descriptionentity_description = description
180 
181  async def async_added_to_hass(self) -> None:
182  """When entity is added to hass."""
183  await super().async_added_to_hass()
184  self.async_on_removeasync_on_remove(
185  self.coordinator.async_subscribe_input_events(self._async_handle_event_async_handle_event)
186  )
187 
188  @callback
189  def _async_handle_event(self, event: dict[str, Any]) -> None:
190  """Handle the demo button event."""
191  if event["id"] == self.input_indexinput_index:
192  self._trigger_event_trigger_event(event["event"])
193  self.async_write_ha_stateasync_write_ha_state()
None _trigger_event(self, str event_type, dict[str, Any]|None event_attributes=None)
Definition: __init__.py:148
CALLBACK_TYPE async_subscribe_input_events(self, Callable[[dict[str, Any]], None] input_event_callback)
Definition: coordinator.py:265
CALLBACK_TYPE async_subscribe_input_events(self, Callable[[dict[str, Any]], None] input_event_callback)
Definition: coordinator.py:540
None __init__(self, ShellyBlockCoordinator coordinator, Block block, ShellyBlockEventDescription description)
Definition: event.py:133
None _async_handle_event(self, dict[str, Any] event)
Definition: event.py:153
None _async_handle_event(self, dict[str, Any] event)
Definition: event.py:189
None __init__(self, ShellyRpcCoordinator coordinator, str key, ShellyRpcEventDescription description)
Definition: event.py:170
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_setup_entry(HomeAssistant hass, ShellyConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: event.py:77
bool is_rpc_momentary_input(dict[str, Any] config, dict[str, Any] status, str key)
Definition: utils.py:376
list[str] get_rpc_key_instances(dict[str, Any] keys_dict, str key)
Definition: utils.py:358
bool is_block_momentary_input(dict[str, Any] settings, Block block, bool include_detached=False)
Definition: utils.py:145
int get_device_entry_gen(ConfigEntry entry)
Definition: utils.py:353
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