hass-core/tests/components/bluetooth/test_api.py
J. Nick Koston 2a5ffa9a5b
Fix timestamps for bluetooth scanners that bundle advertisements (#94511)
#94138 added support for raw/bundled advertisements. We should use the
same monotonic time for all advertisements in the bundle if not time
is passed, or calculate the timestamp and pass it if its known
2023-06-14 21:47:00 -04:00

158 lines
5.4 KiB
Python

"""Tests for the Bluetooth integration API."""
import time
from bleak.backends.scanner import AdvertisementData, BLEDevice
import pytest
from homeassistant.components import bluetooth
from homeassistant.components.bluetooth import (
MONOTONIC_TIME,
BaseHaRemoteScanner,
BaseHaScanner,
HaBluetoothConnector,
async_scanner_by_source,
async_scanner_devices_by_address,
)
from homeassistant.core import HomeAssistant
from . import (
FakeScanner,
MockBleakClient,
_get_manager,
generate_advertisement_data,
generate_ble_device,
)
async def test_scanner_by_source(hass: HomeAssistant, enable_bluetooth: None) -> None:
"""Test we can get a scanner by source."""
hci2_scanner = FakeScanner(hass, "hci2", "hci2")
cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True)
assert async_scanner_by_source(hass, "hci2") is hci2_scanner
cancel_hci2()
assert async_scanner_by_source(hass, "hci2") is None
async def test_monotonic_time() -> None:
"""Test monotonic time."""
assert MONOTONIC_TIME() == pytest.approx(time.monotonic(), abs=0.1)
async def test_async_scanner_devices_by_address_connectable(
hass: HomeAssistant, enable_bluetooth: None
) -> None:
"""Test getting scanner devices by address with connectable devices."""
manager = _get_manager()
class FakeInjectableScanner(BaseHaRemoteScanner):
def inject_advertisement(
self, device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
"""Inject an advertisement."""
self._async_on_advertisement(
device.address,
advertisement_data.rssi,
device.name,
advertisement_data.service_uuids,
advertisement_data.service_data,
advertisement_data.manufacturer_data,
advertisement_data.tx_power,
{"scanner_specific_data": "test"},
MONOTONIC_TIME(),
)
new_info_callback = manager.scanner_adv_received
connector = (
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
)
scanner = FakeInjectableScanner(
hass, "esp32", "esp32", new_info_callback, connector, False
)
unsetup = scanner.async_setup()
cancel = manager.async_register_scanner(scanner, True)
switchbot_device = generate_ble_device(
"44:44:33:11:23:45",
"wohand",
{},
rssi=-100,
)
switchbot_device_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"],
service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"},
manufacturer_data={1: b"\x01"},
rssi=-100,
)
scanner.inject_advertisement(switchbot_device, switchbot_device_adv)
assert async_scanner_devices_by_address(
hass, switchbot_device.address, connectable=True
) == async_scanner_devices_by_address(hass, "44:44:33:11:23:45", connectable=False)
devices = async_scanner_devices_by_address(
hass, switchbot_device.address, connectable=False
)
assert len(devices) == 1
assert devices[0].scanner == scanner
assert devices[0].ble_device.name == switchbot_device.name
assert devices[0].advertisement.local_name == switchbot_device_adv.local_name
unsetup()
cancel()
async def test_async_scanner_devices_by_address_non_connectable(
hass: HomeAssistant, enable_bluetooth: None
) -> None:
"""Test getting scanner devices by address with non-connectable devices."""
manager = _get_manager()
switchbot_device = generate_ble_device(
"44:44:33:11:23:45",
"wohand",
{},
rssi=-100,
)
switchbot_device_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"],
service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"},
manufacturer_data={1: b"\x01"},
rssi=-100,
)
class FakeStaticScanner(BaseHaScanner):
@property
def discovered_devices(self) -> list[BLEDevice]:
"""Return a list of discovered devices."""
return [switchbot_device]
@property
def discovered_devices_and_advertisement_data(
self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices and their advertisement data."""
return {switchbot_device.address: (switchbot_device, switchbot_device_adv)}
connector = (
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
)
scanner = FakeStaticScanner(hass, "esp32", "esp32", connector)
cancel = manager.async_register_scanner(scanner, False)
assert scanner.discovered_devices_and_advertisement_data == {
switchbot_device.address: (switchbot_device, switchbot_device_adv)
}
assert (
async_scanner_devices_by_address(
hass, switchbot_device.address, connectable=True
)
== []
)
devices = async_scanner_devices_by_address(
hass, switchbot_device.address, connectable=False
)
assert len(devices) == 1
assert devices[0].scanner == scanner
assert devices[0].ble_device.name == switchbot_device.name
assert devices[0].advertisement.local_name == switchbot_device_adv.local_name
cancel()