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 mergepull/82367/head
parent
d0efdd750f
commit
47c66dbed4
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
):
|
||||
|
|
Loading…
Reference in New Issue