Home Assistant Unofficial Reference 2024.12.1
event.py
Go to the documentation of this file.
1 """Matter event entities from Node events."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from chip.clusters import Objects as clusters
8 from matter_server.client.models import device_types
9 from matter_server.common.models import EventType, MatterNodeEvent
10 
12  EventDeviceClass,
13  EventEntity,
14  EventEntityDescription,
15 )
16 from homeassistant.config_entries import ConfigEntry
17 from homeassistant.const import Platform
18 from homeassistant.core import HomeAssistant, callback
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 
21 from .entity import MatterEntity
22 from .helpers import get_matter
23 from .models import MatterDiscoverySchema
24 
25 SwitchFeature = clusters.Switch.Bitmaps.Feature
26 
27 EVENT_TYPES_MAP = {
28  # mapping from raw event id's to translation keys
29  0: "switch_latched", # clusters.Switch.Events.SwitchLatched
30  1: "initial_press", # clusters.Switch.Events.InitialPress
31  2: "long_press", # clusters.Switch.Events.LongPress
32  3: "short_release", # clusters.Switch.Events.ShortRelease
33  4: "long_release", # clusters.Switch.Events.LongRelease
34  5: "multi_press_ongoing", # clusters.Switch.Events.MultiPressOngoing
35  6: "multi_press_complete", # clusters.Switch.Events.MultiPressComplete
36 }
37 
38 
39 async def async_setup_entry(
40  hass: HomeAssistant,
41  config_entry: ConfigEntry,
42  async_add_entities: AddEntitiesCallback,
43 ) -> None:
44  """Set up Matter switches from Config Entry."""
45  matter = get_matter(hass)
46  matter.register_platform_handler(Platform.EVENT, async_add_entities)
47 
48 
49 class MatterEventEntity(MatterEntity, EventEntity):
50  """Representation of a Matter Event entity."""
51 
52  def __init__(self, *args: Any, **kwargs: Any) -> None:
53  """Initialize the entity."""
54  super().__init__(*args, **kwargs)
55  # fill the event types based on the features the switch supports
56  event_types: list[str] = []
57  feature_map = int(
58  self.get_matter_attribute_value(clusters.Switch.Attributes.FeatureMap)
59  )
60  if feature_map & SwitchFeature.kLatchingSwitch:
61  # a latching switch only supports switch_latched event
62  event_types.append("switch_latched")
63  elif feature_map & SwitchFeature.kMomentarySwitchMultiPress:
64  # Momentary switch with multi press support
65  # NOTE: We ignore 'multi press ongoing' as it doesn't make a lot
66  # of sense and many devices do not support it.
67  # Instead we report on the 'multi press complete' event with the number
68  # of presses.
69  max_presses_supported = self.get_matter_attribute_value(
70  clusters.Switch.Attributes.MultiPressMax
71  )
72  max_presses_supported = min(max_presses_supported or 1, 8)
73  for i in range(max_presses_supported):
74  event_types.append(f"multi_press_{i + 1}") # noqa: PERF401
75  elif feature_map & SwitchFeature.kMomentarySwitch:
76  # momentary switch without multi press support
77  event_types.append("initial_press")
78  if feature_map & SwitchFeature.kMomentarySwitchRelease:
79  # momentary switch without multi press support can optionally support release
80  event_types.append("short_release")
81 
82  # a momentary switch can optionally support long press
83  if feature_map & SwitchFeature.kMomentarySwitchLongPress:
84  event_types.append("long_press")
85  event_types.append("long_release")
86 
87  self._attr_event_types = event_types
88 
89  async def async_added_to_hass(self) -> None:
90  """Handle being added to Home Assistant."""
91  await super().async_added_to_hass()
92 
93  # subscribe to NodeEvent events
94  self._unsubscribes.append(
95  self.matter_client.subscribe_events(
96  callback=self._on_matter_node_event,
97  event_filter=EventType.NODE_EVENT,
98  node_filter=self._endpoint.node.node_id,
99  )
100  )
101 
102  def _update_from_device(self) -> None:
103  """Call when Node attribute(s) changed."""
104 
105  @callback
106  def _on_matter_node_event(
107  self,
108  event: EventType,
109  data: MatterNodeEvent,
110  ) -> None:
111  """Call on NodeEvent."""
112  if data.endpoint_id != self._endpoint.endpoint_id:
113  return
114  if data.event_id == clusters.Switch.Events.MultiPressComplete.event_id:
115  # multi press event
116  presses = (data.data or {}).get("totalNumberOfPressesCounted", 1)
117  event_type = f"multi_press_{presses}"
118  else:
119  event_type = EVENT_TYPES_MAP[data.event_id]
120 
121  if event_type not in self.event_types:
122  # this should not happen, but guard for bad things
123  # some remotes send events that they do not report as supported (sigh...)
124  return
125 
126  # pass the rest of the data as-is (such as the advanced Position data)
127  self._trigger_event(event_type, data.data)
128  self.async_write_ha_state()
129 
130 
131 # Discovery schema(s) to map Matter Attributes to HA entities
132 DISCOVERY_SCHEMAS = [
134  platform=Platform.EVENT,
135  entity_description=EventEntityDescription(
136  key="GenericSwitch",
137  device_class=EventDeviceClass.BUTTON,
138  translation_key="button",
139  ),
140  entity_class=MatterEventEntity,
141  required_attributes=(
142  clusters.Switch.Attributes.CurrentPosition,
143  clusters.Switch.Attributes.FeatureMap,
144  ),
145  device_type=(device_types.GenericSwitch,),
146  optional_attributes=(
147  clusters.Switch.Attributes.NumberOfPositions,
148  clusters.FixedLabel.Attributes.LabelList,
149  ),
150  allow_multi=True, # also used for sensor (current position) entity
151  ),
152 ]
None __init__(self, _AOSmithCoordinatorT coordinator, str junction_id)
Definition: entity.py:20
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
MatterAdapter get_matter(HomeAssistant hass)
Definition: helpers.py:35
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:67