Make Bluetooth active coordinator debouncers run tasks in the background (#113129)

This commit is contained in:
J. Nick Koston 2024-03-12 07:43:43 -10:00 committed by GitHub
parent 4c2a54746d
commit c948392ebc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 33 additions and 32 deletions

View File

@ -99,6 +99,7 @@ class ActiveBluetoothDataUpdateCoordinator(
cooldown=POLL_DEFAULT_COOLDOWN, cooldown=POLL_DEFAULT_COOLDOWN,
immediate=POLL_DEFAULT_IMMEDIATE, immediate=POLL_DEFAULT_IMMEDIATE,
function=self._async_poll, function=self._async_poll,
background=True,
) )
else: else:
poll_debouncer.function = self._async_poll poll_debouncer.function = self._async_poll

View File

@ -92,6 +92,7 @@ class ActiveBluetoothProcessorCoordinator(
cooldown=POLL_DEFAULT_COOLDOWN, cooldown=POLL_DEFAULT_COOLDOWN,
immediate=POLL_DEFAULT_IMMEDIATE, immediate=POLL_DEFAULT_IMMEDIATE,
function=self._async_poll, function=self._async_poll,
background=True,
) )
else: else:
poll_debouncer.function = self._async_poll poll_debouncer.function = self._async_poll

View File

@ -129,7 +129,7 @@ async def test_basic_usage(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
@ -175,13 +175,13 @@ async def test_bleak_error_during_polling(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
assert coordinator.data is None assert coordinator.data is None
assert coordinator.last_poll_successful is False assert coordinator.last_poll_successful is False
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi}
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
assert coordinator.last_poll_successful is True assert coordinator.last_poll_successful is True
@ -228,13 +228,13 @@ async def test_generic_exception_during_polling(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
assert coordinator.data is None assert coordinator.data is None
assert coordinator.last_poll_successful is False assert coordinator.last_poll_successful is False
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi}
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
assert coordinator.last_poll_successful is True assert coordinator.last_poll_successful is True
@ -280,7 +280,7 @@ async def test_polling_debounce(
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
# We should only get one poll because of the debounce # We should only get one poll because of the debounce
assert coordinator.data == {"poll_count": 1} assert coordinator.data == {"poll_count": 1}
@ -316,7 +316,9 @@ async def test_polling_debounce_with_custom_debouncer(
mode=BluetoothScanningMode.ACTIVE, mode=BluetoothScanningMode.ACTIVE,
needs_poll_method=_needs_poll, needs_poll_method=_needs_poll,
poll_method=_poll_method, poll_method=_poll_method,
poll_debouncer=Debouncer(hass, _LOGGER, cooldown=0.1, immediate=True), poll_debouncer=Debouncer(
hass, _LOGGER, cooldown=0.1, immediate=True, background=True
),
) )
assert coordinator.available is False # no data yet assert coordinator.available is False # no data yet
@ -327,7 +329,7 @@ async def test_polling_debounce_with_custom_debouncer(
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
# We should only get one poll because of the debounce # We should only get one poll because of the debounce
assert coordinator.data == {"poll_count": 1} assert coordinator.data == {"poll_count": 1}
@ -371,25 +373,25 @@ async def test_polling_rejecting_the_first_time(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
# First poll is rejected, so no data yet # First poll is rejected, so no data yet
assert coordinator.data is None assert coordinator.data is None
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
# Data is the same so no poll check # Data is the same so no poll check
assert coordinator.data is None assert coordinator.data is None
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO_2.rssi}
# Data is different so poll is done # Data is different so poll is done
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
# Data is different again so poll is done # Data is different again so poll is done
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
@ -434,19 +436,19 @@ async def test_no_polling_after_stop_event(
assert needs_poll_calls == 0 assert needs_poll_calls == 0
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi} assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
assert coordinator.data == {"fake": "data"} assert coordinator.data == {"fake": "data"}
assert needs_poll_calls == 1 assert needs_poll_calls == 1
hass.set_state(CoreState.stopping) hass.set_state(CoreState.stopping)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert needs_poll_calls == 1 assert needs_poll_calls == 1
# Should not generate a poll now # Should not generate a poll now
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert needs_poll_calls == 1 assert needs_poll_calls == 1
cancel() cancel()

View File

@ -84,7 +84,7 @@ async def test_basic_usage(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert coordinator.available is True assert coordinator.available is True
@ -127,10 +127,7 @@ async def test_poll_can_be_skipped(
needs_poll_method=_poll_needed, needs_poll_method=_poll_needed,
poll_method=_poll, poll_method=_poll,
poll_debouncer=Debouncer( poll_debouncer=Debouncer(
hass, hass, _LOGGER, cooldown=0, immediate=True, background=True
_LOGGER,
cooldown=0,
immediate=True,
), ),
) )
assert coordinator.available is False # no data yet assert coordinator.available is False # no data yet
@ -142,19 +139,19 @@ async def test_poll_can_be_skipped(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": True}) assert async_handle_update.mock_calls[-1] == call({"testdata": True})
flag = False flag = False
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": None}, True) assert async_handle_update.mock_calls[-1] == call({"testdata": None}, True)
flag = True flag = True
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": True}) assert async_handle_update.mock_calls[-1] == call({"testdata": True})
cancel() cancel()
@ -208,7 +205,7 @@ async def test_bleak_error_and_recover(
# First poll fails # First poll fails
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": None}, False) assert async_handle_update.mock_calls[-1] == call({"testdata": None}, False)
assert ( assert (
@ -219,7 +216,7 @@ async def test_bleak_error_and_recover(
# Second poll works # Second poll works
flag = False flag = False
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": False}) assert async_handle_update.mock_calls[-1] == call({"testdata": False})
cancel() cancel()
@ -272,13 +269,13 @@ async def test_poll_failure_and_recover(
# First poll fails # First poll fails
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": None}, False) assert async_handle_update.mock_calls[-1] == call({"testdata": None}, False)
# Second poll works # Second poll works
flag = False flag = False
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": False}) assert async_handle_update.mock_calls[-1] == call({"testdata": False})
cancel() cancel()
@ -329,7 +326,7 @@ async def test_second_poll_needed(
# Second poll gets stuck behind first poll # Second poll gets stuck behind first poll
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[1] == call({"testdata": 1}) assert async_handle_update.mock_calls[1] == call({"testdata": 1})
cancel() cancel()
@ -381,7 +378,7 @@ async def test_rate_limit(
# Third poll gets stuck behind first poll doesn't get queued # Third poll gets stuck behind first poll doesn't get queued
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert async_handle_update.mock_calls[-1] == call({"testdata": 1}) assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
cancel() cancel()
@ -425,7 +422,7 @@ async def test_no_polling_after_stop_event(
cancel = coordinator.async_start() cancel = coordinator.async_start()
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert needs_poll_calls == 1 assert needs_poll_calls == 1
assert coordinator.available is True assert coordinator.available is True
@ -438,12 +435,12 @@ async def test_no_polling_after_stop_event(
assert async_handle_update.mock_calls[1] == call({"testdata": 1}) assert async_handle_update.mock_calls[1] == call({"testdata": 1})
hass.set_state(CoreState.stopping) hass.set_state(CoreState.stopping)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert needs_poll_calls == 1 assert needs_poll_calls == 1
# Should not generate a poll now that CoreState is stopping # Should not generate a poll now that CoreState is stopping
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2) inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
await hass.async_block_till_done() await hass.async_block_till_done(wait_background_tasks=True)
assert needs_poll_calls == 1 assert needs_poll_calls == 1
cancel() cancel()