Home Assistant Unofficial Reference 2024.12.1
event.py
Go to the documentation of this file.
1 """Support for Homekit motion sensors."""
2 
3 from __future__ import annotations
4 
5 from aiohomekit.model.characteristics import CharacteristicsTypes
6 from aiohomekit.model.characteristics.const import InputEventValues
7 from aiohomekit.model.services import Service, ServicesTypes
8 from aiohomekit.utils import clamp_enum_to_char
9 
11  EventDeviceClass,
12  EventEntity,
13  EventEntityDescription,
14 )
15 from homeassistant.config_entries import ConfigEntry
16 from homeassistant.core import HomeAssistant, callback
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 
19 from . import KNOWN_DEVICES
20 from .connection import HKDevice
21 from .entity import BaseCharacteristicEntity
22 
23 INPUT_EVENT_VALUES = {
24  InputEventValues.SINGLE_PRESS: "single_press",
25  InputEventValues.DOUBLE_PRESS: "double_press",
26  InputEventValues.LONG_PRESS: "long_press",
27 }
28 
29 
31  """Representation of a Homekit event entity."""
32 
33  _attr_should_poll = False
34 
35  def __init__(
36  self,
37  connection: HKDevice,
38  service: Service,
39  entity_description: EventEntityDescription,
40  ) -> None:
41  """Initialise a generic HomeKit event entity."""
42  super().__init__(
43  connection,
44  {
45  "aid": service.accessory.aid,
46  "iid": service.iid,
47  },
48  service.characteristics_by_type[CharacteristicsTypes.INPUT_EVENT],
49  )
50 
51  self.entity_descriptionentity_description = entity_description
52 
53  # An INPUT_EVENT may support single_press, long_press and double_press. All are optional. So we have to
54  # clamp InputEventValues for this exact device
55  self._attr_event_types_attr_event_types = [
56  INPUT_EVENT_VALUES[v]
57  for v in clamp_enum_to_char(InputEventValues, self._char_char)
58  ]
59 
60  def get_characteristic_types(self) -> list[str]:
61  """Define the homekit characteristics the entity cares about."""
62  return [CharacteristicsTypes.INPUT_EVENT]
63 
64  async def async_added_to_hass(self) -> None:
65  """Entity added to hass."""
66  await super().async_added_to_hass()
67 
68  self.async_on_removeasync_on_remove(
69  self._accessory_accessory.async_subscribe(
70  {(self._aid, self._char_char.iid)},
71  self._handle_event_handle_event,
72  )
73  )
74 
75  @callback
76  def _handle_event(self) -> None:
77  if self._char_char.value is None:
78  # For IP backed devices the characteristic is marked as
79  # pollable, but always returns None when polled
80  # Make sure we don't explode if we see that edge case.
81  return
82  self._trigger_event_trigger_event(INPUT_EVENT_VALUES[self._char_char.value])
83  self.async_write_ha_stateasync_write_ha_state()
84 
85 
87  hass: HomeAssistant,
88  config_entry: ConfigEntry,
89  async_add_entities: AddEntitiesCallback,
90 ) -> None:
91  """Set up Homekit event."""
92  hkid: str = config_entry.data["AccessoryPairingID"]
93  conn: HKDevice = hass.data[KNOWN_DEVICES][hkid]
94 
95  @callback
96  def async_add_service(service: Service) -> bool:
97  entities = []
98 
99  if service.type == ServicesTypes.DOORBELL:
100  entities.append(
102  conn,
103  service,
105  key=f"{service.accessory.aid}_{service.iid}",
106  device_class=EventDeviceClass.DOORBELL,
107  translation_key="doorbell",
108  ),
109  )
110  )
111 
112  elif service.type == ServicesTypes.SERVICE_LABEL:
113  switches = list(
114  service.accessory.services.filter(
115  service_type=ServicesTypes.STATELESS_PROGRAMMABLE_SWITCH,
116  child_service=service,
117  order_by=[CharacteristicsTypes.SERVICE_LABEL_INDEX],
118  )
119  )
120 
121  # The Apple docs say that if we number the buttons ourselves
122  # We do it in service label index order. `switches` is already in
123  # that order.
124  entities.extend(
126  conn,
127  switch,
129  key=f"{service.accessory.aid}_{service.iid}",
130  device_class=EventDeviceClass.BUTTON,
131  translation_key="button",
132  ),
133  )
134  for switch in switches
135  )
136 
137  elif service.type == ServicesTypes.STATELESS_PROGRAMMABLE_SWITCH:
138  # A stateless switch that has a SERVICE_LABEL_INDEX is part of a group
139  # And is handled separately
140  if not service.has(CharacteristicsTypes.SERVICE_LABEL_INDEX):
141  entities.append(
143  conn,
144  service,
146  key=f"{service.accessory.aid}_{service.iid}",
147  device_class=EventDeviceClass.BUTTON,
148  translation_key="button",
149  ),
150  )
151  )
152 
153  if entities:
154  async_add_entities(entities)
155  return True
156 
157  return False
158 
159  conn.add_listener(async_add_service)
None _trigger_event(self, str event_type, dict[str, Any]|None event_attributes=None)
Definition: __init__.py:148
None __init__(self, HKDevice connection, Service service, EventEntityDescription entity_description)
Definition: event.py:40
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: event.py:90
CALLBACK_TYPE async_subscribe(HomeAssistant hass, str topic, Callable[[ReceiveMessage], Coroutine[Any, Any, None]|None] msg_callback, int qos=DEFAULT_QOS, str|None encoding=DEFAULT_ENCODING)
Definition: client.py:194