Restore history from bluetooth stack at startup (#78612)
parent
13d3f4c3b2
commit
18eef5da1f
|
@ -228,7 +228,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
integration_matcher = IntegrationMatcher(await async_get_bluetooth(hass))
|
||||
integration_matcher.async_setup()
|
||||
manager = BluetoothManager(hass, integration_matcher)
|
||||
manager.async_setup()
|
||||
await manager.async_setup()
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, manager.async_stop)
|
||||
hass.data[DATA_MANAGER] = models.MANAGER = manager
|
||||
adapters = await manager.async_get_bluetooth_adapters()
|
||||
|
|
|
@ -45,7 +45,7 @@ from .models import (
|
|||
BluetoothServiceInfoBleak,
|
||||
)
|
||||
from .usage import install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher
|
||||
from .util import async_get_bluetooth_adapters
|
||||
from .util import async_get_bluetooth_adapters, async_load_history_from_system
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bleak.backends.device import BLEDevice
|
||||
|
@ -213,10 +213,15 @@ class BluetoothManager:
|
|||
self._adapters = await async_get_bluetooth_adapters()
|
||||
return self._find_adapter_by_address(address)
|
||||
|
||||
@hass_callback
|
||||
def async_setup(self) -> None:
|
||||
async def async_setup(self) -> None:
|
||||
"""Set up the bluetooth manager."""
|
||||
install_multiple_bleak_catcher()
|
||||
history = await async_load_history_from_system()
|
||||
# Everything is connectable so it fall into both
|
||||
# buckets since the host system can only provide
|
||||
# connectable devices
|
||||
self._history = history.copy()
|
||||
self._connectable_history = history.copy()
|
||||
self.async_setup_unavailable_tracking()
|
||||
|
||||
@hass_callback
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"requirements": [
|
||||
"bleak==0.17.0",
|
||||
"bleak-retry-connector==1.17.1",
|
||||
"bluetooth-adapters==0.4.1",
|
||||
"bluetooth-adapters==0.5.1",
|
||||
"bluetooth-auto-recovery==0.3.3",
|
||||
"dbus-fast==1.4.0"
|
||||
],
|
||||
|
|
|
@ -19,13 +19,7 @@ from bleak.backends.device import BLEDevice
|
|||
from bleak.backends.scanner import AdvertisementData
|
||||
from dbus_fast import InvalidMessageError
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Event,
|
||||
HomeAssistant,
|
||||
callback as hass_callback,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
from homeassistant.util.package import is_docker_env
|
||||
|
@ -133,7 +127,6 @@ class HaScanner(BaseHaScanner):
|
|||
self.scanner = scanner
|
||||
self.adapter = adapter
|
||||
self._start_stop_lock = asyncio.Lock()
|
||||
self._cancel_stop: CALLBACK_TYPE | None = None
|
||||
self._cancel_watchdog: CALLBACK_TYPE | None = None
|
||||
self._last_detection = 0.0
|
||||
self._start_time = 0.0
|
||||
|
@ -318,9 +311,6 @@ class HaScanner(BaseHaScanner):
|
|||
break
|
||||
|
||||
self._async_setup_scanner_watchdog()
|
||||
self._cancel_stop = self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, self._async_hass_stopping
|
||||
)
|
||||
|
||||
@hass_callback
|
||||
def _async_setup_scanner_watchdog(self) -> None:
|
||||
|
@ -368,11 +358,6 @@ class HaScanner(BaseHaScanner):
|
|||
exc_info=True,
|
||||
)
|
||||
|
||||
async def _async_hass_stopping(self, event: Event) -> None:
|
||||
"""Stop the Bluetooth integration at shutdown."""
|
||||
self._cancel_stop = None
|
||||
await self.async_stop()
|
||||
|
||||
async def _async_reset_adapter(self) -> None:
|
||||
"""Reset the adapter."""
|
||||
# There is currently nothing the user can do to fix this
|
||||
|
@ -396,9 +381,6 @@ class HaScanner(BaseHaScanner):
|
|||
|
||||
async def _async_stop_scanner(self) -> None:
|
||||
"""Stop bluetooth discovery under the lock."""
|
||||
if self._cancel_stop:
|
||||
self._cancel_stop()
|
||||
self._cancel_stop = None
|
||||
_LOGGER.debug("%s: Stopping bluetooth discovery", self.name)
|
||||
try:
|
||||
await self.scanner.stop() # type: ignore[no-untyped-call]
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
import time
|
||||
|
||||
from bluetooth_auto_recovery import recover_adapter
|
||||
|
||||
|
@ -15,6 +16,38 @@ from .const import (
|
|||
WINDOWS_DEFAULT_BLUETOOTH_ADAPTER,
|
||||
AdapterDetails,
|
||||
)
|
||||
from .models import BluetoothServiceInfoBleak
|
||||
|
||||
|
||||
async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBleak]:
|
||||
"""Load the device and advertisement_data history if available on the current system."""
|
||||
if platform.system() != "Linux":
|
||||
return {}
|
||||
from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel
|
||||
BlueZDBusObjects,
|
||||
)
|
||||
|
||||
bluez_dbus = BlueZDBusObjects()
|
||||
await bluez_dbus.load()
|
||||
now = time.monotonic()
|
||||
return {
|
||||
address: BluetoothServiceInfoBleak(
|
||||
name=history.advertisement_data.local_name
|
||||
or history.device.name
|
||||
or history.device.address,
|
||||
address=history.device.address,
|
||||
rssi=history.device.rssi,
|
||||
manufacturer_data=history.advertisement_data.manufacturer_data,
|
||||
service_data=history.advertisement_data.service_data,
|
||||
service_uuids=history.advertisement_data.service_uuids,
|
||||
source=history.source,
|
||||
device=history.device,
|
||||
advertisement=history.advertisement_data,
|
||||
connectable=False,
|
||||
time=now,
|
||||
)
|
||||
for address, history in bluez_dbus.history.items()
|
||||
}
|
||||
|
||||
|
||||
async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
||||
|
|
|
@ -12,7 +12,7 @@ awesomeversion==22.9.0
|
|||
bcrypt==3.1.7
|
||||
bleak-retry-connector==1.17.1
|
||||
bleak==0.17.0
|
||||
bluetooth-adapters==0.4.1
|
||||
bluetooth-adapters==0.5.1
|
||||
bluetooth-auto-recovery==0.3.3
|
||||
certifi>=2021.5.30
|
||||
ciso8601==2.2.0
|
||||
|
|
|
@ -430,7 +430,7 @@ bluemaestro-ble==0.2.0
|
|||
# bluepy==1.3.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bluetooth-adapters==0.4.1
|
||||
bluetooth-adapters==0.5.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bluetooth-auto-recovery==0.3.3
|
||||
|
|
|
@ -341,7 +341,7 @@ blinkpy==0.19.2
|
|||
bluemaestro-ble==0.2.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bluetooth-adapters==0.4.1
|
||||
bluetooth-adapters==0.5.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bluetooth-auto-recovery==0.3.3
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
"""Tests for the bluetooth component."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(name="bluez_dbus_mock")
|
||||
def bluez_dbus_mock():
|
||||
"""Fixture that mocks out the bluez dbus calls."""
|
||||
# Must patch directly since this is loaded on demand only
|
||||
with patch(
|
||||
"bluetooth_adapters.BlueZDBusObjects", return_value=MagicMock(load=AsyncMock())
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="macos_adapter")
|
||||
def macos_adapter():
|
||||
"""Fixture that mocks the macos adapter."""
|
||||
|
@ -25,7 +35,7 @@ def windows_adapter():
|
|||
|
||||
|
||||
@pytest.fixture(name="one_adapter")
|
||||
def one_adapter_fixture():
|
||||
def one_adapter_fixture(bluez_dbus_mock):
|
||||
"""Fixture that mocks one adapter on Linux."""
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.platform.system", return_value="Linux"
|
||||
|
@ -54,7 +64,7 @@ def one_adapter_fixture():
|
|||
|
||||
|
||||
@pytest.fixture(name="two_adapters")
|
||||
def two_adapters_fixture():
|
||||
def two_adapters_fixture(bluez_dbus_mock):
|
||||
"""Fixture that mocks two adapters on Linux."""
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.platform.system", return_value="Linux"
|
||||
|
|
|
@ -47,7 +47,9 @@ GENERIC_BLUETOOTH_SERVICE_INFO_2 = BluetoothServiceInfo(
|
|||
)
|
||||
|
||||
|
||||
async def test_basic_usage(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||
async def test_basic_usage(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test basic usage of the ActiveBluetoothProcessorCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -92,7 +94,9 @@ async def test_basic_usage(hass: HomeAssistant, mock_bleak_scanner_start):
|
|||
cancel()
|
||||
|
||||
|
||||
async def test_poll_can_be_skipped(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||
async def test_poll_can_be_skipped(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test need_poll callback works and can skip a poll if its not needed."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -151,7 +155,7 @@ async def test_poll_can_be_skipped(hass: HomeAssistant, mock_bleak_scanner_start
|
|||
|
||||
|
||||
async def test_bleak_error_and_recover(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, caplog
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters, caplog
|
||||
):
|
||||
"""Test bleak error handling and recovery."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
@ -212,7 +216,9 @@ async def test_bleak_error_and_recover(
|
|||
cancel()
|
||||
|
||||
|
||||
async def test_poll_failure_and_recover(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||
async def test_poll_failure_and_recover(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test error handling and recovery."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -267,7 +273,9 @@ async def test_poll_failure_and_recover(hass: HomeAssistant, mock_bleak_scanner_
|
|||
cancel()
|
||||
|
||||
|
||||
async def test_second_poll_needed(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||
async def test_second_poll_needed(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""If a poll is queued, by the time it starts it may no longer be needed."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -314,7 +322,9 @@ async def test_second_poll_needed(hass: HomeAssistant, mock_bleak_scanner_start)
|
|||
cancel()
|
||||
|
||||
|
||||
async def test_rate_limit(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||
async def test_rate_limit(
|
||||
hass: HomeAssistant, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test error handling and recovery."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
|
|
@ -18,7 +18,11 @@ from tests.common import MockConfigEntry
|
|||
|
||||
|
||||
async def test_options_flow_disabled_not_setup(
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, macos_adapter
|
||||
hass,
|
||||
hass_ws_client,
|
||||
mock_bleak_scanner_start,
|
||||
mock_bluetooth_adapters,
|
||||
macos_adapter,
|
||||
):
|
||||
"""Test options are disabled if the integration has not been setup."""
|
||||
await async_setup_component(hass, "config", {})
|
||||
|
@ -38,6 +42,7 @@ async def test_options_flow_disabled_not_setup(
|
|||
)
|
||||
response = await ws_client.receive_json()
|
||||
assert response["result"][0]["supports_options"] is False
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
||||
|
||||
async def test_async_step_user_macos(hass, macos_adapter):
|
||||
|
@ -262,7 +267,9 @@ async def test_async_step_integration_discovery_already_exists(hass):
|
|||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_options_flow_linux(hass, mock_bleak_scanner_start, one_adapter):
|
||||
async def test_options_flow_linux(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters, one_adapter
|
||||
):
|
||||
"""Test options on Linux."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -308,10 +315,15 @@ async def test_options_flow_linux(hass, mock_bleak_scanner_start, one_adapter):
|
|||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_PASSIVE] is False
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
||||
|
||||
async def test_options_flow_disabled_macos(
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, macos_adapter
|
||||
hass,
|
||||
hass_ws_client,
|
||||
mock_bleak_scanner_start,
|
||||
mock_bluetooth_adapters,
|
||||
macos_adapter,
|
||||
):
|
||||
"""Test options are disabled on MacOS."""
|
||||
await async_setup_component(hass, "config", {})
|
||||
|
@ -334,10 +346,11 @@ async def test_options_flow_disabled_macos(
|
|||
)
|
||||
response = await ws_client.receive_json()
|
||||
assert response["result"][0]["supports_options"] is False
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
||||
|
||||
async def test_options_flow_enabled_linux(
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, one_adapter
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, mock_bluetooth_adapters, one_adapter
|
||||
):
|
||||
"""Test options are enabled on Linux."""
|
||||
await async_setup_component(hass, "config", {})
|
||||
|
@ -363,3 +376,4 @@ async def test_options_flow_enabled_linux(
|
|||
)
|
||||
response = await ws_client.receive_json()
|
||||
assert response["result"][0]["supports_options"] is True
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
|
|
@ -2446,7 +2446,7 @@ async def test_auto_detect_bluetooth_adapters_linux_multiple(hass, two_adapters)
|
|||
assert len(hass.config_entries.flow.async_progress(bluetooth.DOMAIN)) == 2
|
||||
|
||||
|
||||
async def test_auto_detect_bluetooth_adapters_linux_none_found(hass):
|
||||
async def test_auto_detect_bluetooth_adapters_linux_none_found(hass, bluez_dbus_mock):
|
||||
"""Test we auto detect bluetooth adapters on linux with no adapters found."""
|
||||
with patch(
|
||||
"bluetooth_adapters.get_bluetooth_adapter_details", return_value={}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
"""Tests for the Bluetooth integration manager."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from bleak.backends.scanner import AdvertisementData, BLEDevice
|
||||
from bluetooth_adapters import AdvertisementHistory
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth.manager import STALE_ADVERTISEMENT_SECONDS
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import (
|
||||
inject_advertisement_with_source,
|
||||
|
@ -176,3 +179,24 @@ async def test_switching_adapters_based_on_stale(hass, enable_bluetooth):
|
|||
bluetooth.async_ble_device_from_address(hass, address)
|
||||
is switchbot_device_poor_signal_hci1
|
||||
)
|
||||
|
||||
|
||||
async def test_restore_history_from_dbus(hass, one_adapter):
|
||||
"""Test we can restore history from dbus."""
|
||||
address = "AA:BB:CC:CC:CC:FF"
|
||||
|
||||
ble_device = BLEDevice(address, "name")
|
||||
history = {
|
||||
address: AdvertisementHistory(
|
||||
ble_device, AdvertisementData(local_name="name"), "hci0"
|
||||
)
|
||||
}
|
||||
|
||||
with patch(
|
||||
"bluetooth_adapters.BlueZDBusObjects",
|
||||
return_value=MagicMock(load=AsyncMock(), history=history),
|
||||
):
|
||||
assert await async_setup_component(hass, bluetooth.DOMAIN, {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert bluetooth.async_ble_device_from_address(hass, address) is ble_device
|
||||
|
|
|
@ -59,7 +59,7 @@ class MyCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
|||
super()._async_handle_bluetooth_event(service_info, change)
|
||||
|
||||
|
||||
async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||
async def test_basic_usage(hass, mock_bleak_scanner_start, mock_bluetooth_adapters):
|
||||
"""Test basic usage of the PassiveBluetoothDataUpdateCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
coordinator = MyCoordinator(
|
||||
|
@ -88,7 +88,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
|||
|
||||
|
||||
async def test_context_compatiblity_with_data_update_coordinator(
|
||||
hass, mock_bleak_scanner_start
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test contexts can be passed for compatibility with DataUpdateCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
@ -124,7 +124,7 @@ async def test_context_compatiblity_with_data_update_coordinator(
|
|||
|
||||
|
||||
async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
|
||||
hass, mock_bleak_scanner_start
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test that the coordinator goes unavailable when the bluetooth stack no longer sees the device."""
|
||||
with patch(
|
||||
|
@ -165,7 +165,9 @@ async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
|
|||
assert coordinator.available is False
|
||||
|
||||
|
||||
async def test_passive_bluetooth_coordinator_entity(hass, mock_bleak_scanner_start):
|
||||
async def test_passive_bluetooth_coordinator_entity(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test integration of PassiveBluetoothDataUpdateCoordinator with PassiveBluetoothCoordinatorEntity."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
coordinator = MyCoordinator(
|
||||
|
|
|
@ -98,7 +98,7 @@ GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
|||
)
|
||||
|
||||
|
||||
async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||
async def test_basic_usage(hass, mock_bleak_scanner_start, mock_bluetooth_adapters):
|
||||
"""Test basic usage of the PassiveBluetoothProcessorCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -196,7 +196,9 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
|||
cancel_coordinator()
|
||||
|
||||
|
||||
async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
||||
async def test_unavailable_after_no_data(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test that the coordinator is unavailable after no data for a while."""
|
||||
with patch(
|
||||
"bleak.BleakScanner.discovered_devices", # Must patch before we setup
|
||||
|
@ -290,7 +292,9 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
|||
cancel_coordinator()
|
||||
|
||||
|
||||
async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
||||
async def test_no_updates_once_stopping(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test updates are ignored once hass is stopping."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -343,7 +347,9 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
|||
cancel_coordinator()
|
||||
|
||||
|
||||
async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_start):
|
||||
async def test_exception_from_update_method(
|
||||
hass, caplog, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test we handle exceptions from the update method."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -406,7 +412,9 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
|||
cancel_coordinator()
|
||||
|
||||
|
||||
async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
||||
async def test_bad_data_from_update_method(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test we handle bad data from the update method."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -758,7 +766,9 @@ GOVEE_B5178_PRIMARY_AND_REMOTE_PASSIVE_BLUETOOTH_DATA_UPDATE = (
|
|||
)
|
||||
|
||||
|
||||
async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
||||
async def test_integration_with_entity(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test integration of PassiveBluetoothProcessorCoordinator with PassiveBluetoothCoordinatorEntity."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -888,7 +898,9 @@ NO_DEVICES_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
|||
)
|
||||
|
||||
|
||||
async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner_start):
|
||||
async def test_integration_with_entity_without_a_device(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test integration with PassiveBluetoothCoordinatorEntity with no device."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -950,7 +962,7 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
|||
|
||||
|
||||
async def test_passive_bluetooth_entity_with_entity_platform(
|
||||
hass, mock_bleak_scanner_start
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test with a mock entity platform."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
@ -1048,7 +1060,9 @@ BINARY_SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
|||
)
|
||||
|
||||
|
||||
async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_start):
|
||||
async def test_integration_multiple_entity_platforms(
|
||||
hass, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test integration of PassiveBluetoothProcessorCoordinator with multiple platforms."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
|
@ -1138,7 +1152,7 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
|||
|
||||
|
||||
async def test_exception_from_coordinator_update_method(
|
||||
hass, caplog, mock_bleak_scanner_start
|
||||
hass, caplog, mock_bleak_scanner_start, mock_bluetooth_adapters
|
||||
):
|
||||
"""Test we handle exceptions from the update method."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
|
|
@ -991,6 +991,8 @@ def mock_bluetooth_adapters():
|
|||
"""Fixture to mock bluetooth adapters."""
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.util.platform.system", return_value="Linux"
|
||||
), patch(
|
||||
"bluetooth_adapters.BlueZDBusObjects", return_value=MagicMock(load=AsyncMock())
|
||||
), patch(
|
||||
"bluetooth_adapters.get_bluetooth_adapter_details",
|
||||
return_value={
|
||||
|
|
Loading…
Reference in New Issue