Restore HomeKit Controller BLE GSN at startup (#83206)
This commit is contained in:
parent
de77132a5a
commit
1577f6ea50
8 changed files with 50 additions and 37 deletions
|
@ -20,7 +20,7 @@ from aiohomekit.model.services import Service
|
|||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_VIA_DEVICE, EVENT_HOMEASSISTANT_STARTED
|
||||
from homeassistant.core import CALLBACK_TYPE, CoreState, Event, HomeAssistant, callback
|
||||
from homeassistant.core import CoreState, Event, HomeAssistant, callback
|
||||
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
|
||||
|
@ -116,11 +116,6 @@ class HKDevice:
|
|||
|
||||
self.pollable_characteristics: list[tuple[int, int]] = []
|
||||
|
||||
# If this is set polling is active and can be disabled by calling
|
||||
# this method.
|
||||
self._polling_interval_remover: CALLBACK_TYPE | None = None
|
||||
self._ble_available_interval_remover: CALLBACK_TYPE | None = None
|
||||
|
||||
# Never allow concurrent polling of the same accessory or bridge
|
||||
self._polling_lock = asyncio.Lock()
|
||||
self._polling_lock_warned = False
|
||||
|
@ -185,8 +180,8 @@ class HKDevice:
|
|||
self.available = available
|
||||
async_dispatcher_send(self.hass, self.signal_state_updated)
|
||||
|
||||
async def _async_retry_populate_ble_accessory_state(self, event: Event) -> None:
|
||||
"""Try again to populate the BLE accessory state.
|
||||
async def _async_populate_ble_accessory_state(self, event: Event) -> None:
|
||||
"""Populate the BLE accessory state without blocking startup.
|
||||
|
||||
If the accessory was asleep at startup we need to retry
|
||||
since we continued on to allow startup to proceed.
|
||||
|
@ -194,6 +189,7 @@ class HKDevice:
|
|||
If this fails the state may be inconsistent, but will
|
||||
get corrected as soon as the accessory advertises again.
|
||||
"""
|
||||
self._async_start_polling()
|
||||
try:
|
||||
await self.pairing.async_populate_accessories_state(force_update=True)
|
||||
except STARTUP_EXCEPTIONS as ex:
|
||||
|
@ -221,20 +217,28 @@ class HKDevice:
|
|||
# so we only poll those chars but that is not possible
|
||||
# yet.
|
||||
attempts = None if self.hass.state == CoreState.running else 1
|
||||
try:
|
||||
await self.pairing.async_populate_accessories_state(
|
||||
force_update=True, attempts=attempts
|
||||
)
|
||||
except AccessoryNotFoundError:
|
||||
if transport != Transport.BLE or not pairing.accessories:
|
||||
# BLE devices may sleep and we can't force a connection
|
||||
raise
|
||||
if (
|
||||
transport == Transport.BLE
|
||||
and pairing.accessories
|
||||
and pairing.accessories.has_aid(1)
|
||||
):
|
||||
# The GSN gets restored and a catch up poll will be
|
||||
# triggered via disconnected events automatically
|
||||
# if we are out of sync. To be sure we are in sync;
|
||||
# If for some reason the BLE connection failed
|
||||
# previously we force an update after startup
|
||||
# is complete.
|
||||
entry.async_on_unload(
|
||||
self.hass.bus.async_listen(
|
||||
EVENT_HOMEASSISTANT_STARTED,
|
||||
self._async_retry_populate_ble_accessory_state,
|
||||
self._async_populate_ble_accessory_state,
|
||||
)
|
||||
)
|
||||
else:
|
||||
await self.pairing.async_populate_accessories_state(
|
||||
force_update=True, attempts=attempts
|
||||
)
|
||||
self._async_start_polling()
|
||||
|
||||
entry.async_on_unload(pairing.dispatcher_connect(self.process_new_events))
|
||||
entry.async_on_unload(
|
||||
|
@ -252,27 +256,34 @@ class HKDevice:
|
|||
|
||||
self.async_set_available_state(self.pairing.is_available)
|
||||
|
||||
# We use async_request_update to avoid multiple updates
|
||||
# at the same time which would generate a spurious warning
|
||||
# in the log about concurrent polling.
|
||||
self._polling_interval_remover = async_track_time_interval(
|
||||
self.hass, self.async_request_update, self.pairing.poll_interval
|
||||
)
|
||||
|
||||
if transport == Transport.BLE:
|
||||
# If we are using BLE, we need to periodically check of the
|
||||
# BLE device is available since we won't get callbacks
|
||||
# when it goes away since we HomeKit supports disconnected
|
||||
# notifications and we cannot treat a disconnect as unavailability.
|
||||
self._ble_available_interval_remover = async_track_time_interval(
|
||||
self.hass,
|
||||
self.async_update_available_state,
|
||||
timedelta(seconds=BLE_AVAILABILITY_CHECK_INTERVAL),
|
||||
entry.async_on_unload(
|
||||
async_track_time_interval(
|
||||
self.hass,
|
||||
self.async_update_available_state,
|
||||
timedelta(seconds=BLE_AVAILABILITY_CHECK_INTERVAL),
|
||||
)
|
||||
)
|
||||
# BLE devices always get an RSSI sensor as well
|
||||
if "sensor" not in self.platforms:
|
||||
await self.async_load_platform("sensor")
|
||||
|
||||
@callback
|
||||
def _async_start_polling(self) -> None:
|
||||
"""Start polling for updates."""
|
||||
# We use async_request_update to avoid multiple updates
|
||||
# at the same time which would generate a spurious warning
|
||||
# in the log about concurrent polling.
|
||||
self.config_entry.async_on_unload(
|
||||
async_track_time_interval(
|
||||
self.hass, self.async_request_update, self.pairing.poll_interval
|
||||
)
|
||||
)
|
||||
|
||||
async def async_add_new_entities(self) -> None:
|
||||
"""Add new entities to Home Assistant."""
|
||||
await self.async_load_platforms()
|
||||
|
@ -529,9 +540,6 @@ class HKDevice:
|
|||
|
||||
async def async_unload(self) -> None:
|
||||
"""Stop interacting with device and prepare for removal from hass."""
|
||||
if self._polling_interval_remover:
|
||||
self._polling_interval_remover()
|
||||
|
||||
await self.pairing.shutdown()
|
||||
|
||||
await self.hass.config_entries.async_unload_platforms(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue