mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Revert polling changes to HomeKit Controller (#139550)
This reverts #116200 We changed the polling logic to avoid polling if all chars are marked as watchable to avoid crashing the firmware on a very limited set of devices as it was more in line with what iOS does. In the end, the user ended up replacing the device in #116143 because it turned out to be unreliable in other ways. The vendor has since issued a firmware update that may resolve the problem with all of these devices. In practice it turns out many more devices report that chars are evented and never send events. After a few months of data and reports the trade-off does not seem worth it since users are having to set up manual polling on a wide range of devices. The amount of devices with evented chars that do not actually send state vastly exceeds the number of devices that might crash if they are polled too often so restore the previous behavior fixes #138561 fixes #100331 fixes #124529 fixes #123456 fixes #130763 fixes #124099 fixes #124916 fixes #135434 fixes #125273 fixes #124099 fixes #119617
This commit is contained in:
parent
0323a9c4e6
commit
e1ce5b8c69
@ -154,7 +154,6 @@ class HKDevice:
|
||||
self._pending_subscribes: set[tuple[int, int]] = set()
|
||||
self._subscribe_timer: CALLBACK_TYPE | None = None
|
||||
self._load_platforms_lock = asyncio.Lock()
|
||||
self._full_update_requested: bool = False
|
||||
|
||||
@property
|
||||
def entity_map(self) -> Accessories:
|
||||
@ -841,48 +840,11 @@ class HKDevice:
|
||||
|
||||
async def async_request_update(self, now: datetime | None = None) -> None:
|
||||
"""Request an debounced update from the accessory."""
|
||||
self._full_update_requested = True
|
||||
await self._debounced_update.async_call()
|
||||
|
||||
async def async_update(self, now: datetime | None = None) -> None:
|
||||
"""Poll state of all entities attached to this bridge/accessory."""
|
||||
to_poll = self.pollable_characteristics
|
||||
accessories = self.entity_map.accessories
|
||||
|
||||
if (
|
||||
not self._full_update_requested
|
||||
and len(accessories) == 1
|
||||
and self.available
|
||||
and not (to_poll - self.watchable_characteristics)
|
||||
and self.pairing.is_available
|
||||
and await self.pairing.controller.async_reachable(
|
||||
self.unique_id, timeout=5.0
|
||||
)
|
||||
):
|
||||
# If its a single accessory and all chars are watchable,
|
||||
# only poll the firmware version to keep the connection alive
|
||||
# https://github.com/home-assistant/core/issues/123412
|
||||
#
|
||||
# Firmware revision is used here since iOS does this to keep camera
|
||||
# connections alive, and the goal is to not regress
|
||||
# https://github.com/home-assistant/core/issues/116143
|
||||
# by polling characteristics that are not normally polled frequently
|
||||
# and may not be tested by the device vendor.
|
||||
#
|
||||
_LOGGER.debug(
|
||||
"Accessory is reachable, limiting poll to firmware version: %s",
|
||||
self.unique_id,
|
||||
)
|
||||
first_accessory = accessories[0]
|
||||
accessory_info = first_accessory.services.first(
|
||||
service_type=ServicesTypes.ACCESSORY_INFORMATION
|
||||
)
|
||||
assert accessory_info is not None
|
||||
firmware_iid = accessory_info[CharacteristicsTypes.FIRMWARE_REVISION].iid
|
||||
to_poll = {(first_accessory.aid, firmware_iid)}
|
||||
|
||||
self._full_update_requested = False
|
||||
|
||||
if not to_poll:
|
||||
self.async_update_available_state()
|
||||
_LOGGER.debug(
|
||||
|
@ -375,9 +375,9 @@ async def test_poll_firmware_version_only_all_watchable_accessory_mode(
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == STATE_OFF
|
||||
assert mock_get_characteristics.call_count == 2
|
||||
# Verify only firmware version is polled
|
||||
assert mock_get_characteristics.call_args_list[0][0][0] == {(1, 7)}
|
||||
assert mock_get_characteristics.call_args_list[1][0][0] == {(1, 7)}
|
||||
# Verify everything is polled
|
||||
assert mock_get_characteristics.call_args_list[0][0][0] == {(1, 10), (1, 11)}
|
||||
assert mock_get_characteristics.call_args_list[1][0][0] == {(1, 10), (1, 11)}
|
||||
|
||||
# Test device goes offline
|
||||
helper.pairing.available = False
|
||||
@ -429,8 +429,8 @@ async def test_manual_poll_all_chars(
|
||||
) as mock_get_characteristics:
|
||||
# Initial state is that the light is off
|
||||
await helper.poll_and_get_state()
|
||||
# Verify only firmware version is polled
|
||||
assert mock_get_characteristics.call_args_list[0][0][0] == {(1, 7)}
|
||||
# Verify poll polls all chars
|
||||
assert len(mock_get_characteristics.call_args_list[0][0][0]) > 1
|
||||
|
||||
# Now do a manual poll to ensure all chars are polled
|
||||
mock_get_characteristics.reset_mock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user