1 """Homekit Controller entities."""
3 from __future__
import annotations
7 from aiohomekit.model.characteristics
import (
10 CharacteristicPermissions,
13 from aiohomekit.model.services
import Service, ServicesTypes
20 from .connection
import HKDevice, valid_serial_number
21 from .utils
import folded_name
25 """Representation of a Home Assistant HomeKit device."""
27 _attr_should_poll =
False
28 pollable_characteristics: list[tuple[int, int]]
29 watchable_characteristics: list[tuple[int, int]]
30 all_characteristics: set[tuple[int, int]]
32 accessory_info: Service
34 def __init__(self, accessory: HKDevice, devinfo: ConfigType) ->
None:
35 """Initialise a generic HomeKit device."""
37 self._aid: int = devinfo[
"aid"]
38 self._iid: int = devinfo[
"iid"]
39 self._entity_key: tuple[int, int |
None, int |
None] = (
47 self.
_attr_unique_id_attr_unique_id = f
"{accessory.unique_id}_{self._aid}_{self._iid}"
52 """Handle entity removal."""
59 self.
hasshass.async_create_task(self.
async_removeasync_remove(force_remove=
True))
63 """Handle accessory or service disappearance."""
64 entity_map = self.
_accessory_accessory.entity_map
66 accessory := entity_map.aid_or_none(self._aid)
67 )
or not accessory.services.iid_or_none(self._iid):
74 """Handle accessory discovery changes."""
80 """Clear the cache of properties."""
81 for prop
in properties:
82 self.__dict__.pop(prop,
None)
86 """Reconfigure the entity."""
93 """Entity added to hass."""
103 """Prepare to be removed from hass."""
105 self.
_accessory_accessory.async_entity_key_removed(self._entity_key)
109 """Handle unsubscribing from characteristics."""
118 """Handle registering characteristics to watch and subscribe."""
126 """Write characteristics to the device.
128 A characteristic type is unique within a service, but in order to write
129 to a named characteristic on a bridge we need to turn its type into
130 an aid and iid, and send it as a list of tuples, which is what this
135 await entity.async_put_characteristics({
136 CharacteristicsTypes.ON: True
139 payload = self.
serviceservice.build_update(characteristics)
140 return await self.
_accessory_accessory.put_characteristics(payload)
144 """Configure an entity based on its HomeKit characteristics metadata."""
146 self.
accessoryaccessory = accessory.entity_map.aid(self._aid)
148 accessory_info = self.
accessoryaccessory.services.first(
149 service_type=ServicesTypes.ACCESSORY_INFORMATION
151 assert accessory_info
165 for char
in self.
serviceservice.characteristics.filter(char_types=char_types):
170 for service
in self.
accessoryaccessory.services.filter(parent_service=self.
serviceservice):
171 for char
in service.characteristics.filter(char_types=char_types):
179 """Configure an entity based on a HomeKit characteristics metadata."""
182 CharacteristicPermissions.paired_read
in char.perms
183 and char.type
not in EVENT_CHARACTERISTICS
188 if CharacteristicPermissions.events
in char.perms:
192 self.
_char_name_char_name = char.service.value(CharacteristicsTypes.NAME)
196 """Return the OLD ID of this device."""
198 serial = info.value(CharacteristicsTypes.SERIAL_NUMBER)
200 return f
"homekit-{serial}-{self._iid}"
202 return f
"homekit-{self._accessory.unique_id}-{self._aid}-{self._iid}"
206 """Return the default name of the device."""
211 """Return the name of the device if any."""
212 accessory_name = self.
accessoryaccessory.name
217 folded_accessory_name =
folded_name(accessory_name)
220 if folded_device_name.startswith(folded_accessory_name):
223 folded_accessory_name
not in folded_device_name
224 and folded_device_name
not in folded_accessory_name
226 return f
"{accessory_name} {device_name}"
227 return accessory_name
231 """Return True if entity is available."""
233 for char
in self.
serviceservice.characteristics:
234 if char.iid
in all_iids
and not char.available:
240 """Return the device info."""
244 """Define the homekit characteristics the entity cares about."""
245 raise NotImplementedError
248 """Update the entity."""
249 await self.
_accessory_accessory.async_request_update()
253 """A HomeKit entity that is related to an entire accessory rather than a specific service or characteristic."""
255 def __init__(self, accessory: HKDevice, devinfo: ConfigType) ->
None:
256 """Initialise a generic HomeKit accessory."""
257 super().
__init__(accessory, devinfo)
262 """Return the old ID of this device."""
263 serial = self.
accessory_infoaccessory_info.value(CharacteristicsTypes.SERIAL_NUMBER)
264 return f
"homekit-{serial}-aid:{self._aid}"
268 """A HomeKit entity that is related to an single characteristic rather than a whole service.
270 This is typically used to expose additional sensor, binary_sensor or number entities that don't belong with
275 self, accessory: HKDevice, devinfo: ConfigType, char: Characteristic
277 """Initialise a generic single characteristic HomeKit entity."""
279 super().
__init__(accessory, devinfo)
284 """Handle characteristic disappearance."""
286 not self.
_accessory_accessory.entity_map.aid(self._aid)
287 .services.iid(self._iid)
288 .get_char_by_iid(self.
_char_char.iid)
296 """Handle accessory discovery changes."""
305 """A HomeKit entity that is related to an single characteristic rather than a whole service.
307 This is typically used to expose additional sensor, binary_sensor or number entities that don't belong with
312 self, accessory: HKDevice, devinfo: ConfigType, char: Characteristic
314 """Initialise a generic single characteristic HomeKit entity."""
315 super().
__init__(accessory, devinfo, char)
317 f
"{accessory.unique_id}_{self._aid}_{char.service.iid}_{char.iid}"
322 """Return the old ID of this device."""
323 serial = self.
accessory_infoaccessory_info.value(CharacteristicsTypes.SERIAL_NUMBER)
324 return f
"homekit-{serial}-aid:{self._aid}-sid:{self._char.service.iid}-cid:{self._char.iid}"
None __init__(self, HKDevice accessory, ConfigType devinfo)
bool _async_remove_entity_if_characteristics_disappeared(self)
None _async_config_changed(self)
None __init__(self, HKDevice accessory, ConfigType devinfo, Characteristic char)
None __init__(self, HKDevice accessory, ConfigType devinfo, Characteristic char)
None _async_config_changed(self)
str|None default_name(self)
DeviceInfo device_info(self)
None async_put_characteristics(self, dict[str, Any] characteristics)
None async_added_to_hass(self)
None _async_reconfigure(self)
None async_will_remove_from_hass(self)
bool _async_remove_entity_if_accessory_or_service_disappeared(self)
None _async_subscribe_chars(self)
None _async_handle_entity_removed(self)
None __init__(self, HKDevice accessory, ConfigType devinfo)
list[str] get_characteristic_types(self)
None _async_clear_property_cache(self, tuple[str,...] properties)
None _setup_characteristic(self, Characteristic char)
None _async_unsubscribe_chars(self)
watchable_characteristics
None _async_write_ha_state(self)
None async_write_ha_state(self)
None async_on_remove(self, CALLBACK_TYPE func)
None async_remove(self, *bool force_remove=False)
bool valid_serial_number(str serial)
str folded_name(str name)
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)