2022-07-08 23:55:31 +00:00
|
|
|
"""Tests for the Bluetooth integration."""
|
2022-07-22 23:12:08 +00:00
|
|
|
|
|
|
|
|
2022-08-17 20:51:56 +00:00
|
|
|
import time
|
|
|
|
from unittest.mock import patch
|
2022-07-22 23:12:08 +00:00
|
|
|
|
2022-08-17 20:51:56 +00:00
|
|
|
from bleak.backends.scanner import AdvertisementData, BLEDevice
|
|
|
|
|
2022-08-22 18:02:26 +00:00
|
|
|
from homeassistant.components.bluetooth import (
|
|
|
|
DOMAIN,
|
|
|
|
SOURCE_LOCAL,
|
|
|
|
async_get_advertisement_callback,
|
|
|
|
models,
|
|
|
|
)
|
2022-08-19 01:41:07 +00:00
|
|
|
from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS
|
2022-08-17 20:51:56 +00:00
|
|
|
from homeassistant.components.bluetooth.manager import BluetoothManager
|
2022-08-19 01:41:07 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
|
|
|
|
from tests.common import MockConfigEntry
|
2022-08-17 20:51:56 +00:00
|
|
|
|
2022-09-16 10:24:20 +00:00
|
|
|
__all__ = (
|
|
|
|
"inject_advertisement",
|
|
|
|
"inject_advertisement_with_source",
|
|
|
|
"inject_advertisement_with_time_and_source",
|
|
|
|
"inject_advertisement_with_time_and_source_connectable",
|
|
|
|
"inject_bluetooth_service_info",
|
|
|
|
"patch_all_discovered_devices",
|
|
|
|
"patch_discovered_devices",
|
|
|
|
)
|
|
|
|
|
2022-08-17 20:51:56 +00:00
|
|
|
|
|
|
|
def _get_manager() -> BluetoothManager:
|
|
|
|
"""Return the bluetooth manager."""
|
|
|
|
return models.MANAGER
|
|
|
|
|
|
|
|
|
2022-08-22 18:02:26 +00:00
|
|
|
def inject_advertisement(
|
|
|
|
hass: HomeAssistant, device: BLEDevice, adv: AdvertisementData
|
|
|
|
) -> None:
|
2022-08-17 22:38:04 +00:00
|
|
|
"""Inject an advertisement into the manager."""
|
2022-08-22 18:02:26 +00:00
|
|
|
return inject_advertisement_with_source(hass, device, adv, SOURCE_LOCAL)
|
2022-08-17 22:38:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
def inject_advertisement_with_source(
|
2022-08-22 18:02:26 +00:00
|
|
|
hass: HomeAssistant, device: BLEDevice, adv: AdvertisementData, source: str
|
2022-08-17 22:38:04 +00:00
|
|
|
) -> None:
|
|
|
|
"""Inject an advertisement into the manager from a specific source."""
|
2022-08-22 18:02:26 +00:00
|
|
|
inject_advertisement_with_time_and_source(
|
|
|
|
hass, device, adv, time.monotonic(), source
|
|
|
|
)
|
2022-08-17 22:38:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
def inject_advertisement_with_time_and_source(
|
2022-08-22 18:02:26 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
device: BLEDevice,
|
|
|
|
adv: AdvertisementData,
|
|
|
|
time: float,
|
|
|
|
source: str,
|
2022-08-17 22:38:04 +00:00
|
|
|
) -> None:
|
|
|
|
"""Inject an advertisement into the manager from a specific source at a time."""
|
2022-08-22 18:02:26 +00:00
|
|
|
inject_advertisement_with_time_and_source_connectable(
|
|
|
|
hass, device, adv, time, source, True
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def inject_advertisement_with_time_and_source_connectable(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
device: BLEDevice,
|
|
|
|
adv: AdvertisementData,
|
|
|
|
time: float,
|
|
|
|
source: str,
|
|
|
|
connectable: bool,
|
|
|
|
) -> None:
|
|
|
|
"""Inject an advertisement into the manager from a specific source at a time and connectable status."""
|
|
|
|
async_get_advertisement_callback(hass)(
|
|
|
|
models.BluetoothServiceInfoBleak(
|
|
|
|
name=adv.local_name or device.name or device.address,
|
|
|
|
address=device.address,
|
|
|
|
rssi=device.rssi,
|
|
|
|
manufacturer_data=adv.manufacturer_data,
|
|
|
|
service_data=adv.service_data,
|
|
|
|
service_uuids=adv.service_uuids,
|
|
|
|
source=source,
|
|
|
|
device=device,
|
|
|
|
advertisement=adv,
|
|
|
|
connectable=connectable,
|
|
|
|
time=time,
|
|
|
|
)
|
|
|
|
)
|
2022-08-17 20:51:56 +00:00
|
|
|
|
|
|
|
|
2022-09-16 10:24:20 +00:00
|
|
|
def inject_bluetooth_service_info_bleak(
|
|
|
|
hass: HomeAssistant, info: models.BluetoothServiceInfoBleak
|
|
|
|
) -> None:
|
|
|
|
"""Inject an advertisement into the manager with connectable status."""
|
|
|
|
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call]
|
|
|
|
local_name=None if info.name == "" else info.name,
|
|
|
|
manufacturer_data=info.manufacturer_data,
|
|
|
|
service_data=info.service_data,
|
|
|
|
service_uuids=info.service_uuids,
|
|
|
|
)
|
|
|
|
device = BLEDevice( # type: ignore[no-untyped-call]
|
|
|
|
address=info.address,
|
|
|
|
name=info.name,
|
|
|
|
details={},
|
|
|
|
rssi=info.rssi,
|
|
|
|
)
|
|
|
|
inject_advertisement_with_time_and_source_connectable(
|
|
|
|
hass,
|
|
|
|
device,
|
|
|
|
advertisement_data,
|
|
|
|
info.time,
|
|
|
|
SOURCE_LOCAL,
|
|
|
|
connectable=info.connectable,
|
2022-08-17 20:51:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-09-16 10:24:20 +00:00
|
|
|
def inject_bluetooth_service_info(
|
|
|
|
hass: HomeAssistant, info: models.BluetoothServiceInfo
|
|
|
|
) -> None:
|
|
|
|
"""Inject a BluetoothServiceInfo into the manager."""
|
|
|
|
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call]
|
|
|
|
local_name=None if info.name == "" else info.name,
|
|
|
|
manufacturer_data=info.manufacturer_data,
|
|
|
|
service_data=info.service_data,
|
|
|
|
service_uuids=info.service_uuids,
|
|
|
|
)
|
|
|
|
device = BLEDevice( # type: ignore[no-untyped-call]
|
|
|
|
address=info.address,
|
|
|
|
name=info.name,
|
|
|
|
details={},
|
|
|
|
rssi=info.rssi,
|
|
|
|
)
|
|
|
|
inject_advertisement(hass, device, advertisement_data)
|
2022-08-22 18:02:26 +00:00
|
|
|
|
|
|
|
|
2022-09-16 10:24:20 +00:00
|
|
|
def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
|
|
|
"""Mock all the discovered devices from all the scanners."""
|
|
|
|
return patch.object(
|
|
|
|
_get_manager(), "async_all_discovered_devices", return_value=mock_discovered
|
|
|
|
)
|
2022-08-22 18:02:26 +00:00
|
|
|
|
|
|
|
|
2022-08-17 20:51:56 +00:00
|
|
|
def patch_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
|
|
|
"""Mock the combined best path to discovered devices from all the scanners."""
|
|
|
|
return patch.object(
|
2022-08-22 18:02:26 +00:00
|
|
|
_get_manager(), "async_discovered_devices", return_value=mock_discovered
|
2022-08-17 20:51:56 +00:00
|
|
|
)
|
2022-08-19 01:41:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_with_default_adapter(hass: HomeAssistant) -> MockConfigEntry:
|
|
|
|
"""Set up the Bluetooth integration with a default adapter."""
|
|
|
|
return await _async_setup_with_adapter(hass, DEFAULT_ADDRESS)
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_with_one_adapter(hass: HomeAssistant) -> MockConfigEntry:
|
|
|
|
"""Set up the Bluetooth integration with one adapter."""
|
|
|
|
return await _async_setup_with_adapter(hass, "00:00:00:00:00:01")
|
|
|
|
|
|
|
|
|
|
|
|
async def _async_setup_with_adapter(
|
|
|
|
hass: HomeAssistant, address: str
|
|
|
|
) -> MockConfigEntry:
|
|
|
|
"""Set up the Bluetooth integration with any adapter."""
|
|
|
|
entry = MockConfigEntry(domain="bluetooth", unique_id=address)
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
return entry
|