Update bleak to 1.0.1 (#147742)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Marc Mueller 2025-07-13 01:11:37 +02:00 committed by GitHub
parent 5287f4de81
commit fca6dc264f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 121 additions and 142 deletions

View File

@ -15,12 +15,12 @@
], ],
"quality_scale": "internal", "quality_scale": "internal",
"requirements": [ "requirements": [
"bleak==0.22.3", "bleak==1.0.1",
"bleak-retry-connector==3.9.0", "bleak-retry-connector==4.0.0",
"bluetooth-adapters==0.21.4", "bluetooth-adapters==2.0.0",
"bluetooth-auto-recovery==1.5.2", "bluetooth-auto-recovery==1.5.2",
"bluetooth-data-tools==1.28.2", "bluetooth-data-tools==1.28.2",
"dbus-fast==2.43.0", "dbus-fast==2.43.0",
"habluetooth==3.49.0" "habluetooth==4.0.1"
] ]
} }

View File

@ -22,5 +22,5 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["eq3btsmart"], "loggers": ["eq3btsmart"],
"requirements": ["eq3btsmart==2.1.0", "bleak-esphome==2.16.0"] "requirements": ["eq3btsmart==2.1.0", "bleak-esphome==3.1.0"]
} }

View File

@ -19,7 +19,7 @@
"requirements": [ "requirements": [
"aioesphomeapi==34.2.0", "aioesphomeapi==34.2.0",
"esphome-dashboard-api==1.3.0", "esphome-dashboard-api==1.3.0",
"bleak-esphome==2.16.0" "bleak-esphome==3.1.0"
], ],
"zeroconf": ["_esphomelib._tcp.local."] "zeroconf": ["_esphomelib._tcp.local."]
} }

View File

@ -2,14 +2,42 @@
from __future__ import annotations from __future__ import annotations
from microbot import MicroBotApiClient from collections.abc import Generator
from contextlib import contextmanager
import bleak
from homeassistant.components import bluetooth from homeassistant.components import bluetooth
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_ADDRESS, Platform from homeassistant.const import CONF_ACCESS_TOKEN, CONF_ADDRESS, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from .coordinator import MicroBotConfigEntry, MicroBotDataUpdateCoordinator
@contextmanager
def patch_unused_bleak_discover_import() -> Generator[None]:
"""Patch bleak.discover import in microbot. It is unused and was removed in bleak 1.0.0."""
def getattr_bleak(name: str) -> object:
if name == "discover":
return None
raise AttributeError
original_func = bleak.__dict__.get("__getattr__")
bleak.__dict__["__getattr__"] = getattr_bleak
try:
yield
finally:
if original_func is not None:
bleak.__dict__["__getattr__"] = original_func
with patch_unused_bleak_discover_import():
from microbot import MicroBotApiClient
from .coordinator import ( # noqa: E402
MicroBotConfigEntry,
MicroBotDataUpdateCoordinator,
)
PLATFORMS: list[str] = [Platform.SWITCH] PLATFORMS: list[str] = [Platform.SWITCH]

View File

@ -20,9 +20,9 @@ audioop-lts==0.2.1
av==13.1.0 av==13.1.0
awesomeversion==25.5.0 awesomeversion==25.5.0
bcrypt==4.3.0 bcrypt==4.3.0
bleak-retry-connector==3.9.0 bleak-retry-connector==4.0.0
bleak==0.22.3 bleak==1.0.1
bluetooth-adapters==0.21.4 bluetooth-adapters==2.0.0
bluetooth-auto-recovery==1.5.2 bluetooth-auto-recovery==1.5.2
bluetooth-data-tools==1.28.2 bluetooth-data-tools==1.28.2
cached-ipaddress==0.10.0 cached-ipaddress==0.10.0
@ -34,7 +34,7 @@ dbus-fast==2.43.0
fnv-hash-fast==1.5.0 fnv-hash-fast==1.5.0
go2rtc-client==0.2.1 go2rtc-client==0.2.1
ha-ffmpeg==3.2.2 ha-ffmpeg==3.2.2
habluetooth==3.49.0 habluetooth==4.0.1
hass-nabucasa==0.106.0 hass-nabucasa==0.106.0
hassil==2.2.3 hassil==2.2.3
home-assistant-bluetooth==1.13.1 home-assistant-bluetooth==1.13.1

10
requirements_all.txt generated
View File

@ -616,13 +616,13 @@ bizkaibus==0.1.1
# homeassistant.components.eq3btsmart # homeassistant.components.eq3btsmart
# homeassistant.components.esphome # homeassistant.components.esphome
bleak-esphome==2.16.0 bleak-esphome==3.1.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak-retry-connector==3.9.0 bleak-retry-connector==4.0.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak==0.22.3 bleak==1.0.1
# homeassistant.components.blebox # homeassistant.components.blebox
blebox-uniapi==2.5.0 blebox-uniapi==2.5.0
@ -643,7 +643,7 @@ bluemaestro-ble==0.4.1
# bluepy==1.3.0 # bluepy==1.3.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bluetooth-adapters==0.21.4 bluetooth-adapters==2.0.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bluetooth-auto-recovery==1.5.2 bluetooth-auto-recovery==1.5.2
@ -1124,7 +1124,7 @@ ha-silabs-firmware-client==0.2.0
habiticalib==0.4.0 habiticalib==0.4.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
habluetooth==3.49.0 habluetooth==4.0.1
# homeassistant.components.cloud # homeassistant.components.cloud
hass-nabucasa==0.106.0 hass-nabucasa==0.106.0

View File

@ -550,13 +550,13 @@ bimmer-connected[china]==0.17.2
# homeassistant.components.eq3btsmart # homeassistant.components.eq3btsmart
# homeassistant.components.esphome # homeassistant.components.esphome
bleak-esphome==2.16.0 bleak-esphome==3.1.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak-retry-connector==3.9.0 bleak-retry-connector==4.0.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak==0.22.3 bleak==1.0.1
# homeassistant.components.blebox # homeassistant.components.blebox
blebox-uniapi==2.5.0 blebox-uniapi==2.5.0
@ -574,7 +574,7 @@ bluemaestro-ble==0.4.1
# bluepy==1.3.0 # bluepy==1.3.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bluetooth-adapters==0.21.4 bluetooth-adapters==2.0.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bluetooth-auto-recovery==1.5.2 bluetooth-auto-recovery==1.5.2
@ -985,7 +985,7 @@ ha-silabs-firmware-client==0.2.0
habiticalib==0.4.0 habiticalib==0.4.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
habluetooth==3.49.0 habluetooth==4.0.1
# homeassistant.components.cloud # homeassistant.components.cloud
hass-nabucasa==0.106.0 hass-nabucasa==0.106.0

View File

@ -27,6 +27,7 @@ PACKAGE_CHECK_VERSION_RANGE = {
"aiohttp": "SemVer", "aiohttp": "SemVer",
"attrs": "CalVer", "attrs": "CalVer",
"awesomeversion": "CalVer", "awesomeversion": "CalVer",
"bleak": "SemVer",
"grpcio": "SemVer", "grpcio": "SemVer",
"httpx": "SemVer", "httpx": "SemVer",
"mashumaro": "SemVer", "mashumaro": "SemVer",
@ -297,10 +298,6 @@ PYTHON_VERSION_CHECK_EXCEPTIONS: dict[str, dict[str, set[str]]] = {
# - domain is the integration domain # - domain is the integration domain
# - package is the package (can be transitive) referencing the dependency # - package is the package (can be transitive) referencing the dependency
# - dependencyX should be the name of the referenced dependency # - dependencyX should be the name of the referenced dependency
"bluetooth": {
# https://github.com/hbldh/bleak/pull/1718 (not yet released)
"homeassistant": {"bleak"}
},
"python_script": { "python_script": {
# Security audits are needed for each Python version # Security audits are needed for each Python version
"homeassistant": {"restrictedpython"} "homeassistant": {"restrictedpython"}
@ -501,17 +498,9 @@ def get_requirements(integration: Integration, packages: set[str]) -> set[str]:
continue continue
# Check for restrictive version limits on Python # Check for restrictive version limits on Python
if ( if (requires_python := metadata(package)["Requires-Python"]) and not all(
(requires_python := metadata(package)["Requires-Python"]) _is_dependency_version_range_valid(version_part, "SemVer")
and not all( for version_part in requires_python.split(",")
_is_dependency_version_range_valid(version_part, "SemVer")
for version_part in requires_python.split(",")
)
# "bleak" is a transient dependency of 53 integrations, and we don't
# want to add the whole list to PYTHON_VERSION_CHECK_EXCEPTIONS
# This extra check can be removed when bleak is updated
# https://github.com/hbldh/bleak/pull/1718
and (package in packages or package != "bleak")
): ):
needs_python_version_check_exception = True needs_python_version_check_exception = True
integration.add_warning_or_error( integration.add_warning_or_error(

View File

@ -1,11 +1,11 @@
"""Tests for the Bluetooth integration.""" """Tests for the Bluetooth integration."""
from collections.abc import Iterable from collections.abc import Generator, Iterable
from contextlib import contextmanager from contextlib import contextmanager
import itertools import itertools
import time import time
from typing import Any from typing import Any
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, PropertyMock, patch
from bleak import BleakClient from bleak import BleakClient
from bleak.backends.scanner import AdvertisementData, BLEDevice from bleak.backends.scanner import AdvertisementData, BLEDevice
@ -53,7 +53,6 @@ ADVERTISEMENT_DATA_DEFAULTS = {
BLE_DEVICE_DEFAULTS = { BLE_DEVICE_DEFAULTS = {
"name": None, "name": None,
"rssi": -127,
"details": None, "details": None,
} }
@ -89,7 +88,6 @@ def generate_ble_device(
address: str | None = None, address: str | None = None,
name: str | None = None, name: str | None = None,
details: Any | None = None, details: Any | None = None,
rssi: int | None = None,
**kwargs: Any, **kwargs: Any,
) -> BLEDevice: ) -> BLEDevice:
"""Generate a BLEDevice with defaults.""" """Generate a BLEDevice with defaults."""
@ -100,8 +98,6 @@ def generate_ble_device(
new["name"] = name new["name"] = name
if details is not None: if details is not None:
new["details"] = details new["details"] = details
if rssi is not None:
new["rssi"] = rssi
for key, value in BLE_DEVICE_DEFAULTS.items(): for key, value in BLE_DEVICE_DEFAULTS.items():
new.setdefault(key, value) new.setdefault(key, value)
return BLEDevice(**new) return BLEDevice(**new)
@ -215,34 +211,35 @@ def inject_bluetooth_service_info(
@contextmanager @contextmanager
def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None: def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> Generator[None]:
"""Mock all the discovered devices from all the scanners.""" """Mock all the discovered devices from all the scanners."""
manager = _get_manager() manager = _get_manager()
original_history = {}
scanners = list( scanners = list(
itertools.chain( itertools.chain(
manager._connectable_scanners, manager._non_connectable_scanners manager._connectable_scanners, manager._non_connectable_scanners
) )
) )
for scanner in scanners: if scanners and getattr(scanners[0], "scanner", None):
data = scanner.discovered_devices_and_advertisement_data with patch.object(
original_history[scanner] = data.copy() scanners[0].scanner.__class__,
data.clear() "discovered_devices_and_advertisement_data",
if scanners: new=PropertyMock(
data = scanners[0].discovered_devices_and_advertisement_data side_effect=[
data.clear() {
data.update( device.address: (device, MagicMock())
{device.address: (device, MagicMock()) for device in mock_discovered} for device in mock_discovered
) },
yield ]
for scanner in scanners: + [{}] * (len(scanners))
data = scanner.discovered_devices_and_advertisement_data ),
data.clear() ):
data.update(original_history[scanner]) yield
else:
yield
@contextmanager @contextmanager
def patch_discovered_devices(mock_discovered: list[BLEDevice]) -> None: def patch_discovered_devices(mock_discovered: list[BLEDevice]) -> Generator[None]:
"""Mock the combined best path to discovered devices from all the scanners.""" """Mock the combined best path to discovered devices from all the scanners."""
manager = _get_manager() manager = _get_manager()
original_all_history = manager._all_history original_all_history = manager._all_history
@ -305,6 +302,9 @@ class MockBleakClient(BleakClient):
"""Mock clear_cache.""" """Mock clear_cache."""
return True return True
def set_disconnected_callback(self, callback, **kwargs):
"""Mock set_disconnected_callback."""
class FakeScannerMixin: class FakeScannerMixin:
def get_discovered_device_advertisement_data( def get_discovered_device_advertisement_data(

View File

@ -82,7 +82,6 @@ async def test_async_scanner_devices_by_address_connectable(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -116,7 +115,6 @@ async def test_async_scanner_devices_by_address_non_connectable(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",

View File

@ -54,7 +54,6 @@ async def test_remote_scanner(hass: HomeAssistant, name_2: str | None) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -67,7 +66,6 @@ async def test_remote_scanner(hass: HomeAssistant, name_2: str | None) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
name_2, name_2,
{}, {},
rssi=-100,
) )
switchbot_device_adv_2 = generate_advertisement_data( switchbot_device_adv_2 = generate_advertisement_data(
local_name=name_2, local_name=name_2,
@ -80,7 +78,6 @@ async def test_remote_scanner(hass: HomeAssistant, name_2: str | None) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohandlonger", "wohandlonger",
{}, {},
rssi=-100,
) )
switchbot_device_adv_3 = generate_advertisement_data( switchbot_device_adv_3 = generate_advertisement_data(
local_name="wohandlonger", local_name="wohandlonger",
@ -146,7 +143,6 @@ async def test_remote_scanner_expires_connectable(hass: HomeAssistant) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -199,7 +195,6 @@ async def test_remote_scanner_expires_non_connectable(hass: HomeAssistant) -> No
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -272,7 +267,6 @@ async def test_base_scanner_connecting_behavior(hass: HomeAssistant) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -376,7 +370,6 @@ async def test_device_with_ten_minute_advertising_interval(hass: HomeAssistant)
"44:44:33:11:23:45", "44:44:33:11:23:45",
"bparasite", "bparasite",
{}, {},
rssi=-100,
) )
bparasite_device_adv = generate_advertisement_data( bparasite_device_adv = generate_advertisement_data(
local_name="bparasite", local_name="bparasite",
@ -501,7 +494,6 @@ async def test_scanner_stops_responding(hass: HomeAssistant) -> None:
"44:44:33:11:23:45", "44:44:33:11:23:45",
"bparasite", "bparasite",
{}, {},
rssi=-100,
) )
bparasite_device_adv = generate_advertisement_data( bparasite_device_adv = generate_advertisement_data(
local_name="bparasite", local_name="bparasite",
@ -545,7 +537,6 @@ async def test_remote_scanner_bluetooth_config_entry(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",

View File

@ -37,7 +37,7 @@ class FakeHaScanner(FakeScannerMixin, HaScanner):
"""Return the discovered devices and advertisement data.""" """Return the discovered devices and advertisement data."""
return { return {
"44:44:33:11:23:45": ( "44:44:33:11:23:45": (
generate_ble_device(name="x", rssi=-127, address="44:44:33:11:23:45"), generate_ble_device(name="x", address="44:44:33:11:23:45"),
generate_advertisement_data(local_name="x"), generate_advertisement_data(local_name="x"),
) )
} }

View File

@ -78,11 +78,9 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason(
address = "44:44:33:11:23:12" address = "44:44:33:11:23:12"
switchbot_device_signal_100 = generate_ble_device( switchbot_device_signal_100 = generate_ble_device(address, "wohand_signal_100")
address, "wohand_signal_100", rssi=-100
)
switchbot_adv_signal_100 = generate_advertisement_data( switchbot_adv_signal_100 = generate_advertisement_data(
local_name="wohand_signal_100", service_uuids=[] local_name="wohand_signal_100", service_uuids=[], rssi=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS
@ -93,11 +91,9 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason(
is switchbot_device_signal_100 is switchbot_device_signal_100
) )
switchbot_device_signal_99 = generate_ble_device( switchbot_device_signal_99 = generate_ble_device(address, "wohand_signal_99")
address, "wohand_signal_99", rssi=-99
)
switchbot_adv_signal_99 = generate_advertisement_data( switchbot_adv_signal_99 = generate_advertisement_data(
local_name="wohand_signal_99", service_uuids=[] local_name="wohand_signal_99", service_uuids=[], rssi=-99
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_99, switchbot_adv_signal_99, HCI0_SOURCE_ADDRESS hass, switchbot_device_signal_99, switchbot_adv_signal_99, HCI0_SOURCE_ADDRESS
@ -108,11 +104,9 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason(
is switchbot_device_signal_99 is switchbot_device_signal_99
) )
switchbot_device_signal_98 = generate_ble_device( switchbot_device_signal_98 = generate_ble_device(address, "wohand_good_signal")
address, "wohand_good_signal", rssi=-98
)
switchbot_adv_signal_98 = generate_advertisement_data( switchbot_adv_signal_98 = generate_advertisement_data(
local_name="wohand_good_signal", service_uuids=[] local_name="wohand_good_signal", service_uuids=[], rssi=-98
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_98, switchbot_adv_signal_98, HCI1_SOURCE_ADDRESS hass, switchbot_device_signal_98, switchbot_adv_signal_98, HCI1_SOURCE_ADDRESS
@ -805,13 +799,11 @@ async def test_goes_unavailable_connectable_only_and_recovers(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_non_connectable = generate_ble_device( switchbot_device_non_connectable = generate_ble_device(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -978,7 +970,6 @@ async def test_goes_unavailable_dismisses_discovery_and_makes_discoverable(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -1394,7 +1385,6 @@ async def test_bluetooth_rediscover(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -1571,7 +1561,6 @@ async def test_bluetooth_rediscover_no_match(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -1693,11 +1682,9 @@ async def test_async_register_disappeared_callback(
"""Test bluetooth async_register_disappeared_callback handles failures.""" """Test bluetooth async_register_disappeared_callback handles failures."""
address = "44:44:33:11:23:12" address = "44:44:33:11:23:12"
switchbot_device_signal_100 = generate_ble_device( switchbot_device_signal_100 = generate_ble_device(address, "wohand_signal_100")
address, "wohand_signal_100", rssi=-100
)
switchbot_adv_signal_100 = generate_advertisement_data( switchbot_adv_signal_100 = generate_advertisement_data(
local_name="wohand_signal_100", service_uuids=[] local_name="wohand_signal_100", service_uuids=[], rssi=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_100, switchbot_adv_signal_100, "hci0" hass, switchbot_device_signal_100, switchbot_adv_signal_100, "hci0"

View File

@ -124,7 +124,7 @@ async def test_wrapped_bleak_client_local_adapter_only(hass: HomeAssistant) -> N
"bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True
), ),
): ):
assert await client.connect() is True await client.connect()
assert client.is_connected is True assert client.is_connected is True
client.set_disconnected_callback(lambda client: None) client.set_disconnected_callback(lambda client: None)
await client.disconnect() await client.disconnect()
@ -145,7 +145,6 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected(
"source": "esp32_has_connection_slot", "source": "esp32_has_connection_slot",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-40,
) )
switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data( switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -215,7 +214,7 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected(
"bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True "bleak.backends.bluezdbus.client.BleakClientBlueZDBus.is_connected", True
), ),
): ):
assert await client.connect() is True await client.connect()
assert client.is_connected is True assert client.is_connected is True
client.set_disconnected_callback(lambda client: None) client.set_disconnected_callback(lambda client: None)
await client.disconnect() await client.disconnect()
@ -236,10 +235,9 @@ async def test_ble_device_with_proxy_client_out_of_connections_no_scanners(
"source": "esp32", "source": "esp32",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-30,
) )
switchbot_adv = generate_advertisement_data( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-30
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -275,10 +273,9 @@ async def test_ble_device_with_proxy_client_out_of_connections(
"source": "esp32", "source": "esp32",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-30,
) )
switchbot_adv = generate_advertisement_data( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-30
) )
class FakeScanner(FakeScannerMixin, BaseHaRemoteScanner): class FakeScanner(FakeScannerMixin, BaseHaRemoteScanner):
@ -340,10 +337,9 @@ async def test_ble_device_with_proxy_clear_cache(hass: HomeAssistant) -> None:
"source": "esp32", "source": "esp32",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-30,
) )
switchbot_adv = generate_advertisement_data( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-30
) )
class FakeScanner(FakeScannerMixin, BaseHaRemoteScanner): class FakeScanner(FakeScannerMixin, BaseHaRemoteScanner):
@ -417,7 +413,6 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
"source": "esp32_has_connection_slot", "source": "esp32_has_connection_slot",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-40,
) )
switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data( switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -511,7 +506,6 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
"source": "esp32_no_connection_slot", "source": "esp32_no_connection_slot",
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
rssi=-30,
) )
switchbot_proxy_device_no_connection_slot_adv = generate_advertisement_data( switchbot_proxy_device_no_connection_slot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
@ -538,7 +532,6 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{}, {},
rssi=-100,
) )
switchbot_device_adv = generate_advertisement_data( switchbot_device_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",

View File

@ -17,9 +17,7 @@ from . import generate_ble_device
MOCK_BLE_DEVICE = generate_ble_device( MOCK_BLE_DEVICE = generate_ble_device(
"00:00:00:00:00:00", "00:00:00:00:00:00",
"any", "any",
delegate="",
details={"path": "/dev/hci0/device"}, details={"path": "/dev/hci0/device"},
rssi=-127,
) )

View File

@ -38,11 +38,9 @@ async def test_subscribe_advertisements(
"""Test bluetooth subscribe_advertisements.""" """Test bluetooth subscribe_advertisements."""
address = "44:44:33:11:23:12" address = "44:44:33:11:23:12"
switchbot_device_signal_100 = generate_ble_device( switchbot_device_signal_100 = generate_ble_device(address, "wohand_signal_100")
address, "wohand_signal_100", rssi=-100
)
switchbot_adv_signal_100 = generate_advertisement_data( switchbot_adv_signal_100 = generate_advertisement_data(
local_name="wohand_signal_100", service_uuids=[] local_name="wohand_signal_100", service_uuids=[], rssi=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS
@ -68,7 +66,7 @@ async def test_subscribe_advertisements(
"connectable": True, "connectable": True,
"manufacturer_data": {}, "manufacturer_data": {},
"name": "wohand_signal_100", "name": "wohand_signal_100",
"rssi": -127, "rssi": -100,
"service_data": {}, "service_data": {},
"service_uuids": [], "service_uuids": [],
"source": HCI0_SOURCE_ADDRESS, "source": HCI0_SOURCE_ADDRESS,
@ -134,11 +132,9 @@ async def test_subscribe_connection_allocations(
"""Test bluetooth subscribe_connection_allocations.""" """Test bluetooth subscribe_connection_allocations."""
address = "44:44:33:11:23:12" address = "44:44:33:11:23:12"
switchbot_device_signal_100 = generate_ble_device( switchbot_device_signal_100 = generate_ble_device(address, "wohand_signal_100")
address, "wohand_signal_100", rssi=-100
)
switchbot_adv_signal_100 = generate_advertisement_data( switchbot_adv_signal_100 = generate_advertisement_data(
local_name="wohand_signal_100", service_uuids=[] local_name="wohand_signal_100", service_uuids=[], rssi=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS hass, switchbot_device_signal_100, switchbot_adv_signal_100, HCI0_SOURCE_ADDRESS

View File

@ -92,17 +92,13 @@ class FakeBleakClient(BaseFakeBleakClient):
async def connect(self, *args, **kwargs): async def connect(self, *args, **kwargs):
"""Connect.""" """Connect."""
@property
def is_connected(self):
"""Connected."""
return True return True
class FakeBleakClientFailsToConnect(BaseFakeBleakClient):
"""Fake bleak client that fails to connect."""
async def connect(self, *args, **kwargs):
"""Connect."""
return False
class FakeBleakClientRaisesOnConnect(BaseFakeBleakClient): class FakeBleakClientRaisesOnConnect(BaseFakeBleakClient):
"""Fake bleak client that raises on connect.""" """Fake bleak client that raises on connect."""
@ -110,6 +106,11 @@ class FakeBleakClientRaisesOnConnect(BaseFakeBleakClient):
"""Connect.""" """Connect."""
raise ConnectionError("Test exception") raise ConnectionError("Test exception")
@property
def is_connected(self):
"""Not connected."""
return False
def _generate_ble_device_and_adv_data( def _generate_ble_device_and_adv_data(
interface: str, mac: str, rssi: int interface: str, mac: str, rssi: int
@ -119,7 +120,6 @@ def _generate_ble_device_and_adv_data(
generate_ble_device( generate_ble_device(
mac, mac,
"any", "any",
delegate="",
details={"path": f"/org/bluez/{interface}/dev_{mac}"}, details={"path": f"/org/bluez/{interface}/dev_{mac}"},
), ),
generate_advertisement_data(rssi=rssi), generate_advertisement_data(rssi=rssi),
@ -144,16 +144,6 @@ def mock_platform_client_fixture():
yield yield
@pytest.fixture(name="mock_platform_client_that_fails_to_connect")
def mock_platform_client_that_fails_to_connect_fixture():
"""Fixture that mocks the platform client that fails to connect."""
with patch(
"habluetooth.wrappers.get_platform_client_backend_type",
return_value=FakeBleakClientFailsToConnect,
):
yield
@pytest.fixture(name="mock_platform_client_that_raises_on_connect") @pytest.fixture(name="mock_platform_client_that_raises_on_connect")
def mock_platform_client_that_raises_on_connect_fixture(): def mock_platform_client_that_raises_on_connect_fixture():
"""Fixture that mocks the platform client that fails to connect.""" """Fixture that mocks the platform client that fails to connect."""
@ -219,7 +209,8 @@ async def test_test_switch_adapters_when_out_of_slots(
): ):
ble_device = hci0_device_advs["00:00:00:00:00:01"][0] ble_device = hci0_device_advs["00:00:00:00:00:01"][0]
client = bleak.BleakClient(ble_device) client = bleak.BleakClient(ble_device)
assert await client.connect() is True await client.connect()
assert client.is_connected is True
assert allocate_slot_mock.call_count == 1 assert allocate_slot_mock.call_count == 1
assert release_slot_mock.call_count == 0 assert release_slot_mock.call_count == 0
@ -251,7 +242,8 @@ async def test_test_switch_adapters_when_out_of_slots(
): ):
ble_device = hci0_device_advs["00:00:00:00:00:03"][0] ble_device = hci0_device_advs["00:00:00:00:00:03"][0]
client = bleak.BleakClient(ble_device) client = bleak.BleakClient(ble_device)
assert await client.connect() is True await client.connect()
assert client.is_connected is True
assert release_slot_mock.call_count == 0 assert release_slot_mock.call_count == 0
cancel_hci0() cancel_hci0()
@ -262,7 +254,7 @@ async def test_test_switch_adapters_when_out_of_slots(
async def test_release_slot_on_connect_failure( async def test_release_slot_on_connect_failure(
hass: HomeAssistant, hass: HomeAssistant,
install_bleak_catcher, install_bleak_catcher,
mock_platform_client_that_fails_to_connect, mock_platform_client_that_raises_on_connect,
) -> None: ) -> None:
"""Ensure the slot gets released on connection failure.""" """Ensure the slot gets released on connection failure."""
manager = _get_manager() manager = _get_manager()
@ -278,7 +270,9 @@ async def test_release_slot_on_connect_failure(
): ):
ble_device = hci0_device_advs["00:00:00:00:00:01"][0] ble_device = hci0_device_advs["00:00:00:00:00:01"][0]
client = bleak.BleakClient(ble_device) client = bleak.BleakClient(ble_device)
assert await client.connect() is False with pytest.raises(ConnectionError):
await client.connect()
assert client.is_connected is False
assert allocate_slot_mock.call_count == 1 assert allocate_slot_mock.call_count == 1
assert release_slot_mock.call_count == 1 assert release_slot_mock.call_count == 1
@ -335,13 +329,18 @@ async def test_passing_subclassed_str_as_address(
async def connect(self, *args, **kwargs): async def connect(self, *args, **kwargs):
"""Connect.""" """Connect."""
@property
def is_connected(self):
"""Connected."""
return True return True
with patch( with patch(
"habluetooth.wrappers.get_platform_client_backend_type", "habluetooth.wrappers.get_platform_client_backend_type",
return_value=FakeBleakClient, return_value=FakeBleakClient,
): ):
assert await client.connect() is True await client.connect()
assert client.is_connected is True
cancel_hci0() cancel_hci0()
cancel_hci1() cancel_hci1()

View File

@ -55,4 +55,4 @@ async def test_client_usage_while_not_connected(client_data: ESPHomeClientData)
with pytest.raises( with pytest.raises(
BleakError, match=f"{ESP_NAME}.*{ESP_MAC_ADDRESS}.*not connected" BleakError, match=f"{ESP_NAME}.*{ESP_MAC_ADDRESS}.*not connected"
): ):
assert await client.write_gatt_char("test", b"test") is False assert await client.write_gatt_char("test", b"test", False) is False