mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
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
1503674bd6
commit
95e337277c
@ -106,6 +106,8 @@ class ActiveBluetoothDataUpdateCoordinator(
|
|||||||
|
|
||||||
def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool:
|
def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool:
|
||||||
"""Return true if time to try and poll."""
|
"""Return true if time to try and poll."""
|
||||||
|
if self.hass.is_stopping:
|
||||||
|
return False
|
||||||
poll_age: float | None = None
|
poll_age: float | None = None
|
||||||
if self._last_poll:
|
if self._last_poll:
|
||||||
poll_age = monotonic_time_coarse() - 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:
|
def needs_poll(self, service_info: BluetoothServiceInfoBleak) -> bool:
|
||||||
"""Return true if time to try and poll."""
|
"""Return true if time to try and poll."""
|
||||||
|
if self.hass.is_stopping:
|
||||||
|
return False
|
||||||
poll_age: float | None = None
|
poll_age: float | None = None
|
||||||
if self._last_poll:
|
if self._last_poll:
|
||||||
poll_age = monotonic_time_coarse() - self._last_poll
|
poll_age = monotonic_time_coarse() - self._last_poll
|
||||||
|
@ -19,7 +19,7 @@ from homeassistant.components.bluetooth.active_update_coordinator import (
|
|||||||
_T,
|
_T,
|
||||||
ActiveBluetoothDataUpdateCoordinator,
|
ActiveBluetoothDataUpdateCoordinator,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import CoreState, HomeAssistant
|
||||||
from homeassistant.helpers.debounce import Debouncer
|
from homeassistant.helpers.debounce import Debouncer
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -395,3 +395,58 @@ async def test_polling_rejecting_the_first_time(
|
|||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
unregister_listener()
|
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 (
|
from homeassistant.components.bluetooth.active_update_processor import (
|
||||||
ActiveBluetoothProcessorCoordinator,
|
ActiveBluetoothProcessorCoordinator,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import CoreState, HomeAssistant
|
||||||
from homeassistant.helpers.debounce import Debouncer
|
from homeassistant.helpers.debounce import Debouncer
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
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})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
||||||
|
|
||||||
cancel()
|
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…
x
Reference in New Issue
Block a user