diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 314db187b6a..6ef5917a0fb 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -22,11 +22,10 @@ from aiohomekit.model.services import Service, ServicesTypes from homeassistant.components.thread.dataset_store import async_get_preferred_dataset from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_VIA_DEVICE, EVENT_HOMEASSISTANT_STARTED -from homeassistant.core import CoreState, Event, HomeAssistant, callback +from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers.debounce import Debouncer -from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.event import async_track_time_interval @@ -116,8 +115,6 @@ class HKDevice: self.available = False - self.signal_state_updated = "_".join((DOMAIN, self.unique_id, "state_updated")) - self.pollable_characteristics: list[tuple[int, int]] = [] # Never allow concurrent polling of the same accessory or bridge @@ -138,6 +135,9 @@ class HKDevice: function=self.async_update, ) + self._all_subscribers: set[CALLBACK_TYPE] = set() + self._subscriptions: dict[tuple[int, int], set[CALLBACK_TYPE]] = {} + @property def entity_map(self) -> Accessories: """Return the accessories from the pairing.""" @@ -182,7 +182,8 @@ class HKDevice: if self.available == available: return self.available = available - async_dispatcher_send(self.hass, self.signal_state_updated) + for callback_ in self._all_subscribers: + callback_() async def _async_populate_ble_accessory_state(self, event: Event) -> None: """Populate the BLE accessory state without blocking startup. @@ -768,7 +769,31 @@ class HKDevice: self.entity_map.process_changes(new_values_dict) - async_dispatcher_send(self.hass, self.signal_state_updated, new_values_dict) + to_callback: set[CALLBACK_TYPE] = set() + for aid_iid in new_values_dict: + if callbacks := self._subscriptions.get(aid_iid): + to_callback.update(callbacks) + + for callback_ in to_callback: + callback_() + + @callback + def async_subscribe( + self, characteristics: Iterable[tuple[int, int]], callback_: CALLBACK_TYPE + ) -> CALLBACK_TYPE: + """Add characteristics to the watch list.""" + self._all_subscribers.add(callback_) + for aid_iid in characteristics: + self._subscriptions.setdefault(aid_iid, set()).add(callback_) + + def _unsub(): + self._all_subscribers.remove(callback_) + for aid_iid in characteristics: + self._subscriptions[aid_iid].remove(callback_) + if not self._subscriptions[aid_iid]: + del self._subscriptions[aid_iid] + + return _unsub async def get_characteristics(self, *args: Any, **kwargs: Any) -> dict[str, Any]: """Read latest state from homekit accessory.""" diff --git a/homeassistant/components/homekit_controller/entity.py b/homeassistant/components/homekit_controller/entity.py index 5a687020eb6..f6aadfac7ac 100644 --- a/homeassistant/components/homekit_controller/entity.py +++ b/homeassistant/components/homekit_controller/entity.py @@ -11,8 +11,6 @@ from aiohomekit.model.characteristics import ( ) from aiohomekit.model.services import Service, ServicesTypes -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.typing import ConfigType @@ -53,23 +51,11 @@ class HomeKitEntity(Entity): """Return a Service model that this entity is attached to.""" return self.accessory.services.iid(self._iid) - @callback - def _async_state_changed( - self, new_values_dict: dict[tuple[int, int], dict[str, Any]] | None = None - ) -> None: - """Handle when characteristics change value.""" - if new_values_dict is None or self.all_characteristics.intersection( - new_values_dict - ): - self.async_write_ha_state() - async def async_added_to_hass(self) -> None: """Entity added to hass.""" self.async_on_remove( - async_dispatcher_connect( - self.hass, - self._accessory.signal_state_updated, - self._async_state_changed, + self._accessory.async_subscribe( + self.all_characteristics, self._async_write_ha_state ) )