Skip to content

Commit

Permalink
Fix instability with HomeKit trigger accessories (#80703)
Browse files Browse the repository at this point in the history
fixes #78774
fixes #81685
  • Loading branch information
bdraco authored and frenck committed Nov 16, 2022
1 parent 18842ef commit d94e969
Show file tree
Hide file tree
Showing 6 changed files with 449 additions and 31 deletions.
59 changes: 40 additions & 19 deletions homeassistant/components/homekit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
BinarySensorDeviceClass,
)
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
from homeassistant.components.device_automation.trigger import (
async_validate_trigger_config,
)
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN
from homeassistant.components.network import MDNS_TARGET_IP
Expand Down Expand Up @@ -906,28 +909,46 @@ async def _async_create_bridge_accessory(
self.bridge = HomeBridge(self.hass, self.driver, self._name)
for state in entity_states:
self.add_bridge_accessory(state)
dev_reg = device_registry.async_get(self.hass)
if self._devices:
valid_device_ids = []
for device_id in self._devices:
if not dev_reg.async_get(device_id):
_LOGGER.warning(
"HomeKit %s cannot add device %s because it is missing from the device registry",
await self._async_add_trigger_accessories()
return self.bridge

async def _async_add_trigger_accessories(self) -> None:
"""Add devices with triggers to the bridge."""
dev_reg = device_registry.async_get(self.hass)
valid_device_ids = []
for device_id in self._devices:
if not dev_reg.async_get(device_id):
_LOGGER.warning(
"HomeKit %s cannot add device %s because it is missing from the device registry",
self._name,
device_id,
)
else:
valid_device_ids.append(device_id)
for device_id, device_triggers in (
await device_automation.async_get_device_automations(
self.hass,
device_automation.DeviceAutomationType.TRIGGER,
valid_device_ids,
)
).items():
device = dev_reg.async_get(device_id)
assert device is not None
valid_device_triggers: list[dict[str, Any]] = []
for trigger in device_triggers:
try:
await async_validate_trigger_config(self.hass, trigger)
except vol.Invalid as ex:
_LOGGER.debug(
"%s: cannot add unsupported trigger %s because it requires additional inputs which are not supported by HomeKit: %s",
self._name,
device_id,
trigger,
ex,
)
else:
valid_device_ids.append(device_id)
for device_id, device_triggers in (
await device_automation.async_get_device_automations(
self.hass,
device_automation.DeviceAutomationType.TRIGGER,
valid_device_ids,
)
).items():
if device := dev_reg.async_get(device_id):
self.add_bridge_triggers_accessory(device, device_triggers)
return self.bridge
continue
valid_device_triggers.append(trigger)
self.add_bridge_triggers_accessory(device, valid_device_triggers)

async def _async_create_accessories(self) -> bool:
"""Create the accessories."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/homekit/accessories.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ def get_iid_for_obj(self, obj: Characteristic | Service) -> int:
"""Get IID for object."""
aid = obj.broker.aid
if isinstance(obj, Characteristic):
service = obj.service
service: Service = obj.service
iid = self._iid_storage.get_or_allocate_iid(
aid, service.type_id, service.unique_id, obj.type_id, obj.unique_id
)
Expand Down
11 changes: 7 additions & 4 deletions homeassistant/components/homekit/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,16 @@ def _get_accessory_diagnostics(
hass: HomeAssistant, accessory: HomeAccessory
) -> dict[str, Any]:
"""Return diagnostics for an accessory."""
return {
entity_state = None
if accessory.entity_id:
entity_state = hass.states.get(accessory.entity_id)
data = {
"aid": accessory.aid,
"config": accessory.config,
"category": accessory.category,
"name": accessory.display_name,
"entity_id": accessory.entity_id,
"entity_state": async_redact_data(
hass.states.get(accessory.entity_id), TO_REDACT
),
}
if entity_state:
data["entity_state"] = async_redact_data(entity_state, TO_REDACT)
return data
22 changes: 17 additions & 5 deletions homeassistant/components/homekit/type_triggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from pyhap.const import CATEGORY_SENSOR

from homeassistant.core import CALLBACK_TYPE, Context
from homeassistant.helpers import entity_registry
from homeassistant.helpers.trigger import async_initialize_triggers

from .accessories import TYPES, HomeAccessory
from .aidmanager import get_system_unique_id
from .const import (
CHAR_NAME,
CHAR_PROGRAMMABLE_SWITCH_EVENT,
Expand All @@ -18,6 +20,7 @@
SERV_SERVICE_LABEL,
SERV_STATELESS_PROGRAMMABLE_SWITCH,
)
from .util import cleanup_name_for_homekit

_LOGGER = logging.getLogger(__name__)

Expand All @@ -39,13 +42,22 @@ def __init__(
self._remove_triggers: CALLBACK_TYPE | None = None
self.triggers = []
assert device_triggers is not None
ent_reg = entity_registry.async_get(self.hass)
for idx, trigger in enumerate(device_triggers):
type_ = trigger["type"]
subtype = trigger.get("subtype")
type_: str = trigger["type"]
subtype: str | None = trigger.get("subtype")
unique_id = f'{type_}-{subtype or ""}'
trigger_name = (
f"{type_.title()} {subtype.title()}" if subtype else type_.title()
)
if (entity_id := trigger.get("entity_id")) and (
entry := ent_reg.async_get(entity_id)
):
unique_id += f"-entity_unique_id:{get_system_unique_id(entry)}"
trigger_name_parts = []
if entity_id and (state := self.hass.states.get(entity_id)):
trigger_name_parts.append(state.name)
trigger_name_parts.append(type_.replace("_", " ").title())
if subtype:
trigger_name_parts.append(subtype.replace("_", " ").title())
trigger_name = cleanup_name_for_homekit(" ".join(trigger_name_parts))
serv_stateless_switch = self.add_preload_service(
SERV_STATELESS_PROGRAMMABLE_SWITCH,
[CHAR_NAME, CHAR_SERVICE_LABEL_INDEX],
Expand Down
Loading

0 comments on commit d94e969

Please sign in to comment.