Skip to content

Commit

Permalink
Add support for bleak 0.21 (home-assistant#97212)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Jul 25, 2023
1 parent 213a169 commit 6ae7952
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 3 deletions.
24 changes: 21 additions & 3 deletions homeassistant/components/bluetooth/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
import contextlib
from dataclasses import dataclass
from functools import partial
import inspect
import logging
from typing import TYPE_CHECKING, Any, Final

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.scanner import AdvertisementDataCallback, BaseBleakScanner
from bleak.backends.scanner import (
AdvertisementData,
AdvertisementDataCallback,
BaseBleakScanner,
)
from bleak_retry_connector import (
NO_RSSI_VALUE,
ble_device_description,
Expand Down Expand Up @@ -58,6 +63,7 @@ def __init__(
self._detection_cancel: CALLBACK_TYPE | None = None
self._mapped_filters: dict[str, set[str]] = {}
self._advertisement_data_callback: AdvertisementDataCallback | None = None
self._background_tasks: set[asyncio.Task] = set()
remapped_kwargs = {
"detection_callback": detection_callback,
"service_uuids": service_uuids or [],
Expand Down Expand Up @@ -128,12 +134,24 @@ def _setup_detection_callback(self) -> None:
"""Set up the detection callback."""
if self._advertisement_data_callback is None:
return
callback = self._advertisement_data_callback
self._cancel_callback()
super().register_detection_callback(self._advertisement_data_callback)
assert models.MANAGER is not None
assert self._callback is not None

if not inspect.iscoroutinefunction(callback):
detection_callback = callback
else:

def detection_callback(
ble_device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
task = asyncio.create_task(callback(ble_device, advertisement_data))
self._background_tasks.add(task)
task.add_done_callback(self._background_tasks.discard)

self._detection_cancel = models.MANAGER.async_register_bleak_callback(
self._callback, self._mapped_filters
detection_callback, self._mapped_filters
)

def __del__(self) -> None:
Expand Down
59 changes: 59 additions & 0 deletions tests/components/bluetooth/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -2386,6 +2386,65 @@ def _device_detected(
assert len(detected) == 2


async def test_wrapped_instance_with_service_uuids_with_coro_callback(
hass: HomeAssistant, mock_bleak_scanner_start: MagicMock, enable_bluetooth: None
) -> None:
"""Test consumers can use the wrapped instance with a service_uuids list as if it was normal BleakScanner.
Verify that coro callbacks are supported.
"""
with patch(
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=[]
):
await async_setup_with_default_adapter(hass)

with patch.object(hass.config_entries.flow, "async_init"):
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()

detected = []

async def _device_detected(
device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
"""Handle a detected device."""
detected.append((device, advertisement_data))

switchbot_device = generate_ble_device("44:44:33:11:23:45", "wohand")
switchbot_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
)
switchbot_adv_2 = generate_advertisement_data(
local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
)
empty_device = generate_ble_device("11:22:33:44:55:66", "empty")
empty_adv = generate_advertisement_data(local_name="empty")

assert _get_manager() is not None
scanner = HaBleakScannerWrapper(
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
)
scanner.register_detection_callback(_device_detected)

inject_advertisement(hass, switchbot_device, switchbot_adv)
inject_advertisement(hass, switchbot_device, switchbot_adv_2)

await hass.async_block_till_done()

assert len(detected) == 2

# The UUIDs list we created in the wrapped scanner with should be respected
# and we should not get another callback
inject_advertisement(hass, empty_device, empty_adv)
assert len(detected) == 2


async def test_wrapped_instance_with_broken_callbacks(
hass: HomeAssistant, mock_bleak_scanner_start: MagicMock, enable_bluetooth: None
) -> None:
Expand Down

0 comments on commit 6ae7952

Please sign in to comment.