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
pull/82367/head
J. Nick Koston 2022-11-17 13:34:19 -06:00 committed by GitHub
parent d0efdd750f
commit 47c66dbed4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 158 additions and 262 deletions

View File

@ -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:

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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",
},
},
):

View File

@ -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

View File

@ -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": {

View File

@ -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",
},
},
):

View File

@ -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()

View File

@ -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",
},
},
):