mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Update to bleak 0.18.0 (#79008)
This commit is contained in:
parent
02731efc4c
commit
1b144c0e4d
@ -52,6 +52,7 @@ from .models import (
|
|||||||
BluetoothServiceInfo,
|
BluetoothServiceInfo,
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
HaBleakScannerWrapper,
|
HaBleakScannerWrapper,
|
||||||
|
HaBluetoothConnector,
|
||||||
ProcessAdvertisementCallback,
|
ProcessAdvertisementCallback,
|
||||||
)
|
)
|
||||||
from .scanner import HaScanner, ScannerStartError, create_bleak_scanner
|
from .scanner import HaScanner, ScannerStartError, create_bleak_scanner
|
||||||
@ -66,9 +67,11 @@ __all__ = [
|
|||||||
"async_ble_device_from_address",
|
"async_ble_device_from_address",
|
||||||
"async_discovered_service_info",
|
"async_discovered_service_info",
|
||||||
"async_get_scanner",
|
"async_get_scanner",
|
||||||
|
"async_last_service_info",
|
||||||
"async_process_advertisements",
|
"async_process_advertisements",
|
||||||
"async_rediscover_address",
|
"async_rediscover_address",
|
||||||
"async_register_callback",
|
"async_register_callback",
|
||||||
|
"async_register_scanner",
|
||||||
"async_track_unavailable",
|
"async_track_unavailable",
|
||||||
"async_scanner_count",
|
"async_scanner_count",
|
||||||
"BaseHaScanner",
|
"BaseHaScanner",
|
||||||
@ -76,6 +79,7 @@ __all__ = [
|
|||||||
"BluetoothServiceInfoBleak",
|
"BluetoothServiceInfoBleak",
|
||||||
"BluetoothScanningMode",
|
"BluetoothScanningMode",
|
||||||
"BluetoothCallback",
|
"BluetoothCallback",
|
||||||
|
"HaBluetoothConnector",
|
||||||
"SOURCE_LOCAL",
|
"SOURCE_LOCAL",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -66,3 +66,6 @@ ADAPTER_ADDRESS: Final = "address"
|
|||||||
ADAPTER_SW_VERSION: Final = "sw_version"
|
ADAPTER_SW_VERSION: Final = "sw_version"
|
||||||
ADAPTER_HW_VERSION: Final = "hw_version"
|
ADAPTER_HW_VERSION: Final = "hw_version"
|
||||||
ADAPTER_PASSIVE_SCAN: Final = "passive_scan"
|
ADAPTER_PASSIVE_SCAN: Final = "passive_scan"
|
||||||
|
|
||||||
|
|
||||||
|
NO_RSSI_VALUE: Final = -1000
|
||||||
|
@ -24,6 +24,7 @@ from homeassistant.helpers.event import async_track_time_interval
|
|||||||
from .const import (
|
from .const import (
|
||||||
ADAPTER_ADDRESS,
|
ADAPTER_ADDRESS,
|
||||||
ADAPTER_PASSIVE_SCAN,
|
ADAPTER_PASSIVE_SCAN,
|
||||||
|
NO_RSSI_VALUE,
|
||||||
STALE_ADVERTISEMENT_SECONDS,
|
STALE_ADVERTISEMENT_SECONDS,
|
||||||
UNAVAILABLE_TRACK_SECONDS,
|
UNAVAILABLE_TRACK_SECONDS,
|
||||||
AdapterDetails,
|
AdapterDetails,
|
||||||
@ -65,7 +66,6 @@ APPLE_START_BYTES_WANTED: Final = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RSSI_SWITCH_THRESHOLD = 6
|
RSSI_SWITCH_THRESHOLD = 6
|
||||||
NO_RSSI_VALUE = -1000
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -340,6 +340,7 @@ class BluetoothManager:
|
|||||||
service_info.manufacturer_data != old_service_info.manufacturer_data
|
service_info.manufacturer_data != old_service_info.manufacturer_data
|
||||||
or service_info.service_data != old_service_info.service_data
|
or service_info.service_data != old_service_info.service_data
|
||||||
or service_info.service_uuids != old_service_info.service_uuids
|
or service_info.service_uuids != old_service_info.service_uuids
|
||||||
|
or service_info.name != old_service_info.name
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
"after_dependencies": ["hassio"],
|
"after_dependencies": ["hassio"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"bleak==0.17.0",
|
"bleak==0.18.0",
|
||||||
"bleak-retry-connector==1.17.1",
|
"bleak-retry-connector==1.17.3",
|
||||||
"bluetooth-adapters==0.5.1",
|
"bluetooth-adapters==0.5.1",
|
||||||
"bluetooth-auto-recovery==0.3.3",
|
"bluetooth-auto-recovery==0.3.3",
|
||||||
"dbus-fast==1.5.1"
|
"dbus-fast==1.7.0"
|
||||||
],
|
],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -11,17 +11,21 @@ import logging
|
|||||||
from typing import TYPE_CHECKING, Any, Final
|
from typing import TYPE_CHECKING, Any, Final
|
||||||
|
|
||||||
from bleak import BleakClient, BleakError
|
from bleak import BleakClient, BleakError
|
||||||
|
from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type
|
||||||
from bleak.backends.device import BLEDevice
|
from bleak.backends.device import BLEDevice
|
||||||
from bleak.backends.scanner import (
|
from bleak.backends.scanner import (
|
||||||
AdvertisementData,
|
AdvertisementData,
|
||||||
AdvertisementDataCallback,
|
AdvertisementDataCallback,
|
||||||
BaseBleakScanner,
|
BaseBleakScanner,
|
||||||
)
|
)
|
||||||
|
from bleak_retry_connector import freshen_ble_device
|
||||||
|
|
||||||
from homeassistant.core import CALLBACK_TYPE
|
from homeassistant.core import CALLBACK_TYPE, callback as hass_callback
|
||||||
from homeassistant.helpers.frame import report
|
from homeassistant.helpers.frame import report
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
|
|
||||||
|
from .const import NO_RSSI_VALUE
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
from .manager import BluetoothManager
|
from .manager import BluetoothManager
|
||||||
@ -62,6 +66,23 @@ BluetoothCallback = Callable[[BluetoothServiceInfoBleak, BluetoothChange], None]
|
|||||||
ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool]
|
ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HaBluetoothConnector:
|
||||||
|
"""Data for how to connect a BLEDevice from a given scanner."""
|
||||||
|
|
||||||
|
client: type[BaseBleakClient]
|
||||||
|
source: str
|
||||||
|
can_connect: Callable[[], bool]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class _HaWrappedBleakBackend:
|
||||||
|
"""Wrap bleak backend to make it usable by Home Assistant."""
|
||||||
|
|
||||||
|
device: BLEDevice
|
||||||
|
client: type[BaseBleakClient]
|
||||||
|
|
||||||
|
|
||||||
class BaseHaScanner:
|
class BaseHaScanner:
|
||||||
"""Base class for Ha Scanners."""
|
"""Base class for Ha Scanners."""
|
||||||
|
|
||||||
@ -109,6 +130,12 @@ class HaBleakScannerWrapper(BaseBleakScanner):
|
|||||||
detection_callback=detection_callback, service_uuids=service_uuids or []
|
detection_callback=detection_callback, service_uuids=service_uuids or []
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def discover(cls, timeout: float = 5.0, **kwargs: Any) -> list[BLEDevice]:
|
||||||
|
"""Discover devices."""
|
||||||
|
assert MANAGER is not None
|
||||||
|
return list(MANAGER.async_discovered_devices(True))
|
||||||
|
|
||||||
async def stop(self, *args: Any, **kwargs: Any) -> None:
|
async def stop(self, *args: Any, **kwargs: Any) -> None:
|
||||||
"""Stop scanning for devices."""
|
"""Stop scanning for devices."""
|
||||||
|
|
||||||
@ -189,20 +216,116 @@ class HaBleakClientWrapper(BleakClient):
|
|||||||
when an integration does this.
|
when an integration does this.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__( # pylint: disable=super-init-not-called, keyword-arg-before-vararg
|
||||||
self, address_or_ble_device: str | BLEDevice, *args: Any, **kwargs: Any
|
self,
|
||||||
|
address_or_ble_device: str | BLEDevice,
|
||||||
|
disconnected_callback: Callable[[BleakClient], None] | None = None,
|
||||||
|
*args: Any,
|
||||||
|
timeout: float = 10.0,
|
||||||
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the BleakClient."""
|
"""Initialize the BleakClient."""
|
||||||
if isinstance(address_or_ble_device, BLEDevice):
|
if isinstance(address_or_ble_device, BLEDevice):
|
||||||
super().__init__(address_or_ble_device, *args, **kwargs)
|
self.__address = address_or_ble_device.address
|
||||||
return
|
else:
|
||||||
report(
|
report(
|
||||||
"attempted to call BleakClient with an address instead of a BLEDevice",
|
"attempted to call BleakClient with an address instead of a BLEDevice",
|
||||||
exclude_integrations={"bluetooth"},
|
exclude_integrations={"bluetooth"},
|
||||||
error_if_core=False,
|
error_if_core=False,
|
||||||
)
|
)
|
||||||
|
self.__address = address_or_ble_device
|
||||||
|
self.__disconnected_callback = disconnected_callback
|
||||||
|
self.__timeout = timeout
|
||||||
|
self._backend: BaseBleakClient | None = None # type: ignore[assignment]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_connected(self) -> bool:
|
||||||
|
"""Return True if the client is connected to a device."""
|
||||||
|
return self._backend is not None and self._backend.is_connected
|
||||||
|
|
||||||
|
def set_disconnected_callback(
|
||||||
|
self,
|
||||||
|
callback: Callable[[BleakClient], None] | None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Set the disconnect callback."""
|
||||||
|
self.__disconnected_callback = callback
|
||||||
|
if self._backend:
|
||||||
|
self._backend.set_disconnected_callback(callback, **kwargs) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
async def connect(self, **kwargs: Any) -> bool:
|
||||||
|
"""Connect to the specified GATT server."""
|
||||||
|
if not self._backend:
|
||||||
|
wrapped_backend = self._async_get_backend()
|
||||||
|
self._backend = wrapped_backend.client(
|
||||||
|
await freshen_ble_device(wrapped_backend.device)
|
||||||
|
or wrapped_backend.device,
|
||||||
|
disconnected_callback=self.__disconnected_callback,
|
||||||
|
timeout=self.__timeout,
|
||||||
|
)
|
||||||
|
return await super().connect(**kwargs)
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _async_get_backend_for_ble_device(
|
||||||
|
self, ble_device: BLEDevice
|
||||||
|
) -> _HaWrappedBleakBackend | None:
|
||||||
|
"""Get the backend for a BLEDevice."""
|
||||||
|
details = ble_device.details
|
||||||
|
if not isinstance(details, dict) or "connector" not in details:
|
||||||
|
# If client is not defined in details
|
||||||
|
# its the client for this platform
|
||||||
|
cls = get_platform_client_backend_type()
|
||||||
|
return _HaWrappedBleakBackend(ble_device, cls)
|
||||||
|
|
||||||
|
connector: HaBluetoothConnector = details["connector"]
|
||||||
|
# Make sure the backend can connect to the device
|
||||||
|
# as some backends have connection limits
|
||||||
|
if not connector.can_connect():
|
||||||
|
return None
|
||||||
|
|
||||||
|
return _HaWrappedBleakBackend(ble_device, connector.client)
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _async_get_backend(self) -> _HaWrappedBleakBackend:
|
||||||
|
"""Get the bleak backend for the given address."""
|
||||||
assert MANAGER is not None
|
assert MANAGER is not None
|
||||||
ble_device = MANAGER.async_ble_device_from_address(address_or_ble_device, True)
|
address = self.__address
|
||||||
|
ble_device = MANAGER.async_ble_device_from_address(address, True)
|
||||||
if ble_device is None:
|
if ble_device is None:
|
||||||
raise BleakError(f"No device found for address {address_or_ble_device}")
|
raise BleakError(f"No device found for address {address}")
|
||||||
super().__init__(ble_device, *args, **kwargs)
|
|
||||||
|
if backend := self._async_get_backend_for_ble_device(ble_device):
|
||||||
|
return backend
|
||||||
|
|
||||||
|
#
|
||||||
|
# The preferred backend cannot currently connect the device
|
||||||
|
# because it is likely out of connection slots.
|
||||||
|
#
|
||||||
|
# We need to try all backends to find one that can
|
||||||
|
# connect to the device.
|
||||||
|
#
|
||||||
|
# Currently we have to search all the discovered devices
|
||||||
|
# because the bleak API does not allow us to get the
|
||||||
|
# details for a specific device.
|
||||||
|
#
|
||||||
|
for ble_device in sorted(
|
||||||
|
(
|
||||||
|
ble_device
|
||||||
|
for ble_device in MANAGER.async_all_discovered_devices(True)
|
||||||
|
if ble_device.address == address
|
||||||
|
),
|
||||||
|
key=lambda ble_device: ble_device.rssi or NO_RSSI_VALUE,
|
||||||
|
reverse=True,
|
||||||
|
):
|
||||||
|
if backend := self._async_get_backend_for_ble_device(ble_device):
|
||||||
|
return backend
|
||||||
|
|
||||||
|
raise BleakError(
|
||||||
|
f"No backend with an available connection slot that can reach address {address} was found"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def disconnect(self) -> bool:
|
||||||
|
"""Disconnect from the device."""
|
||||||
|
if self._backend is None:
|
||||||
|
return True
|
||||||
|
return await self._backend.disconnect()
|
||||||
|
@ -13,7 +13,7 @@ ORIGINAL_BLEAK_CLIENT = bleak.BleakClient
|
|||||||
def install_multiple_bleak_catcher() -> None:
|
def install_multiple_bleak_catcher() -> None:
|
||||||
"""Wrap the bleak classes to return the shared instance if multiple instances are detected."""
|
"""Wrap the bleak classes to return the shared instance if multiple instances are detected."""
|
||||||
bleak.BleakScanner = HaBleakScannerWrapper # type: ignore[misc, assignment]
|
bleak.BleakScanner = HaBleakScannerWrapper # type: ignore[misc, assignment]
|
||||||
bleak.BleakClient = HaBleakClientWrapper # type: ignore[misc]
|
bleak.BleakClient = HaBleakClientWrapper # type: ignore[misc, assignment]
|
||||||
|
|
||||||
|
|
||||||
def uninstall_multiple_bleak_catcher() -> None:
|
def uninstall_multiple_bleak_catcher() -> None:
|
||||||
|
@ -10,14 +10,14 @@ atomicwrites-homeassistant==1.4.1
|
|||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
awesomeversion==22.9.0
|
awesomeversion==22.9.0
|
||||||
bcrypt==3.1.7
|
bcrypt==3.1.7
|
||||||
bleak-retry-connector==1.17.1
|
bleak-retry-connector==1.17.3
|
||||||
bleak==0.17.0
|
bleak==0.18.0
|
||||||
bluetooth-adapters==0.5.1
|
bluetooth-adapters==0.5.1
|
||||||
bluetooth-auto-recovery==0.3.3
|
bluetooth-auto-recovery==0.3.3
|
||||||
certifi>=2021.5.30
|
certifi>=2021.5.30
|
||||||
ciso8601==2.2.0
|
ciso8601==2.2.0
|
||||||
cryptography==37.0.4
|
cryptography==37.0.4
|
||||||
dbus-fast==1.5.1
|
dbus-fast==1.7.0
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
hass-nabucasa==0.55.0
|
hass-nabucasa==0.55.0
|
||||||
home-assistant-bluetooth==1.3.0
|
home-assistant-bluetooth==1.3.0
|
||||||
|
@ -410,10 +410,10 @@ bimmer_connected==0.10.4
|
|||||||
bizkaibus==0.1.1
|
bizkaibus==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak-retry-connector==1.17.1
|
bleak-retry-connector==1.17.3
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak==0.17.0
|
bleak==0.18.0
|
||||||
|
|
||||||
# homeassistant.components.blebox
|
# homeassistant.components.blebox
|
||||||
blebox_uniapi==2.0.2
|
blebox_uniapi==2.0.2
|
||||||
@ -540,7 +540,7 @@ datadog==0.15.0
|
|||||||
datapoint==0.9.8
|
datapoint==0.9.8
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
dbus-fast==1.5.1
|
dbus-fast==1.7.0
|
||||||
|
|
||||||
# homeassistant.components.debugpy
|
# homeassistant.components.debugpy
|
||||||
debugpy==1.6.3
|
debugpy==1.6.3
|
||||||
|
@ -331,10 +331,10 @@ bellows==0.33.1
|
|||||||
bimmer_connected==0.10.4
|
bimmer_connected==0.10.4
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak-retry-connector==1.17.1
|
bleak-retry-connector==1.17.3
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak==0.17.0
|
bleak==0.18.0
|
||||||
|
|
||||||
# homeassistant.components.blebox
|
# homeassistant.components.blebox
|
||||||
blebox_uniapi==2.0.2
|
blebox_uniapi==2.0.2
|
||||||
@ -417,7 +417,7 @@ datadog==0.15.0
|
|||||||
datapoint==0.9.8
|
datapoint==0.9.8
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
dbus-fast==1.5.1
|
dbus-fast==1.7.0
|
||||||
|
|
||||||
# homeassistant.components.debugpy
|
# homeassistant.components.debugpy
|
||||||
debugpy==1.6.3
|
debugpy==1.6.3
|
||||||
|
@ -56,7 +56,7 @@ def bluez_dbus_mock():
|
|||||||
@pytest.fixture(name="macos_adapter")
|
@pytest.fixture(name="macos_adapter")
|
||||||
def macos_adapter():
|
def macos_adapter():
|
||||||
"""Fixture that mocks the macos adapter."""
|
"""Fixture that mocks the macos adapter."""
|
||||||
with patch(
|
with patch("bleak.get_platform_scanner_backend_type"), patch(
|
||||||
"homeassistant.components.bluetooth.platform.system", return_value="Darwin"
|
"homeassistant.components.bluetooth.platform.system", return_value="Darwin"
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.scanner.platform.system",
|
"homeassistant.components.bluetooth.scanner.platform.system",
|
||||||
|
212
tests/components/bluetooth/test_models.py
Normal file
212
tests/components/bluetooth/test_models.py
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
"""Tests for the Bluetooth integration models."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import bleak
|
||||||
|
from bleak import BleakClient, BleakError
|
||||||
|
from bleak.backends.device import BLEDevice
|
||||||
|
from bleak.backends.scanner import AdvertisementData
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.bluetooth.models import (
|
||||||
|
BaseHaScanner,
|
||||||
|
HaBleakClientWrapper,
|
||||||
|
HaBleakScannerWrapper,
|
||||||
|
HaBluetoothConnector,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import _get_manager, inject_advertisement, inject_advertisement_with_source
|
||||||
|
|
||||||
|
|
||||||
|
class MockBleakClient(BleakClient):
|
||||||
|
"""Mock bleak client."""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Mock init."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._device_path = "/dev/test"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_connected(self) -> bool:
|
||||||
|
"""Mock connected."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def connect(self, *args, **kwargs):
|
||||||
|
"""Mock connect."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def disconnect(self, *args, **kwargs):
|
||||||
|
"""Mock disconnect."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_services(self, *args, **kwargs):
|
||||||
|
"""Mock get_services."""
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wrapped_bleak_scanner(hass, enable_bluetooth):
|
||||||
|
"""Test wrapped bleak scanner dispatches calls as expected."""
|
||||||
|
scanner = HaBleakScannerWrapper()
|
||||||
|
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
|
||||||
|
switchbot_adv = AdvertisementData(
|
||||||
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
|
)
|
||||||
|
inject_advertisement(hass, switchbot_device, switchbot_adv)
|
||||||
|
assert scanner.discovered_devices == [switchbot_device]
|
||||||
|
assert await scanner.discover() == [switchbot_device]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wrapped_bleak_client_raises_device_missing(hass, enable_bluetooth):
|
||||||
|
"""Test wrapped bleak client dispatches calls as expected."""
|
||||||
|
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
|
||||||
|
client = HaBleakClientWrapper(switchbot_device)
|
||||||
|
assert client.is_connected is False
|
||||||
|
with pytest.raises(bleak.BleakError):
|
||||||
|
await client.connect()
|
||||||
|
assert client.is_connected is False
|
||||||
|
await client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wrapped_bleak_client_set_disconnected_callback_before_connected(
|
||||||
|
hass, enable_bluetooth
|
||||||
|
):
|
||||||
|
"""Test wrapped bleak client can set a disconnected callback before connected."""
|
||||||
|
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
|
||||||
|
client = HaBleakClientWrapper(switchbot_device)
|
||||||
|
client.set_disconnected_callback(lambda client: None)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_wrapped_bleak_client_set_disconnected_callback_after_connected(
|
||||||
|
hass, enable_bluetooth, one_adapter
|
||||||
|
):
|
||||||
|
"""Test wrapped bleak client can set a disconnected callback after connected."""
|
||||||
|
switchbot_device = BLEDevice(
|
||||||
|
"44:44:33:11:23:45", "wohand", {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"}
|
||||||
|
)
|
||||||
|
switchbot_adv = AdvertisementData(
|
||||||
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
|
)
|
||||||
|
inject_advertisement(hass, switchbot_device, switchbot_adv)
|
||||||
|
client = HaBleakClientWrapper(switchbot_device)
|
||||||
|
with patch(
|
||||||
|
"bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect"
|
||||||
|
) as connect:
|
||||||
|
await client.connect()
|
||||||
|
assert len(connect.mock_calls) == 1
|
||||||
|
assert client._backend is not None
|
||||||
|
client.set_disconnected_callback(lambda client: None)
|
||||||
|
await client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_ble_device_with_proxy_client_out_of_connections(
|
||||||
|
hass, enable_bluetooth, one_adapter
|
||||||
|
):
|
||||||
|
"""Test we switch to the next available proxy when one runs out of connections."""
|
||||||
|
manager = _get_manager()
|
||||||
|
|
||||||
|
switchbot_proxy_device_no_connection_slot = BLEDevice(
|
||||||
|
"44:44:33:11:23:45",
|
||||||
|
"wohand",
|
||||||
|
{
|
||||||
|
"connector": HaBluetoothConnector(
|
||||||
|
MockBleakClient, "mock_bleak_client", lambda: False
|
||||||
|
),
|
||||||
|
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
|
||||||
|
},
|
||||||
|
rssi=-30,
|
||||||
|
)
|
||||||
|
switchbot_adv = AdvertisementData(
|
||||||
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
|
)
|
||||||
|
|
||||||
|
inject_advertisement_with_source(
|
||||||
|
hass, switchbot_proxy_device_no_connection_slot, switchbot_adv, "esp32"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert manager.async_discovered_devices(True) == [
|
||||||
|
switchbot_proxy_device_no_connection_slot
|
||||||
|
]
|
||||||
|
|
||||||
|
client = HaBleakClientWrapper(switchbot_proxy_device_no_connection_slot)
|
||||||
|
with patch(
|
||||||
|
"bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect"
|
||||||
|
), pytest.raises(BleakError):
|
||||||
|
await client.connect()
|
||||||
|
assert client.is_connected is False
|
||||||
|
client.set_disconnected_callback(lambda client: None)
|
||||||
|
await client.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_ble_device_with_proxy_client_out_of_connections_uses_best_available(
|
||||||
|
hass, enable_bluetooth, one_adapter
|
||||||
|
):
|
||||||
|
"""Test we switch to the next available proxy when one runs out of connections."""
|
||||||
|
manager = _get_manager()
|
||||||
|
|
||||||
|
switchbot_proxy_device_no_connection_slot = BLEDevice(
|
||||||
|
"44:44:33:11:23:45",
|
||||||
|
"wohand",
|
||||||
|
{
|
||||||
|
"connector": HaBluetoothConnector(
|
||||||
|
MockBleakClient, "mock_bleak_client", lambda: False
|
||||||
|
),
|
||||||
|
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
|
||||||
|
},
|
||||||
|
rssi=-30,
|
||||||
|
)
|
||||||
|
switchbot_proxy_device_has_connection_slot = BLEDevice(
|
||||||
|
"44:44:33:11:23:45",
|
||||||
|
"wohand",
|
||||||
|
{
|
||||||
|
"connector": HaBluetoothConnector(
|
||||||
|
MockBleakClient, "mock_bleak_client", lambda: True
|
||||||
|
),
|
||||||
|
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
|
||||||
|
},
|
||||||
|
rssi=-40,
|
||||||
|
)
|
||||||
|
switchbot_device = BLEDevice(
|
||||||
|
"44:44:33:11:23:45",
|
||||||
|
"wohand",
|
||||||
|
{"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"},
|
||||||
|
rssi=-100,
|
||||||
|
)
|
||||||
|
switchbot_adv = AdvertisementData(
|
||||||
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
|
)
|
||||||
|
|
||||||
|
inject_advertisement_with_source(
|
||||||
|
hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01"
|
||||||
|
)
|
||||||
|
inject_advertisement_with_source(
|
||||||
|
hass,
|
||||||
|
switchbot_proxy_device_has_connection_slot,
|
||||||
|
switchbot_adv,
|
||||||
|
"esp32_has_connection_slot",
|
||||||
|
)
|
||||||
|
inject_advertisement_with_source(
|
||||||
|
hass,
|
||||||
|
switchbot_proxy_device_no_connection_slot,
|
||||||
|
switchbot_adv,
|
||||||
|
"esp32_no_connection_slot",
|
||||||
|
)
|
||||||
|
|
||||||
|
class FakeScanner(BaseHaScanner):
|
||||||
|
@property
|
||||||
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
|
"""Return a list of discovered devices."""
|
||||||
|
return [switchbot_proxy_device_has_connection_slot]
|
||||||
|
|
||||||
|
scanner = FakeScanner()
|
||||||
|
cancel = manager.async_register_scanner(scanner, True)
|
||||||
|
assert manager.async_discovered_devices(True) == [
|
||||||
|
switchbot_proxy_device_no_connection_slot
|
||||||
|
]
|
||||||
|
|
||||||
|
client = HaBleakClientWrapper(switchbot_proxy_device_no_connection_slot)
|
||||||
|
with patch("bleak.backends.bluezdbus.client.BleakClientBlueZDBus.connect"):
|
||||||
|
await client.connect()
|
||||||
|
assert client.is_connected is True
|
||||||
|
client.set_disconnected_callback(lambda client: None)
|
||||||
|
await client.disconnect()
|
||||||
|
cancel()
|
@ -5,7 +5,6 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
import bleak
|
import bleak
|
||||||
from bleak.backends.device import BLEDevice
|
from bleak.backends.device import BLEDevice
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth.models import (
|
from homeassistant.components.bluetooth.models import (
|
||||||
HaBleakClientWrapper,
|
HaBleakClientWrapper,
|
||||||
@ -57,9 +56,6 @@ async def test_bleak_client_reports_with_address(hass, enable_bluetooth, caplog)
|
|||||||
"""Test we report when we pass an address to BleakClient."""
|
"""Test we report when we pass an address to BleakClient."""
|
||||||
install_multiple_bleak_catcher()
|
install_multiple_bleak_catcher()
|
||||||
|
|
||||||
with pytest.raises(bleak.BleakError):
|
|
||||||
instance = bleak.BleakClient("00:00:00:00:00:00")
|
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
_get_manager(),
|
_get_manager(),
|
||||||
"async_ble_device_from_address",
|
"async_ble_device_from_address",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user