Prevent executor overload when starting many homekit instances (#59950)

This commit is contained in:
J. Nick Koston 2021-11-19 00:39:49 -06:00 committed by GitHub
parent 073bf6d6fd
commit 2f00f8d3de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 22 additions and 19 deletions

View File

@ -1,4 +1,6 @@
"""Support for Apple HomeKit.""" """Support for Apple HomeKit."""
from __future__ import annotations
import asyncio import asyncio
import ipaddress import ipaddress
import logging import logging
@ -91,6 +93,7 @@ from .const import (
HOMEKIT_PAIRING_QR, HOMEKIT_PAIRING_QR,
HOMEKIT_PAIRING_QR_SECRET, HOMEKIT_PAIRING_QR_SECRET,
MANUFACTURER, MANUFACTURER,
PERSIST_LOCK,
SERVICE_HOMEKIT_RESET_ACCESSORY, SERVICE_HOMEKIT_RESET_ACCESSORY,
SERVICE_HOMEKIT_START, SERVICE_HOMEKIT_START,
SERVICE_HOMEKIT_UNPAIR, SERVICE_HOMEKIT_UNPAIR,
@ -171,6 +174,15 @@ UNPAIR_SERVICE_SCHEMA = vol.All(
) )
def _async_all_homekit_instances(hass: HomeAssistant) -> list[HomeKit]:
"""All active HomeKit instances."""
return [
data[HOMEKIT]
for data in hass.data[DOMAIN].values()
if isinstance(data, dict) and HOMEKIT in data
]
def _async_get_entries_by_name(current_entries): def _async_get_entries_by_name(current_entries):
"""Return a dict of the entries by name.""" """Return a dict of the entries by name."""
@ -181,7 +193,7 @@ def _async_get_entries_by_name(current_entries):
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the HomeKit from yaml.""" """Set up the HomeKit from yaml."""
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})[PERSIST_LOCK] = asyncio.Lock()
_async_register_events_and_services(hass) _async_register_events_and_services(hass)
@ -360,10 +372,7 @@ def _async_register_events_and_services(hass: HomeAssistant):
async def async_handle_homekit_reset_accessory(service): async def async_handle_homekit_reset_accessory(service):
"""Handle reset accessory HomeKit service call.""" """Handle reset accessory HomeKit service call."""
for entry_id in hass.data[DOMAIN]: for homekit in _async_all_homekit_instances(hass):
if HOMEKIT not in hass.data[DOMAIN][entry_id]:
continue
homekit = hass.data[DOMAIN][entry_id][HOMEKIT]
if homekit.status != STATUS_RUNNING: if homekit.status != STATUS_RUNNING:
_LOGGER.warning( _LOGGER.warning(
"HomeKit is not running. Either it is waiting to be " "HomeKit is not running. Either it is waiting to be "
@ -393,16 +402,11 @@ def _async_register_events_and_services(hass: HomeAssistant):
for ctype, cval in dev_reg_ent.connections for ctype, cval in dev_reg_ent.connections
if ctype == device_registry.CONNECTION_NETWORK_MAC if ctype == device_registry.CONNECTION_NETWORK_MAC
] ]
domain_data = hass.data[DOMAIN]
matching_instances = [ matching_instances = [
domain_data[entry_id][HOMEKIT] homekit
for entry_id in domain_data for homekit in _async_all_homekit_instances(hass)
if HOMEKIT in domain_data[entry_id] if homekit.driver
and domain_data[entry_id][HOMEKIT].driver and device_registry.format_mac(homekit.driver.state.mac) in macs
and device_registry.format_mac(
domain_data[entry_id][HOMEKIT].driver.state.mac
)
in macs
] ]
if not matching_instances: if not matching_instances:
raise HomeAssistantError( raise HomeAssistantError(
@ -421,10 +425,7 @@ def _async_register_events_and_services(hass: HomeAssistant):
async def async_handle_homekit_service_start(service): async def async_handle_homekit_service_start(service):
"""Handle start HomeKit service call.""" """Handle start HomeKit service call."""
tasks = [] tasks = []
for entry_id in hass.data[DOMAIN]: for homekit in _async_all_homekit_instances(hass):
if HOMEKIT not in hass.data[DOMAIN][entry_id]:
continue
homekit = hass.data[DOMAIN][entry_id][HOMEKIT]
if homekit.status == STATUS_RUNNING: if homekit.status == STATUS_RUNNING:
_LOGGER.debug("HomeKit is already running") _LOGGER.debug("HomeKit is already running")
continue continue
@ -707,7 +708,8 @@ class HomeKit:
self._async_register_bridge() self._async_register_bridge()
_LOGGER.debug("Driver start for %s", self._name) _LOGGER.debug("Driver start for %s", self._name)
await self.driver.async_start() await self.driver.async_start()
self.driver.async_persist() async with self.hass.data[DOMAIN][PERSIST_LOCK]:
await self.hass.async_add_executor_job(self.driver.persist)
self.status = STATUS_RUNNING self.status = STATUS_RUNNING
if self.driver.state.paired: if self.driver.state.paired:

View File

@ -12,6 +12,7 @@ HOMEKIT_PAIRING_QR_SECRET = "homekit-pairing-qr-secret"
HOMEKIT = "homekit" HOMEKIT = "homekit"
SHUTDOWN_TIMEOUT = 30 SHUTDOWN_TIMEOUT = 30
CONF_ENTRY_INDEX = "index" CONF_ENTRY_INDEX = "index"
PERSIST_LOCK = "persist_lock"
# ### Codecs #### # ### Codecs ####
VIDEO_CODEC_COPY = "copy" VIDEO_CODEC_COPY = "copy"