From 47c66dbed4b11b5dbea25e91ec027f6bb940b66b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 17 Nov 2022 13:34:19 -0600 Subject: [PATCH] Offload platform specific bluetooth code to bluetooth-adapters (#82196) * Offload platform specific bluetooth code to bluetooth-adapters * adjust * fix some more patch targets * more test fixes * almost there * may not be setup yet * more fixes * fixes * fix test * fix merge --- .../components/bluetooth/__init__.py | 25 ++--- .../components/bluetooth/config_flow.py | 21 ++-- homeassistant/components/bluetooth/const.py | 29 +----- homeassistant/components/bluetooth/manager.py | 25 +++-- .../components/bluetooth/manifest.json | 2 +- homeassistant/components/bluetooth/scanner.py | 4 +- homeassistant/components/bluetooth/util.py | 81 ++------------- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bluetooth/__init__.py | 2 +- tests/components/bluetooth/conftest.py | 99 ++++++++----------- .../components/bluetooth/test_config_flow.py | 4 +- .../components/bluetooth/test_diagnostics.py | 18 ++-- tests/components/bluetooth/test_init.py | 79 +++++++-------- tests/components/bluetooth/test_manager.py | 6 +- tests/conftest.py | 19 ++-- 17 files changed, 158 insertions(+), 262 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index e3f62280661..9a8cf115a8e 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -10,6 +10,16 @@ from typing import TYPE_CHECKING, cast import async_timeout from awesomeversion import AwesomeVersion +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + ADAPTER_HW_VERSION, + ADAPTER_SW_VERSION, + DEFAULT_ADDRESS, + AdapterDetails, + adapter_human_name, + adapter_unique_name, + get_adapters, +) from homeassistant.components import usb from homeassistant.config_entries import ( @@ -32,20 +42,15 @@ from homeassistant.loader import async_get_bluetooth from . import models from .const import ( - ADAPTER_ADDRESS, - ADAPTER_HW_VERSION, - ADAPTER_SW_VERSION, BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DATA_MANAGER, - DEFAULT_ADDRESS, DOMAIN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, - AdapterDetails, ) from .manager import BluetoothManager from .match import BluetoothCallbackMatcher, IntegrationMatcher @@ -62,7 +67,6 @@ from .models import ( ProcessAdvertisementCallback, ) from .scanner import HaScanner, ScannerStartError -from .util import adapter_human_name, adapter_unique_name, async_default_adapter if TYPE_CHECKING: from bleak.backends.device import BLEDevice @@ -288,13 +292,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the bluetooth integration.""" integration_matcher = IntegrationMatcher(await async_get_bluetooth(hass)) integration_matcher.async_setup() - manager = BluetoothManager(hass, integration_matcher) + bluetooth_adapters = get_adapters() + manager = BluetoothManager(hass, integration_matcher, bluetooth_adapters) 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() - async_migrate_entries(hass, adapters) + async_migrate_entries(hass, adapters, bluetooth_adapters.default_adapter) await async_discover_adapters(hass, adapters) async def _async_rediscover_adapters() -> None: @@ -347,17 +352,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: EVENT_HOMEASSISTANT_STARTED, hass_callback(lambda event: _async_check_haos(hass)), ) - return True @hass_callback def async_migrate_entries( - hass: HomeAssistant, adapters: dict[str, AdapterDetails] + hass: HomeAssistant, adapters: dict[str, AdapterDetails], default_adapter: str ) -> None: """Migrate config entries to support multiple.""" current_entries = hass.config_entries.async_entries(DOMAIN) - default_adapter = async_default_adapter() for entry in current_entries: if entry.unique_id: diff --git a/homeassistant/components/bluetooth/config_flow.py b/homeassistant/components/bluetooth/config_flow.py index 324520a8b5b..ffebce2e521 100644 --- a/homeassistant/components/bluetooth/config_flow.py +++ b/homeassistant/components/bluetooth/config_flow.py @@ -3,6 +3,13 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, cast +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + AdapterDetails, + adapter_human_name, + adapter_unique_name, + get_adapters, +) import voluptuous as vol from homeassistant.components import onboarding @@ -11,15 +18,7 @@ from homeassistant.core import callback from homeassistant.helpers.typing import DiscoveryInfoType from . import models -from .const import ( - ADAPTER_ADDRESS, - CONF_ADAPTER, - CONF_DETAILS, - CONF_PASSIVE, - DOMAIN, - AdapterDetails, -) -from .util import adapter_human_name, adapter_unique_name, async_get_bluetooth_adapters +from .const import CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, DOMAIN if TYPE_CHECKING: from homeassistant.data_entry_flow import FlowResult @@ -87,7 +86,9 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN): ) configured_addresses = self._async_current_ids() - self._adapters = await async_get_bluetooth_adapters() + bluetooth_adapters = get_adapters() + await bluetooth_adapters.refresh() + self._adapters = bluetooth_adapters.adapters unconfigured_adapters = [ adapter for adapter, details in self._adapters.items() diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index 038c2b1988f..d44858107ab 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import timedelta -from typing import Final, TypedDict +from typing import Final DOMAIN = "bluetooth" @@ -10,18 +10,6 @@ CONF_ADAPTER = "adapter" CONF_DETAILS = "details" CONF_PASSIVE = "passive" -WINDOWS_DEFAULT_BLUETOOTH_ADAPTER = "bluetooth" -MACOS_DEFAULT_BLUETOOTH_ADAPTER = "Core Bluetooth" -UNIX_DEFAULT_BLUETOOTH_ADAPTER = "hci0" - -DEFAULT_ADAPTER_BY_PLATFORM = { - "Windows": WINDOWS_DEFAULT_BLUETOOTH_ADAPTER, - "Darwin": MACOS_DEFAULT_BLUETOOTH_ADAPTER, -} - - -# Some operating systems hide the adapter address for privacy reasons (ex MacOS) -DEFAULT_ADDRESS: Final = "00:00:00:00:00:00" SOURCE_LOCAL: Final = "local" @@ -66,18 +54,3 @@ SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30) # are not present LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS = 120 BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS = 5 - - -class AdapterDetails(TypedDict, total=False): - """Adapter details.""" - - address: str - sw_version: str - hw_version: str | None - passive_scan: bool - - -ADAPTER_ADDRESS: Final = "address" -ADAPTER_SW_VERSION: Final = "sw_version" -ADAPTER_HW_VERSION: Final = "hw_version" -ADAPTER_PASSIVE_SCAN: Final = "passive_scan" diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index c216abde4ea..535ee5e3716 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -10,6 +10,12 @@ from typing import TYPE_CHECKING, Any, Final from bleak.backends.scanner import AdvertisementDataCallback from bleak_retry_connector import NO_RSSI_VALUE, RSSI_SWITCH_THRESHOLD +from bluetooth_adapters import ( + ADAPTER_ADDRESS, + ADAPTER_PASSIVE_SCAN, + AdapterDetails, + BluetoothAdapters, +) from homeassistant import config_entries from homeassistant.core import ( @@ -24,11 +30,8 @@ from homeassistant.util.dt import monotonic_time_coarse from .advertisement_tracker import AdvertisementTracker from .const import ( - ADAPTER_ADDRESS, - ADAPTER_PASSIVE_SCAN, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS, UNAVAILABLE_TRACK_SECONDS, - AdapterDetails, ) from .match import ( ADDRESS, @@ -47,7 +50,7 @@ from .models import ( BluetoothServiceInfoBleak, ) from .usage import install_multiple_bleak_catcher, uninstall_multiple_bleak_catcher -from .util import async_get_bluetooth_adapters, async_load_history_from_system +from .util import async_load_history_from_system if TYPE_CHECKING: from bleak.backends.device import BLEDevice @@ -102,6 +105,7 @@ class BluetoothManager: self, hass: HomeAssistant, integration_matcher: IntegrationMatcher, + bluetooth_adapters: BluetoothAdapters, ) -> None: """Init bluetooth manager.""" self.hass = hass @@ -127,6 +131,7 @@ class BluetoothManager: self._connectable_scanners: list[BaseHaScanner] = [] self._adapters: dict[str, AdapterDetails] = {} self._sources: set[str] = set() + self._bluetooth_adapters = bluetooth_adapters @property def supports_passive_scan(self) -> bool: @@ -172,21 +177,25 @@ class BluetoothManager: self, cached: bool = True ) -> dict[str, AdapterDetails]: """Get bluetooth adapters.""" - if not cached or not self._adapters: - self._adapters = await async_get_bluetooth_adapters() + if not self._adapters or not cached: + if not cached: + await self._bluetooth_adapters.refresh() + self._adapters = self._bluetooth_adapters.adapters return self._adapters async def async_get_adapter_from_address(self, address: str) -> str | None: """Get adapter from address.""" if adapter := self._find_adapter_by_address(address): return adapter - self._adapters = await async_get_bluetooth_adapters() + await self._bluetooth_adapters.refresh() + self._adapters = self._bluetooth_adapters.adapters return self._find_adapter_by_address(address) async def async_setup(self) -> None: """Set up the bluetooth manager.""" + await self._bluetooth_adapters.refresh() install_multiple_bleak_catcher() - history = await async_load_history_from_system() + history = async_load_history_from_system(self._bluetooth_adapters) # Everything is connectable so it fall into both # buckets since the host system can only provide # connectable devices diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 255c4ce0e82..ca0df4d2042 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -8,7 +8,7 @@ "requirements": [ "bleak==0.19.2", "bleak-retry-connector==2.8.4", - "bluetooth-adapters==0.7.0", + "bluetooth-adapters==0.8.0", "bluetooth-auto-recovery==0.4.0", "bluetooth-data-tools==0.3.0", "dbus-fast==1.74.1" diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index c05894d3b44..797a0551552 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -16,6 +16,7 @@ from bleak.backends.bluezdbus.advertisement_monitor import OrPattern from bleak.backends.bluezdbus.scanner import BlueZScannerArgs from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback +from bluetooth_adapters import DEFAULT_ADDRESS, adapter_human_name from dbus_fast import InvalidMessageError from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback @@ -25,14 +26,13 @@ from homeassistant.util.dt import monotonic_time_coarse from homeassistant.util.package import is_docker_env from .const import ( - DEFAULT_ADDRESS, SCANNER_WATCHDOG_INTERVAL, SCANNER_WATCHDOG_TIMEOUT, SOURCE_LOCAL, START_TIMEOUT, ) from .models import BaseHaScanner, BluetoothScanningMode, BluetoothServiceInfoBleak -from .util import adapter_human_name, async_reset_adapter +from .util import async_reset_adapter OriginalBleakScanner = bleak.BleakScanner MONOTONIC_TIME = monotonic_time_coarse diff --git a/homeassistant/components/bluetooth/util.py b/homeassistant/components/bluetooth/util.py index abfba6b22bc..c2336dd7af0 100644 --- a/homeassistant/components/bluetooth/util.py +++ b/homeassistant/components/bluetooth/util.py @@ -1,34 +1,20 @@ """The bluetooth integration utilities.""" from __future__ import annotations -import platform - +from bluetooth_adapters import BluetoothAdapters from bluetooth_auto_recovery import recover_adapter from homeassistant.core import callback from homeassistant.util.dt import monotonic_time_coarse -from .const import ( - DEFAULT_ADAPTER_BY_PLATFORM, - DEFAULT_ADDRESS, - MACOS_DEFAULT_BLUETOOTH_ADAPTER, - UNIX_DEFAULT_BLUETOOTH_ADAPTER, - WINDOWS_DEFAULT_BLUETOOTH_ADAPTER, - AdapterDetails, -) from .models import BluetoothServiceInfoBleak -async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBleak]: +@callback +def async_load_history_from_system( + adapters: BluetoothAdapters, +) -> 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 = monotonic_time_coarse() return { address: BluetoothServiceInfoBleak( @@ -46,65 +32,10 @@ async def async_load_history_from_system() -> dict[str, BluetoothServiceInfoBlea connectable=False, time=now, ) - for address, history in bluez_dbus.history.items() + for address, history in adapters.history.items() } -async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]: - """Return a list of bluetooth adapters.""" - if platform.system() == "Windows": - return { - WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( - address=DEFAULT_ADDRESS, - sw_version=platform.release(), - passive_scan=False, - ) - } - if platform.system() == "Darwin": - return { - MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( - address=DEFAULT_ADDRESS, - sw_version=platform.release(), - passive_scan=False, - ) - } - from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel - get_bluetooth_adapter_details, - ) - - adapters: dict[str, AdapterDetails] = {} - adapter_details = await get_bluetooth_adapter_details() - for adapter, details in adapter_details.items(): - adapter1 = details["org.bluez.Adapter1"] - adapters[adapter] = AdapterDetails( - address=adapter1["Address"], - sw_version=adapter1["Name"], # This is actually the BlueZ version - hw_version=adapter1.get("Modalias"), - passive_scan="org.bluez.AdvertisementMonitorManager1" in details, - ) - return adapters - - -@callback -def async_default_adapter() -> str: - """Return the default adapter for the platform.""" - return DEFAULT_ADAPTER_BY_PLATFORM.get( - platform.system(), UNIX_DEFAULT_BLUETOOTH_ADAPTER - ) - - -@callback -def adapter_human_name(adapter: str, address: str) -> str: - """Return a human readable name for the adapter.""" - return adapter if address == DEFAULT_ADDRESS else f"{adapter} ({address})" - - -@callback -def adapter_unique_name(adapter: str, address: str) -> str: - """Return a unique name for the adapter.""" - return adapter if address == DEFAULT_ADDRESS else address - - async def async_reset_adapter(adapter: str | None) -> bool | None: """Reset the adapter.""" if adapter and adapter.startswith("hci"): diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 394de1abc83..833421d3bf2 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ awesomeversion==22.9.0 bcrypt==3.1.7 bleak-retry-connector==2.8.4 bleak==0.19.2 -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 bluetooth-auto-recovery==0.4.0 bluetooth-data-tools==0.3.0 certifi>=2021.5.30 diff --git a/requirements_all.txt b/requirements_all.txt index 679be04f0e7..0fd0942c23d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -447,7 +447,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a7c25fb3cea..418b0aa48a9 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -361,7 +361,7 @@ blinkpy==0.19.2 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.7.0 +bluetooth-adapters==0.8.0 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.4.0 diff --git a/tests/components/bluetooth/__init__.py b/tests/components/bluetooth/__init__.py index e695f18c42f..0ffa347c6d2 100644 --- a/tests/components/bluetooth/__init__.py +++ b/tests/components/bluetooth/__init__.py @@ -6,6 +6,7 @@ from typing import Any from unittest.mock import patch from bleak.backends.scanner import AdvertisementData, BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components.bluetooth import ( DOMAIN, @@ -13,7 +14,6 @@ from homeassistant.components.bluetooth import ( async_get_advertisement_callback, models, ) -from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS from homeassistant.components.bluetooth.manager import BluetoothManager from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component diff --git a/tests/components/bluetooth/conftest.py b/tests/components/bluetooth/conftest.py index 3d29b4cbbe1..c650a84df85 100644 --- a/tests/components/bluetooth/conftest.py +++ b/tests/components/bluetooth/conftest.py @@ -1,6 +1,6 @@ """Tests for the bluetooth component.""" -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch import pytest @@ -43,16 +43,6 @@ def mock_operating_system_90(): yield -@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.""" @@ -62,7 +52,7 @@ def macos_adapter(): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Darwin", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Darwin" + "bluetooth_adapters.systems.platform.system", return_value="Darwin" ): yield @@ -71,14 +61,14 @@ def macos_adapter(): def windows_adapter(): """Fixture that mocks the windows adapter.""" with patch( - "homeassistant.components.bluetooth.util.platform.system", + "bluetooth_adapters.systems.platform.system", return_value="Windows", ): yield @pytest.fixture(name="no_adapters") -def no_adapter_fixture(bluez_dbus_mock): +def no_adapter_fixture(): """Fixture that mocks no adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -86,16 +76,18 @@ def no_adapter_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={}, + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): yield @pytest.fixture(name="one_adapter") -def one_adapter_fixture(bluez_dbus_mock): +def one_adapter_fixture(): """Fixture that mocks one adapter on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -103,20 +95,17 @@ def one_adapter_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - }, - "org.bluez.AdvertisementMonitorManager1": { - "SupportedMonitorTypes": ["or_patterns"], - "SupportedFeatures": [], - }, + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": True, + "sw_version": "homeassistant", }, }, ): @@ -124,7 +113,7 @@ def one_adapter_fixture(bluez_dbus_mock): @pytest.fixture(name="two_adapters") -def two_adapters_fixture(bluez_dbus_mock): +def two_adapters_fixture(): """Fixture that mocks two adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -132,27 +121,23 @@ def two_adapters_fixture(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - }, - "org.bluez.AdvertisementMonitorManager1": { - "SupportedMonitorTypes": ["or_patterns"], - "SupportedFeatures": [], - }, + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": True, + "sw_version": "homeassistant", }, }, ): @@ -160,7 +145,7 @@ def two_adapters_fixture(bluez_dbus_mock): @pytest.fixture(name="one_adapter_old_bluez") -def one_adapter_old_bluez(bluez_dbus_mock): +def one_adapter_old_bluez(): """Fixture that mocks two adapters on Linux.""" with patch( "homeassistant.components.bluetooth.platform.system", return_value="Linux" @@ -168,15 +153,17 @@ def one_adapter_old_bluez(bluez_dbus_mock): "homeassistant.components.bluetooth.scanner.platform.system", return_value="Linux", ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.linux.LinuxAdapters.refresh" + ), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.43", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): diff --git a/tests/components/bluetooth/test_config_flow.py b/tests/components/bluetooth/test_config_flow.py index 69619ba76a7..76c0346026b 100644 --- a/tests/components/bluetooth/test_config_flow.py +++ b/tests/components/bluetooth/test_config_flow.py @@ -2,14 +2,14 @@ from unittest.mock import patch +from bluetooth_adapters import DEFAULT_ADDRESS, AdapterDetails + from homeassistant import config_entries from homeassistant.components.bluetooth.const import ( CONF_ADAPTER, CONF_DETAILS, CONF_PASSIVE, - DEFAULT_ADDRESS, DOMAIN, - AdapterDetails, ) from homeassistant.data_entry_flow import FlowResultType from homeassistant.setup import async_setup_component diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index 56321175241..bfbefe74151 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -4,9 +4,9 @@ from unittest.mock import ANY, patch from bleak.backends.scanner import BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS from homeassistant.components import bluetooth -from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS from . import generate_advertisement_data, inject_advertisement @@ -68,15 +68,15 @@ async def test_diagnostics( "adapters": { "hci0": { "address": "00:00:00:00:00:01", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, "hci1": { "address": "00:00:00:00:00:02", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, }, "dbus": { @@ -99,15 +99,15 @@ async def test_diagnostics( "adapters": { "hci0": { "address": "00:00:00:00:00:01", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, "hci1": { "address": "00:00:00:00:00:02", - "hw_version": "usbid:1234", + "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": True, - "sw_version": "BlueZ 4.63", + "sw_version": "homeassistant", }, }, "advertisement_tracker": { diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 5a5437af71a..da315984f3c 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -6,6 +6,7 @@ from unittest.mock import ANY, MagicMock, Mock, patch from bleak import BleakError from bleak.backends.scanner import AdvertisementData, BLEDevice +from bluetooth_adapters import DEFAULT_ADDRESS import pytest from homeassistant.components import bluetooth @@ -22,7 +23,6 @@ from homeassistant.components.bluetooth import ( from homeassistant.components.bluetooth.const import ( BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS, CONF_PASSIVE, - DEFAULT_ADDRESS, DOMAIN, LINUX_FIRMWARE_LOAD_FALLBACK_SECONDS, SOURCE_LOCAL, @@ -2522,7 +2522,7 @@ async def test_async_ble_device_from_address( async def test_can_unsetup_bluetooth_single_adapter_macos( - hass, mock_bleak_scanner_start, enable_bluetooth, macos_adapter + hass, mock_bleak_scanner_start, macos_adapter ): """Test we can setup and unsetup bluetooth.""" entry = MockConfigEntry(domain=bluetooth.DOMAIN, data={}, unique_id=DEFAULT_ADDRESS) @@ -2605,12 +2605,13 @@ 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, bluez_dbus_mock): +async def test_auto_detect_bluetooth_adapters_linux_none_found(hass): """Test we auto detect bluetooth adapters on linux with no adapters found.""" with patch( - "bluetooth_adapters.get_bluetooth_adapter_details", return_value={} - ), patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() @@ -2620,9 +2621,7 @@ async def test_auto_detect_bluetooth_adapters_linux_none_found(hass, bluez_dbus_ async def test_auto_detect_bluetooth_adapters_macos(hass): """Test we auto detect bluetooth adapters on macos.""" - with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Darwin" - ): + with patch("bluetooth_adapters.systems.platform.system", return_value="Darwin"): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() assert not hass.config_entries.async_entries(bluetooth.DOMAIN) @@ -2632,7 +2631,7 @@ async def test_auto_detect_bluetooth_adapters_macos(hass): async def test_no_auto_detect_bluetooth_adapters_windows(hass): """Test we auto detect bluetooth adapters on windows.""" with patch( - "homeassistant.components.bluetooth.util.platform.system", + "bluetooth_adapters.systems.platform.system", return_value="Windows", ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) @@ -2710,23 +2709,21 @@ async def test_discover_new_usb_adapters(hass, mock_bleak_scanner_start, one_ada assert not hass.config_entries.flow.async_progress(DOMAIN) with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): @@ -2768,10 +2765,10 @@ async def test_discover_new_usb_adapters_with_firmware_fallback_delay( assert not hass.config_entries.flow.async_progress(DOMAIN) with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={}, + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + {}, ): async_fire_time_changed( hass, dt_util.utcnow() + timedelta(BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS * 2) @@ -2781,23 +2778,21 @@ async def test_discover_new_usb_adapters_with_firmware_fallback_delay( assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 0 with patch( - "homeassistant.components.bluetooth.util.platform.system", return_value="Linux" - ), patch( - "bluetooth_adapters.get_bluetooth_adapter_details", - return_value={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, "hci1": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:02", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:02", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ): diff --git a/tests/components/bluetooth/test_manager.py b/tests/components/bluetooth/test_manager.py index 0375f68309f..c61d8f4c9f1 100644 --- a/tests/components/bluetooth/test_manager.py +++ b/tests/components/bluetooth/test_manager.py @@ -1,7 +1,7 @@ """Tests for the Bluetooth integration manager.""" import time -from unittest.mock import AsyncMock, MagicMock, patch +from unittest.mock import patch from bleak.backends.scanner import BLEDevice from bluetooth_adapters import AdvertisementHistory @@ -275,8 +275,8 @@ async def test_restore_history_from_dbus(hass, one_adapter): } with patch( - "bluetooth_adapters.BlueZDBusObjects", - return_value=MagicMock(load=AsyncMock(), history=history), + "bluetooth_adapters.systems.linux.LinuxAdapters.history", + history, ): assert await async_setup_component(hass, bluetooth.DOMAIN, {}) await hass.async_block_till_done() diff --git a/tests/conftest.py b/tests/conftest.py index 1047293ee16..ca005625655 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1041,18 +1041,15 @@ async def mock_enable_bluetooth( 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={ + "bluetooth_adapters.systems.platform.system", return_value="Linux" + ), patch("bluetooth_adapters.systems.linux.LinuxAdapters.refresh"), patch( + "bluetooth_adapters.systems.linux.LinuxAdapters.adapters", + { "hci0": { - "org.bluez.Adapter1": { - "Address": "00:00:00:00:00:01", - "Name": "BlueZ 4.63", - "Modalias": "usbid:1234", - } + "address": "00:00:00:00:00:01", + "hw_version": "usb:v1D6Bp0246d053F", + "passive_scan": False, + "sw_version": "homeassistant", }, }, ):