Avoid starting a bluetooth poll when Home Assistant is stopping (#88819)
* Avoid starting a bluetooth poll when Home Assistant is stopping * tests
This commit is contained in:
parent
f8934175cb
commit
1d1c553d9b
4 changed files with 123 additions and 2 deletions
|
@ -106,6 +106,8 @@ class ActiveBluetoothDataUpdateCoordinator(
|
|||
|
||||
def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool:
|
||||
"""Return true if time to try and poll."""
|
||||
if self.hass.is_stopping:
|
||||
return False
|
||||
poll_age: float | None = None
|
||||
if self._last_poll:
|
||||
poll_age = monotonic_time_coarse() - self._last_poll
|
||||
|
|
|
@ -99,6 +99,8 @@ class ActiveBluetoothProcessorCoordinator(
|
|||
|
||||
def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool:
|
||||
"""Return true if time to try and poll."""
|
||||
if self.hass.is_stopping:
|
||||
return False
|
||||
poll_age: float | None = None
|
||||
if self._last_poll:
|
||||
poll_age = monotonic_time_coarse() - self._last_poll
|
||||
|
|
|
@ -19,7 +19,7 @@ from homeassistant.components.bluetooth.active_update_coordinator import (
|
|||
_T,
|
||||
ActiveBluetoothDataUpdateCoordinator,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
@ -395,3 +395,58 @@ async def test_polling_rejecting_the_first_time(
|
|||
|
||||
cancel()
|
||||
unregister_listener()
|
||||
|
||||
|
||||
async def test_no_polling_after_stop_event(
|
||||
hass: HomeAssistant,
|
||||
mock_bleak_scanner_start: MagicMock,
|
||||
mock_bluetooth_adapters: None,
|
||||
) -> None:
|
||||
"""Test we do not poll after the stop event."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
needs_poll_calls = 0
|
||||
|
||||
def _needs_poll(
|
||||
service_info: BluetoothServiceInfoBleak, seconds_since_last_poll: float | None
|
||||
) -> bool:
|
||||
nonlocal needs_poll_calls
|
||||
needs_poll_calls += 1
|
||||
return True
|
||||
|
||||
async def _poll_method(service_info: BluetoothServiceInfoBleak) -> dict[str, Any]:
|
||||
return {"fake": "data"}
|
||||
|
||||
coordinator = MyCoordinator(
|
||||
hass=hass,
|
||||
logger=_LOGGER,
|
||||
address="aa:bb:cc:dd:ee:ff",
|
||||
mode=BluetoothScanningMode.ACTIVE,
|
||||
needs_poll_method=_needs_poll,
|
||||
poll_method=_poll_method,
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
|
||||
mock_listener = MagicMock()
|
||||
unregister_listener = coordinator.async_add_listener(mock_listener)
|
||||
|
||||
cancel = coordinator.async_start()
|
||||
assert needs_poll_calls == 0
|
||||
|
||||
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||
await hass.async_block_till_done()
|
||||
assert coordinator.passive_data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
|
||||
assert coordinator.data == {"fake": "data"}
|
||||
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
hass.state = CoreState.stopping
|
||||
await hass.async_block_till_done()
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
# Should not generate a poll now
|
||||
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
|
||||
await hass.async_block_till_done()
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
cancel()
|
||||
unregister_listener()
|
||||
|
|
|
@ -16,7 +16,7 @@ from homeassistant.components.bluetooth import (
|
|||
from homeassistant.components.bluetooth.active_update_processor import (
|
||||
ActiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
@ -384,3 +384,65 @@ async def test_rate_limit(
|
|||
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
||||
|
||||
cancel()
|
||||
|
||||
|
||||
async def test_no_polling_after_stop_event(
|
||||
hass: HomeAssistant,
|
||||
mock_bleak_scanner_start: MagicMock,
|
||||
mock_bluetooth_adapters: None,
|
||||
) -> None:
|
||||
"""Test we do not poll after the stop event."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
needs_poll_calls = 0
|
||||
|
||||
def _update_method(service_info: BluetoothServiceInfoBleak):
|
||||
return {"testdata": 0}
|
||||
|
||||
def _poll_needed(*args, **kwargs):
|
||||
nonlocal needs_poll_calls
|
||||
needs_poll_calls += 1
|
||||
return True
|
||||
|
||||
async def _poll(*args, **kwargs):
|
||||
return {"testdata": 1}
|
||||
|
||||
coordinator = ActiveBluetoothProcessorCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
address="aa:bb:cc:dd:ee:ff",
|
||||
mode=BluetoothScanningMode.ACTIVE,
|
||||
update_method=_update_method,
|
||||
needs_poll_method=_poll_needed,
|
||||
poll_method=_poll,
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
|
||||
processor = MagicMock()
|
||||
coordinator.async_register_processor(processor)
|
||||
async_handle_update = processor.async_handle_update
|
||||
|
||||
cancel = coordinator.async_start()
|
||||
|
||||
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||
await hass.async_block_till_done()
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
assert coordinator.available is True
|
||||
|
||||
# async_handle_update should have been called twice
|
||||
# The first time, it was passed the data from parsing the advertisement
|
||||
# The second time, it was passed the data from polling
|
||||
assert len(async_handle_update.mock_calls) == 2
|
||||
assert async_handle_update.mock_calls[0] == call({"testdata": 0})
|
||||
assert async_handle_update.mock_calls[1] == call({"testdata": 1})
|
||||
|
||||
hass.state = CoreState.stopping
|
||||
await hass.async_block_till_done()
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
# Should not generate a poll now that CoreState is stopping
|
||||
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO_2)
|
||||
await hass.async_block_till_done()
|
||||
assert needs_poll_calls == 1
|
||||
|
||||
cancel()
|
||||
|
|
Loading…
Add table
Reference in a new issue