mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Ensure HomeKit connection is kept alive for devices that timeout too quickly (#123601)
This commit is contained in:
parent
f6e82ae0ba
commit
b20623447e
@ -845,21 +845,41 @@ class HKDevice:
|
||||
|
||||
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 (
|
||||
len(self.entity_map.accessories) == 1
|
||||
len(accessories) == 1
|
||||
and self.available
|
||||
and not (self.pollable_characteristics - self.watchable_characteristics)
|
||||
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,
|
||||
# we don't need to poll.
|
||||
_LOGGER.debug("Accessory is reachable, skip polling: %s", self.unique_id)
|
||||
return
|
||||
# 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)}
|
||||
|
||||
if not self.pollable_characteristics:
|
||||
if not to_poll:
|
||||
self.async_update_available_state()
|
||||
_LOGGER.debug(
|
||||
"HomeKit connection not polling any characteristics: %s", self.unique_id
|
||||
@ -892,9 +912,7 @@ class HKDevice:
|
||||
_LOGGER.debug("Starting HomeKit device update: %s", self.unique_id)
|
||||
|
||||
try:
|
||||
new_values_dict = await self.get_characteristics(
|
||||
self.pollable_characteristics
|
||||
)
|
||||
new_values_dict = await self.get_characteristics(to_poll)
|
||||
except AccessoryNotFoundError:
|
||||
# Not only did the connection fail, but also the accessory is not
|
||||
# visible on the network.
|
||||
|
@ -344,10 +344,10 @@ async def test_thread_provision_migration_failed(hass: HomeAssistant) -> None:
|
||||
assert config_entry.data["Connection"] == "BLE"
|
||||
|
||||
|
||||
async def test_skip_polling_all_watchable_accessory_mode(
|
||||
async def test_poll_firmware_version_only_all_watchable_accessory_mode(
|
||||
hass: HomeAssistant, get_next_aid: Callable[[], int]
|
||||
) -> None:
|
||||
"""Test that we skip polling if available and all chars are watchable accessory mode."""
|
||||
"""Test that we only poll firmware if available and all chars are watchable accessory mode."""
|
||||
|
||||
def _create_accessory(accessory):
|
||||
service = accessory.add_service(ServicesTypes.LIGHTBULB, name="TestDevice")
|
||||
@ -370,7 +370,10 @@ async def test_skip_polling_all_watchable_accessory_mode(
|
||||
# Initial state is that the light is off
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == STATE_OFF
|
||||
assert mock_get_characteristics.call_count == 0
|
||||
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)}
|
||||
|
||||
# Test device goes offline
|
||||
helper.pairing.available = False
|
||||
@ -382,16 +385,16 @@ async def test_skip_polling_all_watchable_accessory_mode(
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
# Tries twice before declaring unavailable
|
||||
assert mock_get_characteristics.call_count == 2
|
||||
assert mock_get_characteristics.call_count == 4
|
||||
|
||||
# Test device comes back online
|
||||
helper.pairing.available = True
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == STATE_OFF
|
||||
assert mock_get_characteristics.call_count == 3
|
||||
assert mock_get_characteristics.call_count == 6
|
||||
|
||||
# Next poll should not happen because its a single
|
||||
# accessory, available, and all chars are watchable
|
||||
state = await helper.poll_and_get_state()
|
||||
assert state.state == STATE_OFF
|
||||
assert mock_get_characteristics.call_count == 3
|
||||
assert mock_get_characteristics.call_count == 8
|
||||
|
Loading…
x
Reference in New Issue
Block a user