Poll HomeKit Controller locks for state after lock operation (#82058)

This commit is contained in:
J. Nick Koston 2022-11-14 10:54:28 -06:00 committed by GitHub
parent bbda122c99
commit 1ded3ac51e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 39 additions and 2 deletions

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import asyncio
from collections.abc import Callable, Iterable
from datetime import timedelta
from datetime import datetime, timedelta
import logging
from types import MappingProxyType
from typing import Any
@ -22,6 +22,7 @@ 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.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
@ -29,6 +30,7 @@ from homeassistant.helpers.event import async_track_time_interval
from .const import (
CHARACTERISTIC_PLATFORMS,
CONTROLLER,
DEBOUNCE_COOLDOWN,
DOMAIN,
HOMEKIT_ACCESSORY_DISPATCH,
IDENTIFIER_ACCESSORY_ID,
@ -41,6 +43,8 @@ from .device_trigger import async_fire_triggers, async_setup_triggers_for_entry
RETRY_INTERVAL = 60 # seconds
MAX_POLL_FAILURES_TO_DECLARE_UNAVAILABLE = 3
BLE_AVAILABILITY_CHECK_INTERVAL = 1800 # seconds
_LOGGER = logging.getLogger(__name__)
@ -127,6 +131,14 @@ class HKDevice:
self.watchable_characteristics: list[tuple[int, int]] = []
self._debounced_update = Debouncer(
hass,
_LOGGER,
cooldown=DEBOUNCE_COOLDOWN,
immediate=False,
function=self.async_update,
)
@property
def entity_map(self) -> Accessories:
"""Return the accessories from the pairing."""
@ -240,8 +252,11 @@ 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_update, self.pairing.poll_interval
self.hass, self.async_request_update, self.pairing.poll_interval
)
if transport == Transport.BLE:
@ -631,6 +646,10 @@ class HKDevice:
"""Update the available state of the device."""
self.async_set_available_state(self.pairing.is_available)
async def async_request_update(self, now: datetime | None = None) -> None:
"""Request an debounced update from the accessory."""
await self._debounced_update.async_call()
async def async_update(self, now=None):
"""Poll state of all entities attached to this bridge/accessory."""
if not self.pollable_characteristics:

View file

@ -107,3 +107,10 @@ STARTUP_EXCEPTIONS = (
EncryptionError,
AccessoryDisconnectedError,
)
# 10 seconds was chosen because its soon enough
# for most state changes to happen but not too
# long that the BLE connection is dropped. It
# also happens to be the same value used by
# the update coordinator.
DEBOUNCE_COOLDOWN = 10 # seconds

View file

@ -175,6 +175,10 @@ class HomeKitEntity(Entity):
"""Define the homekit characteristics the entity cares about."""
raise NotImplementedError
async def async_update(self) -> None:
"""Update the entity."""
await self._accessory.async_request_update()
class AccessoryEntity(HomeKitEntity):
"""A HomeKit entity that is related to an entire accessory rather than a specific service or characteristic."""

View file

@ -124,6 +124,10 @@ class HomeKitLock(HomeKitEntity, LockEntity):
await self.async_put_characteristics(
{CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE: TARGET_STATE_MAP[state]}
)
# Some locks need to be polled to update the current state
# after a target state change.
# https://github.com/home-assistant/core/issues/81887
await self._accessory.async_request_update()
@property
def extra_state_attributes(self) -> dict[str, Any]:

View file

@ -21,6 +21,7 @@ from aiohomekit.zeroconf import HomeKitService
from homeassistant.components.device_automation import DeviceAutomationType
from homeassistant.components.homekit_controller.const import (
CONTROLLER,
DEBOUNCE_COOLDOWN,
DOMAIN,
HOMEKIT_ACCESSORY_DISPATCH,
IDENTIFIER_ACCESSORY_ID,
@ -146,6 +147,7 @@ class Helper:
# If they are enabled, then HA will pick up the changes next time
# we yield control
await time_changed(self.hass, 60)
await time_changed(self.hass, DEBOUNCE_COOLDOWN)
await self.hass.async_block_till_done()
@ -165,6 +167,7 @@ class Helper:
async def poll_and_get_state(self) -> State:
"""Trigger a time based poll and return the current entity state."""
await time_changed(self.hass, 60)
await time_changed(self.hass, DEBOUNCE_COOLDOWN)
state = self.hass.states.get(self.entity_id)
assert state is not None