Bump bleak to 0.19.0 (#80349)

This commit is contained in:
J. Nick Koston 2022-10-15 07:57:23 -10:00 committed by GitHub
parent 3460e0b074
commit d12cbab6c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 638 additions and 390 deletions

View file

@ -74,4 +74,4 @@ ADAPTER_HW_VERSION: Final = "hw_version"
ADAPTER_PASSIVE_SCAN: Final = "passive_scan" ADAPTER_PASSIVE_SCAN: Final = "passive_scan"
NO_RSSI_VALUE: Final = -1000 NO_RSSI_VALUE: Final = -127

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable
from dataclasses import replace
from datetime import datetime, timedelta from datetime import datetime, timedelta
import itertools import itertools
import logging import logging
@ -121,7 +122,8 @@ class BluetoothManager:
self._bleak_callbacks: list[ self._bleak_callbacks: list[
tuple[AdvertisementDataCallback, dict[str, set[str]]] tuple[AdvertisementDataCallback, dict[str, set[str]]]
] = [] ] = []
self._history: dict[str, BluetoothServiceInfoBleak] = {} self._all_history: dict[str, BluetoothServiceInfoBleak] = {}
self._non_connectable_history: dict[str, BluetoothServiceInfoBleak] = {}
self._connectable_history: dict[str, BluetoothServiceInfoBleak] = {} self._connectable_history: dict[str, BluetoothServiceInfoBleak] = {}
self._non_connectable_scanners: list[BaseHaScanner] = [] self._non_connectable_scanners: list[BaseHaScanner] = []
self._connectable_scanners: list[BaseHaScanner] = [] self._connectable_scanners: list[BaseHaScanner] = []
@ -155,8 +157,9 @@ class BluetoothManager:
service_info.as_dict() service_info.as_dict()
for service_info in self._connectable_history.values() for service_info in self._connectable_history.values()
], ],
"history": [ "non_connectable_history": [
service_info.as_dict() for service_info in self._history.values() service_info.as_dict()
for service_info in self._non_connectable_history.values()
], ],
"advertisement_tracker": self._advertisement_tracker.async_diagnostics(), "advertisement_tracker": self._advertisement_tracker.async_diagnostics(),
} }
@ -189,7 +192,7 @@ class BluetoothManager:
# Everything is connectable so it fall into both # Everything is connectable so it fall into both
# buckets since the host system can only provide # buckets since the host system can only provide
# connectable devices # connectable devices
self._history = history.copy() self._all_history = history.copy()
self._connectable_history = history.copy() self._connectable_history = history.copy()
self.async_setup_unavailable_tracking() self.async_setup_unavailable_tracking()
@ -202,32 +205,32 @@ class BluetoothManager:
self._cancel_unavailable_tracking = None self._cancel_unavailable_tracking = None
uninstall_multiple_bleak_catcher() uninstall_multiple_bleak_catcher()
async def async_get_devices_by_address( @hass_callback
def async_get_discovered_devices_and_advertisement_data_by_address(
self, address: str, connectable: bool self, address: str, connectable: bool
) -> list[BLEDevice]: ) -> list[tuple[BLEDevice, AdvertisementData]]:
"""Get devices by address.""" """Get devices and advertisement_data by address."""
types_ = (True,) if connectable else (True, False) types_ = (True,) if connectable else (True, False)
return [ return [
device device_advertisement_data
for device in await asyncio.gather( for device_advertisement_data in (
*( scanner.discovered_devices_and_advertisement_data.get(address)
scanner.async_get_device_by_address(address) for type_ in types_
for type_ in types_ for scanner in self._get_scanners_by_type(type_)
for scanner in self._get_scanners_by_type(type_)
)
) )
if device is not None if device_advertisement_data is not None
] ]
@hass_callback @hass_callback
def async_all_discovered_devices(self, connectable: bool) -> Iterable[BLEDevice]: def _async_all_discovered_addresses(self, connectable: bool) -> Iterable[str]:
"""Return all of discovered devices from all the scanners including duplicates.""" """Return all of discovered addresses from all the scanners including duplicates."""
yield from itertools.chain.from_iterable( yield from itertools.chain.from_iterable(
scanner.discovered_devices for scanner in self._get_scanners_by_type(True) scanner.discovered_devices_and_advertisement_data
for scanner in self._get_scanners_by_type(True)
) )
if not connectable: if not connectable:
yield from itertools.chain.from_iterable( yield from itertools.chain.from_iterable(
scanner.discovered_devices scanner.discovered_devices_and_advertisement_data
for scanner in self._get_scanners_by_type(False) for scanner in self._get_scanners_by_type(False)
) )
@ -253,33 +256,38 @@ class BluetoothManager:
"""Watch for unavailable devices and cleanup state history.""" """Watch for unavailable devices and cleanup state history."""
monotonic_now = MONOTONIC_TIME() monotonic_now = MONOTONIC_TIME()
connectable_history = self._connectable_history connectable_history = self._connectable_history
all_history = self._history non_connectable_history = self._non_connectable_history
removed_addresses: set[str] = set() all_history = self._all_history
tracker = self._advertisement_tracker
intervals = tracker.intervals
for connectable in (True, False): for connectable in (True, False):
unavailable_callbacks = self._get_unavailable_callbacks_by_type(connectable) unavailable_callbacks = self._get_unavailable_callbacks_by_type(connectable)
intervals = self._advertisement_tracker.intervals
history = connectable_history if connectable else all_history history = connectable_history if connectable else all_history
history_set = set(history) disappeared = set(history).difference(
active_addresses = { self._async_all_discovered_addresses(connectable)
device.address )
for device in self.async_all_discovered_devices(connectable)
}
disappeared = history_set.difference(active_addresses)
for address in disappeared: for address in disappeared:
# if not connectable:
# For non-connectable devices we also check the device has exceeded #
# the advertising interval before we mark it as unavailable # For non-connectable devices we also check the device has exceeded
# since it may have gone to sleep and since we do not need an active connection # the advertising interval before we mark it as unavailable
# to it we can only determine its availability by the lack of advertisements # since it may have gone to sleep and since we do not need an active connection
# # to it we can only determine its availability by the lack of advertisements
if not connectable and (advertising_interval := intervals.get(address)): #
time_since_seen = monotonic_now - history[address].time if advertising_interval := intervals.get(address):
if time_since_seen <= advertising_interval: time_since_seen = monotonic_now - all_history[address].time
continue if time_since_seen <= advertising_interval:
continue
non_connectable_history.pop(address, None)
# The second loop (connectable=False) is responsible for removing
# the device from all the interval tracking since it is no longer
# available for both connectable and non-connectable
tracker.async_remove_address(address)
service_info = history.pop(address) service_info = history.pop(address)
removed_addresses.add(address)
if not (callbacks := unavailable_callbacks.get(address)): if not (callbacks := unavailable_callbacks.get(address)):
continue continue
@ -290,14 +298,10 @@ class BluetoothManager:
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.exception("Error in unavailable callback") _LOGGER.exception("Error in unavailable callback")
# If we removed the device from both the connectable history
# and all history then we can remove it from the advertisement tracker
for address in removed_addresses:
if address not in connectable_history and address not in all_history:
self._advertisement_tracker.async_remove_address(address)
def _prefer_previous_adv_from_different_source( def _prefer_previous_adv_from_different_source(
self, old: BluetoothServiceInfoBleak, new: BluetoothServiceInfoBleak self,
old: BluetoothServiceInfoBleak,
new: BluetoothServiceInfoBleak,
) -> bool: ) -> bool:
"""Prefer previous advertisement from a different source if it is better.""" """Prefer previous advertisement from a different source if it is better."""
if new.time - old.time > ( if new.time - old.time > (
@ -308,8 +312,8 @@ class BluetoothManager:
# If the old advertisement is stale, any new advertisement is preferred # If the old advertisement is stale, any new advertisement is preferred
_LOGGER.debug( _LOGGER.debug(
"%s (%s): Switching from %s[%s] to %s[%s] (time elapsed:%s > stale seconds:%s)", "%s (%s): Switching from %s[%s] to %s[%s] (time elapsed:%s > stale seconds:%s)",
new.advertisement.local_name, new.name,
new.device.address, new.address,
old.source, old.source,
old.connectable, old.connectable,
new.source, new.source,
@ -318,19 +322,21 @@ class BluetoothManager:
stale_seconds, stale_seconds,
) )
return False return False
if new.device.rssi - RSSI_SWITCH_THRESHOLD > (old.device.rssi or NO_RSSI_VALUE): if (new.rssi or NO_RSSI_VALUE) - RSSI_SWITCH_THRESHOLD > (
old.rssi or NO_RSSI_VALUE
):
# If new advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred # If new advertisement is RSSI_SWITCH_THRESHOLD more, the new one is preferred
_LOGGER.debug( _LOGGER.debug(
"%s (%s): Switching from %s[%s] to %s[%s] (new rssi:%s - threshold:%s > old rssi:%s)", "%s (%s): Switching from %s[%s] to %s[%s] (new rssi:%s - threshold:%s > old rssi:%s)",
new.advertisement.local_name, new.name,
new.device.address, new.address,
old.source, old.source,
old.connectable, old.connectable,
new.source, new.source,
new.connectable, new.connectable,
new.device.rssi, new.rssi,
RSSI_SWITCH_THRESHOLD, RSSI_SWITCH_THRESHOLD,
old.device.rssi, old.rssi,
) )
return False return False
return True return True
@ -355,9 +361,9 @@ class BluetoothManager:
return return
device = service_info.device device = service_info.device
connectable = service_info.connectable
address = device.address address = device.address
all_history = self._connectable_history if connectable else self._history all_history = self._all_history
source = service_info.source source = service_info.source
if ( if (
(old_service_info := all_history.get(address)) (old_service_info := all_history.get(address))
@ -368,11 +374,11 @@ class BluetoothManager:
): ):
return return
self._history[address] = service_info if connectable := service_info.connectable:
if connectable:
self._connectable_history[address] = service_info self._connectable_history[address] = service_info
# Bleak callbacks must get a connectable device else:
self._non_connectable_history[address] = service_info
all_history[address] = service_info
# Track advertisement intervals to determine when we need to # Track advertisement intervals to determine when we need to
# switch adapters or mark a device as unavailable # switch adapters or mark a device as unavailable
@ -393,11 +399,18 @@ class BluetoothManager:
): ):
return return
if connectable: if is_connectable_by_any_source := address in self._connectable_history:
# Bleak callbacks must get a connectable device # Bleak callbacks must get a connectable device
for callback_filters in self._bleak_callbacks: for callback_filters in self._bleak_callbacks:
_dispatch_bleak_callback(*callback_filters, device, advertisement_data) _dispatch_bleak_callback(*callback_filters, device, advertisement_data)
if not connectable and is_connectable_by_any_source:
# Since we have a connectable path and our BleakClient will
# route any connection attempts to the connectable path, we
# mark the service_info as connectable so that the callbacks
# will be called and the device can be discovered.
service_info = replace(service_info, connectable=True)
matched_domains = self._integration_matcher.match_domains(service_info) matched_domains = self._integration_matcher.match_domains(service_info)
_LOGGER.debug( _LOGGER.debug(
"%s: %s %s connectable: %s match: %s rssi: %s", "%s: %s %s connectable: %s match: %s rssi: %s",
@ -406,7 +419,7 @@ class BluetoothManager:
advertisement_data, advertisement_data,
connectable, connectable,
matched_domains, matched_domains,
device.rssi, advertisement_data.rssi,
) )
for match in self._callback_index.match_callbacks(service_info): for match in self._callback_index.match_callbacks(service_info):
@ -518,27 +531,23 @@ class BluetoothManager:
def _get_scanners_by_type(self, connectable: bool) -> list[BaseHaScanner]: def _get_scanners_by_type(self, connectable: bool) -> list[BaseHaScanner]:
"""Return the scanners by type.""" """Return the scanners by type."""
return ( if connectable:
self._connectable_scanners return self._connectable_scanners
if connectable return self._non_connectable_scanners
else self._non_connectable_scanners
)
def _get_unavailable_callbacks_by_type( def _get_unavailable_callbacks_by_type(
self, connectable: bool self, connectable: bool
) -> dict[str, list[Callable[[BluetoothServiceInfoBleak], None]]]: ) -> dict[str, list[Callable[[BluetoothServiceInfoBleak], None]]]:
"""Return the unavailable callbacks by type.""" """Return the unavailable callbacks by type."""
return ( if connectable:
self._connectable_unavailable_callbacks return self._connectable_unavailable_callbacks
if connectable return self._unavailable_callbacks
else self._unavailable_callbacks
)
def _get_history_by_type( def _get_history_by_type(
self, connectable: bool self, connectable: bool
) -> dict[str, BluetoothServiceInfoBleak]: ) -> dict[str, BluetoothServiceInfoBleak]:
"""Return the history by type.""" """Return the history by type."""
return self._connectable_history if connectable else self._history return self._connectable_history if connectable else self._all_history
def async_register_scanner( def async_register_scanner(
self, scanner: BaseHaScanner, connectable: bool self, scanner: BaseHaScanner, connectable: bool

View file

@ -6,8 +6,8 @@
"after_dependencies": ["hassio"], "after_dependencies": ["hassio"],
"quality_scale": "internal", "quality_scale": "internal",
"requirements": [ "requirements": [
"bleak==0.18.1", "bleak==0.19.0",
"bleak-retry-connector==2.1.3", "bleak-retry-connector==2.2.0",
"bluetooth-adapters==0.6.0", "bluetooth-adapters==0.6.0",
"bluetooth-auto-recovery==0.3.4", "bluetooth-auto-recovery==0.3.4",
"dbus-fast==1.45.0" "dbus-fast==1.45.0"

View file

@ -115,9 +115,12 @@ class BaseHaScanner:
def discovered_devices(self) -> list[BLEDevice]: def discovered_devices(self) -> list[BLEDevice]:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
@property
@abstractmethod @abstractmethod
async def async_get_device_by_address(self, address: str) -> BLEDevice | None: def discovered_devices_and_advertisement_data(
"""Get a device by address.""" self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices and their advertisement data."""
async def async_diagnostics(self) -> dict[str, Any]: async def async_diagnostics(self) -> dict[str, Any]:
"""Return diagnostic information about the scanner.""" """Return diagnostic information about the scanner."""
@ -127,7 +130,6 @@ class BaseHaScanner:
{ {
"name": device.name, "name": device.name,
"address": device.address, "address": device.address,
"rssi": device.rssi,
} }
for device in self.discovered_devices for device in self.discovered_devices
], ],
@ -285,7 +287,7 @@ class HaBleakClientWrapper(BleakClient):
"""Connect to the specified GATT server.""" """Connect to the specified GATT server."""
if not self._backend: if not self._backend:
wrapped_backend = ( wrapped_backend = (
self._async_get_backend() or await self._async_get_fallback_backend() self._async_get_backend() or self._async_get_fallback_backend()
) )
self._backend = wrapped_backend.client( self._backend = wrapped_backend.client(
await freshen_ble_device(wrapped_backend.device) await freshen_ble_device(wrapped_backend.device)
@ -329,7 +331,8 @@ class HaBleakClientWrapper(BleakClient):
return None return None
async def _async_get_fallback_backend(self) -> _HaWrappedBleakBackend: @hass_callback
def _async_get_fallback_backend(self) -> _HaWrappedBleakBackend:
"""Get a fallback backend for the given address.""" """Get a fallback backend for the given address."""
# #
# The preferred backend cannot currently connect the device # The preferred backend cannot currently connect the device
@ -340,13 +343,20 @@ class HaBleakClientWrapper(BleakClient):
# #
assert MANAGER is not None assert MANAGER is not None
address = self.__address address = self.__address
devices = await MANAGER.async_get_devices_by_address(address, True) device_advertisement_datas = (
for ble_device in sorted( MANAGER.async_get_discovered_devices_and_advertisement_data_by_address(
devices, address, True
key=lambda ble_device: ble_device.rssi or NO_RSSI_VALUE, )
)
for device_advertisement_data in sorted(
device_advertisement_datas,
key=lambda device_advertisement_data: device_advertisement_data[1].rssi
or NO_RSSI_VALUE,
reverse=True, reverse=True,
): ):
if backend := self._async_get_backend_for_ble_device(ble_device): if backend := self._async_get_backend_for_ble_device(
device_advertisement_data[0]
):
return backend return backend
raise BleakError( raise BleakError(

View file

@ -17,7 +17,6 @@ from bleak.backends.bluezdbus.advertisement_monitor import OrPattern
from bleak.backends.bluezdbus.scanner import BlueZScannerArgs from bleak.backends.bluezdbus.scanner import BlueZScannerArgs
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback
from bleak_retry_connector import get_device_by_adapter
from dbus_fast import InvalidMessageError from dbus_fast import InvalidMessageError
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
@ -144,6 +143,13 @@ class HaScanner(BaseHaScanner):
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
return self.scanner.discovered_devices return self.scanner.discovered_devices
@property
def discovered_devices_and_advertisement_data(
self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices and advertisement data."""
return self.scanner.discovered_devices_and_advertisement_data
@hass_callback @hass_callback
def async_setup(self) -> None: def async_setup(self) -> None:
"""Set up the scanner.""" """Set up the scanner."""
@ -151,16 +157,6 @@ class HaScanner(BaseHaScanner):
self._async_detection_callback, self.mode, self.adapter self._async_detection_callback, self.mode, self.adapter
) )
async def async_get_device_by_address(self, address: str) -> BLEDevice | None:
"""Get a device by address."""
if platform.system() == "Linux":
return await get_device_by_adapter(address, self.adapter)
# We don't have a fast version of this for MacOS yet
return next(
(device for device in self.discovered_devices if device.address == address),
None,
)
async def async_diagnostics(self) -> dict[str, Any]: async def async_diagnostics(self) -> dict[str, Any]:
"""Return diagnostic information about the scanner.""" """Return diagnostic information about the scanner."""
base_diag = await super().async_diagnostics() base_diag = await super().async_diagnostics()

View file

@ -37,7 +37,9 @@ class ESPHomeScanner(BaseHaScanner):
"""Initialize the scanner.""" """Initialize the scanner."""
super().__init__(hass, scanner_id) super().__init__(hass, scanner_id)
self._new_info_callback = new_info_callback self._new_info_callback = new_info_callback
self._discovered_devices: dict[str, BLEDevice] = {} self._discovered_device_advertisement_datas: dict[
str, tuple[BLEDevice, AdvertisementData]
] = {}
self._discovered_device_timestamps: dict[str, float] = {} self._discovered_device_timestamps: dict[str, float] = {}
self._connector = connector self._connector = connector
self._connectable = connectable self._connectable = connectable
@ -61,17 +63,23 @@ class ESPHomeScanner(BaseHaScanner):
if now - timestamp > FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS if now - timestamp > FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
] ]
for address in expired: for address in expired:
del self._discovered_devices[address] del self._discovered_device_advertisement_datas[address]
del self._discovered_device_timestamps[address] del self._discovered_device_timestamps[address]
@property @property
def discovered_devices(self) -> list[BLEDevice]: def discovered_devices(self) -> list[BLEDevice]:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
return list(self._discovered_devices.values()) return [
device_advertisement_data[0]
for device_advertisement_data in self._discovered_device_advertisement_datas.values()
]
async def async_get_device_by_address(self, address: str) -> BLEDevice | None: @property
"""Get a device by address.""" def discovered_devices_and_advertisement_data(
return self._discovered_devices.get(address) self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices and advertisement data."""
return self._discovered_device_advertisement_datas
@callback @callback
def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None: def async_on_advertisement(self, adv: BluetoothLEAdvertisement) -> None:
@ -79,32 +87,39 @@ class ESPHomeScanner(BaseHaScanner):
now = time.monotonic() now = time.monotonic()
address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper address = ":".join(TWO_CHAR.findall("%012X" % adv.address)) # must be upper
name = adv.name name = adv.name
if prev_discovery := self._discovered_devices.get(address): if prev_discovery := self._discovered_device_advertisement_datas.get(address):
# If the last discovery had the full local name # If the last discovery had the full local name
# and this one doesn't, keep the old one as we # and this one doesn't, keep the old one as we
# always want the full local name over the short one # always want the full local name over the short one
if len(prev_discovery.name) > len(adv.name): prev_device = prev_discovery[0]
name = prev_discovery.name if len(prev_device.name) > len(adv.name):
name = prev_device.name
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call] advertisement_data = AdvertisementData(
local_name=None if name == "" else name, local_name=None if name == "" else name,
manufacturer_data=adv.manufacturer_data, manufacturer_data=adv.manufacturer_data,
service_data=adv.service_data, service_data=adv.service_data,
service_uuids=adv.service_uuids, service_uuids=adv.service_uuids,
rssi=adv.rssi,
tx_power=-127,
platform_data=(),
) )
device = BLEDevice( # type: ignore[no-untyped-call] device = BLEDevice( # type: ignore[no-untyped-call]
address=address, address=address,
name=name, name=name,
details=self._details, details=self._details,
rssi=adv.rssi, rssi=adv.rssi, # deprecated, will be removed in newer bleak
)
self._discovered_device_advertisement_datas[address] = (
device,
advertisement_data,
) )
self._discovered_devices[address] = device
self._discovered_device_timestamps[address] = now self._discovered_device_timestamps[address] = now
self._new_info_callback( self._new_info_callback(
BluetoothServiceInfoBleak( BluetoothServiceInfoBleak(
name=advertisement_data.local_name or device.name or device.address, name=advertisement_data.local_name or device.name or device.address,
address=device.address, address=device.address,
rssi=device.rssi, rssi=adv.rssi,
manufacturer_data=advertisement_data.manufacturer_data, manufacturer_data=advertisement_data.manufacturer_data,
service_data=advertisement_data.service_data, service_data=advertisement_data.service_data,
service_uuids=advertisement_data.service_uuids, service_uuids=advertisement_data.service_uuids,

View file

@ -10,7 +10,10 @@ from aiohomekit.model.characteristics import Characteristic, CharacteristicsType
from aiohomekit.model.characteristics.const import ThreadNodeCapabilities, ThreadStatus from aiohomekit.model.characteristics.const import ThreadNodeCapabilities, ThreadStatus
from aiohomekit.model.services import Service, ServicesTypes from aiohomekit.model.services import Service, ServicesTypes
from homeassistant.components.bluetooth import async_ble_device_from_address from homeassistant.components.bluetooth import (
async_ble_device_from_address,
async_last_service_info,
)
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass, SensorDeviceClass,
SensorEntity, SensorEntity,
@ -571,8 +574,8 @@ class RSSISensor(HomeKitEntity, SensorEntity):
def native_value(self) -> int | None: def native_value(self) -> int | None:
"""Return the current rssi value.""" """Return the current rssi value."""
address = self._accessory.pairing_data["AccessoryAddress"] address = self._accessory.pairing_data["AccessoryAddress"]
ble_device = async_ble_device_from_address(self.hass, address) last_service_info = async_last_service_info(self.hass, address)
return ble_device.rssi if ble_device else None return last_service_info.rssi if last_service_info else None
async def async_setup_entry( async def async_setup_entry(

View file

@ -60,6 +60,7 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
self.device_name = device_name self.device_name = device_name
self.base_unique_id = base_unique_id self.base_unique_id = base_unique_id
self.model = model self.model = model
self.service_info: bluetooth.BluetoothServiceInfoBleak | None = None
self._ready_event = asyncio.Event() self._ready_event = asyncio.Event()
@callback @callback
@ -70,6 +71,7 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
) -> None: ) -> None:
"""Handle a Bluetooth event.""" """Handle a Bluetooth event."""
self.ble_device = service_info.device self.ble_device = service_info.device
self.service_info = service_info
if adv := switchbot.parse_advertisement_data( if adv := switchbot.parse_advertisement_data(
service_info.device, service_info.advertisement service_info.device, service_info.advertisement
): ):

View file

@ -117,4 +117,5 @@ class SwitchbotRSSISensor(SwitchBotSensor):
@property @property
def native_value(self) -> str | int: def native_value(self) -> str | int:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.coordinator.ble_device.rssi assert self.coordinator.service_info is not None
return self.coordinator.service_info.rssi

View file

@ -10,8 +10,8 @@ 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==2.1.3 bleak-retry-connector==2.2.0
bleak==0.18.1 bleak==0.19.0
bluetooth-adapters==0.6.0 bluetooth-adapters==0.6.0
bluetooth-auto-recovery==0.3.4 bluetooth-auto-recovery==0.3.4
certifi>=2021.5.30 certifi>=2021.5.30
@ -20,7 +20,7 @@ cryptography==38.0.1
dbus-fast==1.45.0 dbus-fast==1.45.0
fnvhash==0.1.0 fnvhash==0.1.0
hass-nabucasa==0.56.0 hass-nabucasa==0.56.0
home-assistant-bluetooth==1.3.0 home-assistant-bluetooth==1.6.0
home-assistant-frontend==20221010.0 home-assistant-frontend==20221010.0
httpx==0.23.0 httpx==0.23.0
ifaddr==0.1.7 ifaddr==0.1.7

View file

@ -36,7 +36,7 @@ dependencies = [
# When bumping httpx, please check the version pins of # When bumping httpx, please check the version pins of
# httpcore, anyio, and h11 in gen_requirements_all # httpcore, anyio, and h11 in gen_requirements_all
"httpx==0.23.0", "httpx==0.23.0",
"home-assistant-bluetooth==1.3.0", "home-assistant-bluetooth==1.6.0",
"ifaddr==0.1.7", "ifaddr==0.1.7",
"jinja2==3.1.2", "jinja2==3.1.2",
"lru-dict==1.1.8", "lru-dict==1.1.8",

View file

@ -11,7 +11,7 @@ bcrypt==3.1.7
certifi>=2021.5.30 certifi>=2021.5.30
ciso8601==2.2.0 ciso8601==2.2.0
httpx==0.23.0 httpx==0.23.0
home-assistant-bluetooth==1.3.0 home-assistant-bluetooth==1.6.0
ifaddr==0.1.7 ifaddr==0.1.7
jinja2==3.1.2 jinja2==3.1.2
lru-dict==1.1.8 lru-dict==1.1.8

View file

@ -413,10 +413,10 @@ bimmer_connected==0.10.4
bizkaibus==0.1.1 bizkaibus==0.1.1
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak-retry-connector==2.1.3 bleak-retry-connector==2.2.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak==0.18.1 bleak==0.19.0
# homeassistant.components.blebox # homeassistant.components.blebox
blebox_uniapi==2.1.0 blebox_uniapi==2.1.0

View file

@ -337,10 +337,10 @@ bellows==0.34.2
bimmer_connected==0.10.4 bimmer_connected==0.10.4
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak-retry-connector==2.1.3 bleak-retry-connector==2.2.0
# homeassistant.components.bluetooth # homeassistant.components.bluetooth
bleak==0.18.1 bleak==0.19.0
# homeassistant.components.blebox # homeassistant.components.blebox
blebox_uniapi==2.1.0 blebox_uniapi==2.1.0

View file

@ -5,10 +5,11 @@ from unittest.mock import patch
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
def patch_async_setup_entry(return_value=True): def patch_async_setup_entry(return_value=True):
"""Patch async setup entry to return True.""" """Patch async setup entry to return True."""
@ -48,7 +49,7 @@ WAVE_SERVICE_INFO = BluetoothServiceInfoBleak(
"cc:cc:cc:cc:cc:cc", "cc:cc:cc:cc:cc:cc",
"cc-cc-cc-cc-cc-cc", "cc-cc-cc-cc-cc-cc",
), ),
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
manufacturer_data={820: b"\xe4/\xa5\xae\t\x00"}, manufacturer_data={820: b"\xe4/\xa5\xae\t\x00"},
service_uuids=["b42e1c08-ade7-11e4-89d3-123b93f75cba"], service_uuids=["b42e1c08-ade7-11e4-89d3-123b93f75cba"],
), ),
@ -68,7 +69,7 @@ UNKNOWN_SERVICE_INFO = BluetoothServiceInfoBleak(
"cc:cc:cc:cc:cc:cc", "cc:cc:cc:cc:cc:cc",
"unknown", "unknown",
), ),
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
manufacturer_data={}, manufacturer_data={},
service_uuids=[], service_uuids=[],
), ),

View file

@ -2,6 +2,7 @@
import time import time
from typing import Any
from unittest.mock import patch from unittest.mock import patch
from bleak.backends.scanner import AdvertisementData, BLEDevice from bleak.backends.scanner import AdvertisementData, BLEDevice
@ -27,8 +28,27 @@ __all__ = (
"inject_bluetooth_service_info", "inject_bluetooth_service_info",
"patch_all_discovered_devices", "patch_all_discovered_devices",
"patch_discovered_devices", "patch_discovered_devices",
"generate_advertisement_data",
) )
ADVERTISEMENT_DATA_DEFAULTS = {
"local_name": "",
"manufacturer_data": {},
"service_data": {},
"service_uuids": [],
"rssi": -127,
"platform_data": ((),),
"tx_power": -127,
}
def generate_advertisement_data(**kwargs: Any) -> AdvertisementData:
"""Generate advertisement data with defaults."""
new = kwargs.copy()
for key, value in ADVERTISEMENT_DATA_DEFAULTS.items():
new.setdefault(key, value)
return AdvertisementData(**new)
def _get_manager() -> BluetoothManager: def _get_manager() -> BluetoothManager:
"""Return the bluetooth manager.""" """Return the bluetooth manager."""
@ -77,7 +97,7 @@ def inject_advertisement_with_time_and_source_connectable(
models.BluetoothServiceInfoBleak( models.BluetoothServiceInfoBleak(
name=adv.local_name or device.name or device.address, name=adv.local_name or device.name or device.address,
address=device.address, address=device.address,
rssi=device.rssi, rssi=adv.rssi,
manufacturer_data=adv.manufacturer_data, manufacturer_data=adv.manufacturer_data,
service_data=adv.service_data, service_data=adv.service_data,
service_uuids=adv.service_uuids, service_uuids=adv.service_uuids,
@ -94,17 +114,17 @@ def inject_bluetooth_service_info_bleak(
hass: HomeAssistant, info: models.BluetoothServiceInfoBleak hass: HomeAssistant, info: models.BluetoothServiceInfoBleak
) -> None: ) -> None:
"""Inject an advertisement into the manager with connectable status.""" """Inject an advertisement into the manager with connectable status."""
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call] advertisement_data = generate_advertisement_data(
local_name=None if info.name == "" else info.name, local_name=None if info.name == "" else info.name,
manufacturer_data=info.manufacturer_data, manufacturer_data=info.manufacturer_data,
service_data=info.service_data, service_data=info.service_data,
service_uuids=info.service_uuids, service_uuids=info.service_uuids,
rssi=info.rssi,
) )
device = BLEDevice( # type: ignore[no-untyped-call] device = BLEDevice( # type: ignore[no-untyped-call]
address=info.address, address=info.address,
name=info.name, name=info.name,
details={}, details={},
rssi=info.rssi,
) )
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, hass,
@ -120,17 +140,17 @@ def inject_bluetooth_service_info(
hass: HomeAssistant, info: models.BluetoothServiceInfo hass: HomeAssistant, info: models.BluetoothServiceInfo
) -> None: ) -> None:
"""Inject a BluetoothServiceInfo into the manager.""" """Inject a BluetoothServiceInfo into the manager."""
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call] advertisement_data = generate_advertisement_data( # type: ignore[no-untyped-call]
local_name=None if info.name == "" else info.name, local_name=None if info.name == "" else info.name,
manufacturer_data=info.manufacturer_data, manufacturer_data=info.manufacturer_data,
service_data=info.service_data, service_data=info.service_data,
service_uuids=info.service_uuids, service_uuids=info.service_uuids,
rssi=info.rssi,
) )
device = BLEDevice( # type: ignore[no-untyped-call] device = BLEDevice( # type: ignore[no-untyped-call]
address=info.address, address=info.address,
name=info.name, name=info.name,
details={}, details={},
rssi=info.rssi,
) )
inject_advertisement(hass, device, advertisement_data) inject_advertisement(hass, device, advertisement_data)
@ -138,7 +158,9 @@ def inject_bluetooth_service_info(
def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None: def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
"""Mock all the discovered devices from all the scanners.""" """Mock all the discovered devices from all the scanners."""
return patch.object( return patch.object(
_get_manager(), "async_all_discovered_devices", return_value=mock_discovered _get_manager(),
"_async_all_discovered_addresses",
return_value={ble_device.address for ble_device in mock_discovered},
) )

View file

@ -21,7 +21,11 @@ from homeassistant.components.bluetooth.models import BaseHaScanner
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import inject_advertisement_with_time_and_source from . import (
generate_advertisement_data,
inject_advertisement_with_time_and_source,
inject_advertisement_with_time_and_source_connectable,
)
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
@ -33,8 +37,8 @@ async def test_advertisment_interval_shorter_than_adapter_stack_timeout(
): ):
"""Test we can determine the advertisement interval.""" """Test we can determine the advertisement interval."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:12", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -77,8 +81,8 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_connectab
): ):
"""Test device with a long advertisement interval.""" """Test device with a long advertisement interval."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:18", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -124,7 +128,7 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
"""Test device with a long advertisement interval with an adapter change.""" """Test device with a long advertisement interval with an adapter change."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -179,7 +183,7 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_not_conne
"""Test device with a long advertisement interval that is not connectable not reaching the advertising interval.""" """Test device with a long advertisement interval that is not connectable not reaching the advertising interval."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -227,9 +231,11 @@ async def test_advertisment_interval_shorter_than_adapter_stack_timeout_adapter_
): ):
"""Test device with a short advertisement interval with an adapter change that is not connectable.""" """Test device with a short advertisement interval with an adapter change that is not connectable."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:5C", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
rssi=-100,
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -248,9 +254,18 @@ async def test_advertisment_interval_shorter_than_adapter_stack_timeout_adapter_
"original", "original",
) )
switchbot_adv_better_rssi = generate_advertisement_data(
local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
rssi=-30,
)
for i in range(ADVERTISING_TIMES_NEEDED): for i in range(ADVERTISING_TIMES_NEEDED):
inject_advertisement_with_time_and_source( inject_advertisement_with_time_and_source(
hass, switchbot_device, switchbot_adv, start_monotonic_time + (i * 2), "new" hass,
switchbot_device,
switchbot_adv_better_rssi,
start_monotonic_time + (i * 2),
"new",
) )
switchbot_device_unavailable_cancel = async_track_unavailable( switchbot_device_unavailable_cancel = async_track_unavailable(
@ -282,8 +297,10 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
"""Test device with a long advertisement interval with an adapter change that is not connectable.""" """Test device with a long advertisement interval with an adapter change that is not connectable."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
rssi=-100,
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -291,8 +308,11 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
"""Fake scanner.""" """Fake scanner."""
@property @property
def discovered_devices(self) -> list[BLEDevice]: def discovered_devices_and_advertisement_data(
return [] self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices."""
return {}
scanner = FakeScanner(hass, "new") scanner = FakeScanner(hass, "new")
cancel_scanner = async_register_scanner(hass, scanner, False) cancel_scanner = async_register_scanner(hass, scanner, False)
@ -304,21 +324,28 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
switchbot_device_went_unavailable = True switchbot_device_went_unavailable = True
for i in range(ADVERTISING_TIMES_NEEDED): for i in range(ADVERTISING_TIMES_NEEDED):
inject_advertisement_with_time_and_source( inject_advertisement_with_time_and_source_connectable(
hass, hass,
switchbot_device, switchbot_device,
switchbot_adv, switchbot_adv,
start_monotonic_time + (i * 2), start_monotonic_time + (i * 2),
"original", "original",
connectable=False,
) )
switchbot_better_rssi_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
rssi=-30,
)
for i in range(ADVERTISING_TIMES_NEEDED): for i in range(ADVERTISING_TIMES_NEEDED):
inject_advertisement_with_time_and_source( inject_advertisement_with_time_and_source_connectable(
hass, hass,
switchbot_device, switchbot_device,
switchbot_adv, switchbot_better_rssi_adv,
start_monotonic_time + (i * ONE_HOUR_SECONDS), start_monotonic_time + (i * ONE_HOUR_SECONDS),
"new", "new",
connectable=False,
) )
switchbot_device_unavailable_cancel = async_track_unavailable( switchbot_device_unavailable_cancel = async_track_unavailable(
@ -364,7 +391,7 @@ async def test_advertisment_interval_longer_increasing_than_adapter_stack_timeou
"""Test device with a increasing advertisement interval with an adapter change that is not connectable.""" """Test device with a increasing advertisement interval with an adapter change that is not connectable."""
start_monotonic_time = time.monotonic() start_monotonic_time = time.monotonic()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False

View file

@ -3,12 +3,12 @@
from unittest.mock import ANY, patch from unittest.mock import ANY, patch
from bleak.backends.scanner import AdvertisementData, BLEDevice from bleak.backends.scanner import BLEDevice
from homeassistant.components import bluetooth from homeassistant.components import bluetooth
from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS
from . import inject_advertisement from . import generate_advertisement_data, inject_advertisement
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.components.diagnostics import get_diagnostics_for_config_entry
@ -96,11 +96,6 @@ async def test_diagnostics(
} }
}, },
"manager": { "manager": {
"advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"adapters": { "adapters": {
"hci0": { "hci0": {
"address": "00:00:00:00:00:01", "address": "00:00:00:00:00:01",
@ -115,13 +110,18 @@ async def test_diagnostics(
"sw_version": "BlueZ 4.63", "sw_version": "BlueZ 4.63",
}, },
}, },
"advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"connectable_history": [], "connectable_history": [],
"history": [], "non_connectable_history": [],
"scanners": [ "scanners": [
{ {
"adapter": "hci0", "adapter": "hci0",
"discovered_devices": [ "discovered_devices": [
{"address": "44:44:33:11:23:45", "name": "x", "rssi": -60} {"address": "44:44:33:11:23:45", "name": "x"}
], ],
"last_detection": ANY, "last_detection": ANY,
"name": "hci0 (00:00:00:00:00:01)", "name": "hci0 (00:00:00:00:00:01)",
@ -132,7 +132,7 @@ async def test_diagnostics(
{ {
"adapter": "hci0", "adapter": "hci0",
"discovered_devices": [ "discovered_devices": [
{"address": "44:44:33:11:23:45", "name": "x", "rssi": -60} {"address": "44:44:33:11:23:45", "name": "x"}
], ],
"last_detection": ANY, "last_detection": ANY,
"name": "hci0 (00:00:00:00:00:01)", "name": "hci0 (00:00:00:00:00:01)",
@ -143,7 +143,7 @@ async def test_diagnostics(
{ {
"adapter": "hci1", "adapter": "hci1",
"discovered_devices": [ "discovered_devices": [
{"address": "44:44:33:11:23:45", "name": "x", "rssi": -60} {"address": "44:44:33:11:23:45", "name": "x"}
], ],
"last_detection": ANY, "last_detection": ANY,
"name": "hci1 (00:00:00:00:00:02)", "name": "hci1 (00:00:00:00:00:02)",
@ -166,7 +166,7 @@ async def test_diagnostics_macos(
# error if the test is not running on linux since we won't have the correct # error if the test is not running on linux since we won't have the correct
# deps installed when testing on MacOS. # deps installed when testing on MacOS.
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( 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"}
) )
@ -203,11 +203,6 @@ async def test_diagnostics_macos(
} }
}, },
"manager": { "manager": {
"advertisement_tracker": {
"intervals": {},
"sources": {"44:44:33:11:23:45": "local"},
"timings": {"44:44:33:11:23:45": [ANY]},
},
"adapters": { "adapters": {
"Core Bluetooth": { "Core Bluetooth": {
"address": "00:00:00:00:00:00", "address": "00:00:00:00:00:00",
@ -215,39 +210,41 @@ async def test_diagnostics_macos(
"sw_version": ANY, "sw_version": ANY,
} }
}, },
"advertisement_tracker": {
"intervals": {},
"sources": {"44:44:33:11:23:45": "local"},
"timings": {"44:44:33:11:23:45": [ANY]},
},
"connectable_history": [ "connectable_history": [
{ {
"address": "44:44:33:11:23:45", "address": "44:44:33:11:23:45",
"advertisement": ANY, "advertisement": [
"wohand",
{"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}},
{},
[],
-127,
-127,
[[]],
],
"connectable": True, "connectable": True,
"manufacturer_data": ANY, "manufacturer_data": {
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
},
"name": "wohand", "name": "wohand",
"rssi": 0, "rssi": -127,
"service_data": {},
"service_uuids": [],
"source": "local",
"time": ANY,
}
],
"history": [
{
"address": "44:44:33:11:23:45",
"advertisement": ANY,
"connectable": True,
"manufacturer_data": ANY,
"name": "wohand",
"rssi": 0,
"service_data": {}, "service_data": {},
"service_uuids": [], "service_uuids": [],
"source": "local", "source": "local",
"time": ANY, "time": ANY,
} }
], ],
"non_connectable_history": [],
"scanners": [ "scanners": [
{ {
"adapter": "Core Bluetooth", "adapter": "Core Bluetooth",
"discovered_devices": [ "discovered_devices": [
{"address": "44:44:33:11:23:45", "name": "x", "rssi": -60} {"address": "44:44:33:11:23:45", "name": "x"}
], ],
"last_detection": ANY, "last_detection": ANY,
"name": "Core Bluetooth", "name": "Core Bluetooth",

View file

@ -44,6 +44,7 @@ from homeassistant.util import dt as dt_util
from . import ( from . import (
_get_manager, _get_manager,
async_setup_with_default_adapter, async_setup_with_default_adapter,
generate_advertisement_data,
inject_advertisement, inject_advertisement,
inject_advertisement_with_time_and_source_connectable, inject_advertisement_with_time_and_source_connectable,
patch_discovered_devices, patch_discovered_devices,
@ -334,7 +335,9 @@ async def test_discovery_match_by_service_uuid(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement(hass, wrong_device, wrong_adv) inject_advertisement(hass, wrong_device, wrong_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -342,7 +345,7 @@ async def test_discovery_match_by_service_uuid(
assert len(mock_config_flow.mock_calls) == 0 assert len(mock_config_flow.mock_calls) == 0
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
@ -379,7 +382,9 @@ async def test_discovery_match_by_service_uuid_connectable(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, wrong_device, wrong_adv, time.monotonic(), "any", True hass, wrong_device, wrong_adv, time.monotonic(), "any", True
@ -389,7 +394,7 @@ async def test_discovery_match_by_service_uuid_connectable(
assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0 assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
@ -424,7 +429,9 @@ async def test_discovery_match_by_service_uuid_not_connectable(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, wrong_device, wrong_adv, time.monotonic(), "any", False hass, wrong_device, wrong_adv, time.monotonic(), "any", False
@ -434,7 +441,7 @@ async def test_discovery_match_by_service_uuid_not_connectable(
assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0 assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
@ -467,7 +474,9 @@ async def test_discovery_match_by_name_connectable_false(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, wrong_device, wrong_adv, time.monotonic(), "any", False hass, wrong_device, wrong_adv, time.monotonic(), "any", False
@ -477,7 +486,7 @@ async def test_discovery_match_by_name_connectable_false(
assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0 assert len(_domains_from_mock_config_flow(mock_config_flow)) == 0
qingping_device = BLEDevice("44:44:33:11:23:45", "Qingping Motion & Light") qingping_device = BLEDevice("44:44:33:11:23:45", "Qingping Motion & Light")
qingping_adv = AdvertisementData( qingping_adv = generate_advertisement_data(
local_name="Qingping Motion & Light", local_name="Qingping Motion & Light",
service_data={ service_data={
"0000fdcd-0000-1000-8000-00805f9b34fb": b"H\x12\xcd\xd5`4-X\x08\x04\x01\xe8\x00\x00\x0f\x01{" "0000fdcd-0000-1000-8000-00805f9b34fb": b"H\x12\xcd\xd5`4-X\x08\x04\x01\xe8\x00\x00\x0f\x01{"
@ -493,8 +502,20 @@ async def test_discovery_match_by_name_connectable_false(
mock_config_flow.reset_mock() mock_config_flow.reset_mock()
# Make sure it will also take a connectable device # Make sure it will also take a connectable device
qingping_adv_with_better_rssi = generate_advertisement_data(
local_name="Qingping Motion & Light",
service_data={
"0000fdcd-0000-1000-8000-00805f9b34fb": b"H\x12\xcd\xd5`4-X\x08\x04\x01\xe8\x00\x00\x0f\x02{"
},
rssi=-30,
)
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, qingping_device, qingping_adv, time.monotonic(), "any", True hass,
qingping_device,
qingping_adv_with_better_rssi,
time.monotonic(),
"any",
True,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert _domains_from_mock_config_flow(mock_config_flow) == ["qingping"] assert _domains_from_mock_config_flow(mock_config_flow) == ["qingping"]
@ -517,7 +538,9 @@ async def test_discovery_match_by_local_name(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:45", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement(hass, wrong_device, wrong_adv) inject_advertisement(hass, wrong_device, wrong_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -525,7 +548,7 @@ async def test_discovery_match_by_local_name(
assert len(mock_config_flow.mock_calls) == 0 assert len(mock_config_flow.mock_calls) == 0
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( 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"}
) )
@ -559,12 +582,12 @@ async def test_discovery_match_by_manufacturer_id_and_manufacturer_data_start(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
hkc_device = BLEDevice("44:44:33:11:23:45", "lock") hkc_device = BLEDevice("44:44:33:11:23:45", "lock")
hkc_adv_no_mfr_data = AdvertisementData( hkc_adv_no_mfr_data = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={}, manufacturer_data={},
) )
hkc_adv = AdvertisementData( hkc_adv = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={76: b"\x06\x02\x03\x99"}, manufacturer_data={76: b"\x06\x02\x03\x99"},
@ -593,7 +616,7 @@ async def test_discovery_match_by_manufacturer_id_and_manufacturer_data_start(
mock_config_flow.reset_mock() mock_config_flow.reset_mock()
not_hkc_device = BLEDevice("44:44:33:11:23:21", "lock") not_hkc_device = BLEDevice("44:44:33:11:23:21", "lock")
not_hkc_adv = AdvertisementData( not_hkc_adv = generate_advertisement_data(
local_name="lock", service_uuids=[], manufacturer_data={76: b"\x02"} local_name="lock", service_uuids=[], manufacturer_data={76: b"\x02"}
) )
@ -602,7 +625,7 @@ async def test_discovery_match_by_manufacturer_id_and_manufacturer_data_start(
assert len(mock_config_flow.mock_calls) == 0 assert len(mock_config_flow.mock_calls) == 0
not_apple_device = BLEDevice("44:44:33:11:23:23", "lock") not_apple_device = BLEDevice("44:44:33:11:23:23", "lock")
not_apple_adv = AdvertisementData( not_apple_adv = generate_advertisement_data(
local_name="lock", service_uuids=[], manufacturer_data={21: b"\x02"} local_name="lock", service_uuids=[], manufacturer_data={21: b"\x02"}
) )
@ -642,36 +665,38 @@ async def test_discovery_match_by_service_data_uuid_then_others(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
device = BLEDevice("44:44:33:11:23:45", "lock") device = BLEDevice("44:44:33:11:23:45", "lock")
adv_without_service_data_uuid = AdvertisementData( adv_without_service_data_uuid = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={}, manufacturer_data={},
) )
adv_with_mfr_data = AdvertisementData( adv_with_mfr_data = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={323: b"\x01\x02\x03"}, manufacturer_data={323: b"\x01\x02\x03"},
service_data={}, service_data={},
) )
adv_with_service_data_uuid = AdvertisementData( adv_with_service_data_uuid = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={}, manufacturer_data={},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"}, service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"},
) )
adv_with_service_data_uuid_and_mfr_data = AdvertisementData( adv_with_service_data_uuid_and_mfr_data = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={323: b"\x01\x02\x03"}, manufacturer_data={323: b"\x01\x02\x03"},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"}, service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"},
) )
adv_with_service_data_uuid_and_mfr_data_and_service_uuid = AdvertisementData( adv_with_service_data_uuid_and_mfr_data_and_service_uuid = (
local_name="lock", generate_advertisement_data(
manufacturer_data={323: b"\x01\x02\x03"}, local_name="lock",
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"}, manufacturer_data={323: b"\x01\x02\x03"},
service_uuids=["0000fd3d-0000-1000-8000-00805f9b34fd"], service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x01\x02\x03"},
service_uuids=["0000fd3d-0000-1000-8000-00805f9b34fd"],
)
) )
adv_with_service_uuid = AdvertisementData( adv_with_service_uuid = generate_advertisement_data(
local_name="lock", local_name="lock",
manufacturer_data={}, manufacturer_data={},
service_data={}, service_data={},
@ -790,18 +815,18 @@ async def test_discovery_match_by_service_data_uuid_when_format_changes(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
device = BLEDevice("44:44:33:11:23:45", "lock") device = BLEDevice("44:44:33:11:23:45", "lock")
adv_without_service_data_uuid = AdvertisementData( adv_without_service_data_uuid = generate_advertisement_data(
local_name="Qingping Temp RH M", local_name="Qingping Temp RH M",
service_uuids=[], service_uuids=[],
manufacturer_data={}, manufacturer_data={},
) )
xiaomi_format_adv = AdvertisementData( xiaomi_format_adv = generate_advertisement_data(
local_name="Qingping Temp RH M", local_name="Qingping Temp RH M",
service_data={ service_data={
"0000fe95-0000-1000-8000-00805f9b34fb": b"0XH\x0b\x06\xa7%\x144-X\x08" "0000fe95-0000-1000-8000-00805f9b34fb": b"0XH\x0b\x06\xa7%\x144-X\x08"
}, },
) )
qingping_format_adv = AdvertisementData( qingping_format_adv = generate_advertisement_data(
local_name="Qingping Temp RH M", local_name="Qingping Temp RH M",
service_data={ service_data={
"0000fdcd-0000-1000-8000-00805f9b34fb": b"\x08\x16\xa7%\x144-X\x01\x04\xdb\x00\xa6\x01\x02\x01d" "0000fdcd-0000-1000-8000-00805f9b34fb": b"\x08\x16\xa7%\x144-X\x01\x04\xdb\x00\xa6\x01\x02\x01d"
@ -871,12 +896,12 @@ async def test_discovery_match_first_by_service_uuid_and_then_manufacturer_id(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
device = BLEDevice("44:44:33:11:23:45", "lock") device = BLEDevice("44:44:33:11:23:45", "lock")
adv_service_uuids = AdvertisementData( adv_service_uuids = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=["0000fd3d-0000-1000-8000-00805f9b34fc"], service_uuids=["0000fd3d-0000-1000-8000-00805f9b34fc"],
manufacturer_data={}, manufacturer_data={},
) )
adv_manufacturer_data = AdvertisementData( adv_manufacturer_data = generate_advertisement_data(
local_name="lock", local_name="lock",
service_uuids=[], service_uuids=[],
manufacturer_data={76: b"\x06\x02\x03\x99"}, manufacturer_data={76: b"\x06\x02\x03\x99"},
@ -924,10 +949,10 @@ async def test_rediscovery(hass, mock_bleak_scanner_start, enable_bluetooth):
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"] local_name="wohand", service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"]
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={1: b"\x01"}, manufacturer_data={1: b"\x01"},
@ -958,8 +983,8 @@ async def test_async_discovered_device_api(
with patch( with patch(
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt "homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
), patch( ), patch(
"bleak.BleakScanner.discovered_devices", # Must patch before we setup "bleak.BleakScanner.discovered_devices_and_advertisement_data", # Must patch before we setup
[MagicMock(address="44:44:33:11:23:45")], {"44:44:33:11:23:45": (MagicMock(address="44:44:33:11:23:45"), MagicMock())},
): ):
assert not bluetooth.async_discovered_service_info(hass) assert not bluetooth.async_discovered_service_info(hass)
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
@ -974,10 +999,14 @@ async def test_async_discovered_device_api(
assert not bluetooth.async_discovered_service_info(hass) assert not bluetooth.async_discovered_service_info(hass)
wrong_device = BLEDevice("44:44:33:11:23:42", "wrong_name") wrong_device = BLEDevice("44:44:33:11:23:42", "wrong_name")
wrong_adv = AdvertisementData(local_name="wrong_name", service_uuids=[]) wrong_adv = generate_advertisement_data(
local_name="wrong_name", service_uuids=[]
)
inject_advertisement(hass, wrong_device, wrong_adv) inject_advertisement(hass, wrong_device, wrong_adv)
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData(local_name="wohand", service_uuids=[]) switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=[]
)
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
wrong_device_went_unavailable = False wrong_device_went_unavailable = False
switchbot_device_went_unavailable = False switchbot_device_went_unavailable = False
@ -1070,7 +1099,7 @@ async def test_register_callbacks(hass, mock_bleak_scanner_start, enable_bluetoo
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
@ -1080,13 +1109,13 @@ async def test_register_callbacks(hass, mock_bleak_scanner_start, enable_bluetoo
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1138,7 +1167,7 @@ async def test_register_callbacks_raises_exception(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
@ -1197,7 +1226,7 @@ async def test_register_callback_by_address(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
@ -1207,13 +1236,13 @@ async def test_register_callback_by_address(
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
# 3rd callback raises ValueError but is still tracked # 3rd callback raises ValueError but is still tracked
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
@ -1299,18 +1328,29 @@ async def test_register_callback_by_address_connectable_only(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_better_rssi = 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"},
rssi=-30,
)
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, switchbot_device, switchbot_adv, time.monotonic(), "test", False hass, switchbot_device, switchbot_adv, time.monotonic(), "test", False
) )
inject_advertisement_with_time_and_source_connectable( inject_advertisement_with_time_and_source_connectable(
hass, switchbot_device, switchbot_adv, time.monotonic(), "test", True hass,
switchbot_device,
switchbot_adv_better_rssi,
time.monotonic(),
"test",
True,
) )
cancel() cancel()
@ -1354,7 +1394,7 @@ async def test_register_callback_by_manufacturer_id(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
apple_device = BLEDevice("44:44:33:11:23:45", "rtx") apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="rtx", local_name="rtx",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1362,7 +1402,7 @@ async def test_register_callback_by_manufacturer_id(
inject_advertisement(hass, apple_device, apple_adv) inject_advertisement(hass, apple_device, apple_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1409,7 +1449,7 @@ async def test_register_callback_by_connectable(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
apple_device = BLEDevice("44:44:33:11:23:45", "rtx") apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="rtx", local_name="rtx",
manufacturer_data={7676: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={7676: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1417,7 +1457,7 @@ async def test_register_callback_by_connectable(
inject_advertisement(hass, apple_device, apple_adv) inject_advertisement(hass, apple_device, apple_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1464,7 +1504,7 @@ async def test_not_filtering_wanted_apple_devices(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
ibeacon_device = BLEDevice("44:44:33:11:23:45", "rtx") ibeacon_device = BLEDevice("44:44:33:11:23:45", "rtx")
ibeacon_adv = AdvertisementData( ibeacon_adv = generate_advertisement_data(
local_name="ibeacon", local_name="ibeacon",
manufacturer_data={76: b"\x02\x00\x00\x00"}, manufacturer_data={76: b"\x02\x00\x00\x00"},
) )
@ -1472,7 +1512,7 @@ async def test_not_filtering_wanted_apple_devices(
inject_advertisement(hass, ibeacon_device, ibeacon_adv) inject_advertisement(hass, ibeacon_device, ibeacon_adv)
homekit_device = BLEDevice("44:44:33:11:23:46", "rtx") homekit_device = BLEDevice("44:44:33:11:23:46", "rtx")
homekit_adv = AdvertisementData( homekit_adv = generate_advertisement_data(
local_name="homekit", local_name="homekit",
manufacturer_data={76: b"\x06\x00\x00\x00"}, manufacturer_data={76: b"\x06\x00\x00\x00"},
) )
@ -1480,7 +1520,7 @@ async def test_not_filtering_wanted_apple_devices(
inject_advertisement(hass, homekit_device, homekit_adv) inject_advertisement(hass, homekit_device, homekit_adv)
apple_device = BLEDevice("44:44:33:11:23:47", "rtx") apple_device = BLEDevice("44:44:33:11:23:47", "rtx")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="apple", local_name="apple",
manufacturer_data={76: b"\x10\x00\x00\x00"}, manufacturer_data={76: b"\x10\x00\x00\x00"},
) )
@ -1524,7 +1564,7 @@ async def test_filtering_noisy_apple_devices(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
apple_device = BLEDevice("44:44:33:11:23:45", "rtx") apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="noisy", local_name="noisy",
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1532,7 +1572,7 @@ async def test_filtering_noisy_apple_devices(
inject_advertisement(hass, apple_device, apple_adv) inject_advertisement(hass, apple_device, apple_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1574,7 +1614,7 @@ async def test_register_callback_by_address_connectable_manufacturer_id(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
apple_device = BLEDevice("44:44:33:11:23:45", "rtx") apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="rtx", local_name="rtx",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1628,7 +1668,7 @@ async def test_register_callback_by_manufacturer_id_and_address(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
rtx_device = BLEDevice("44:44:33:11:23:45", "rtx") rtx_device = BLEDevice("44:44:33:11:23:45", "rtx")
rtx_adv = AdvertisementData( rtx_adv = generate_advertisement_data(
local_name="rtx", local_name="rtx",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1636,7 +1676,7 @@ async def test_register_callback_by_manufacturer_id_and_address(
inject_advertisement(hass, rtx_device, rtx_adv) inject_advertisement(hass, rtx_device, rtx_adv)
yale_device = BLEDevice("44:44:33:11:23:45", "apple") yale_device = BLEDevice("44:44:33:11:23:45", "apple")
yale_adv = AdvertisementData( yale_adv = generate_advertisement_data(
local_name="yale", local_name="yale",
manufacturer_data={465: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={465: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1645,7 +1685,7 @@ async def test_register_callback_by_manufacturer_id_and_address(
await hass.async_block_till_done() await hass.async_block_till_done()
other_apple_device = BLEDevice("44:44:33:11:23:22", "apple") other_apple_device = BLEDevice("44:44:33:11:23:22", "apple")
other_apple_adv = AdvertisementData( other_apple_adv = generate_advertisement_data(
local_name="apple", local_name="apple",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1696,7 +1736,7 @@ async def test_register_callback_by_service_uuid_and_address(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_dev = BLEDevice("44:44:33:11:23:45", "switchbot") switchbot_dev = BLEDevice("44:44:33:11:23:45", "switchbot")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="switchbot", local_name="switchbot",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
) )
@ -1704,7 +1744,7 @@ async def test_register_callback_by_service_uuid_and_address(
inject_advertisement(hass, switchbot_dev, switchbot_adv) inject_advertisement(hass, switchbot_dev, switchbot_adv)
switchbot_missing_service_uuid_dev = BLEDevice("44:44:33:11:23:45", "switchbot") switchbot_missing_service_uuid_dev = BLEDevice("44:44:33:11:23:45", "switchbot")
switchbot_missing_service_uuid_adv = AdvertisementData( switchbot_missing_service_uuid_adv = generate_advertisement_data(
local_name="switchbot", local_name="switchbot",
) )
@ -1714,7 +1754,7 @@ async def test_register_callback_by_service_uuid_and_address(
await hass.async_block_till_done() await hass.async_block_till_done()
service_uuid_wrong_address_dev = BLEDevice("44:44:33:11:23:22", "switchbot2") service_uuid_wrong_address_dev = BLEDevice("44:44:33:11:23:22", "switchbot2")
service_uuid_wrong_address_adv = AdvertisementData( service_uuid_wrong_address_adv = generate_advertisement_data(
local_name="switchbot2", local_name="switchbot2",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
) )
@ -1765,7 +1805,7 @@ async def test_register_callback_by_service_data_uuid_and_address(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_dev = BLEDevice("44:44:33:11:23:45", "switchbot") switchbot_dev = BLEDevice("44:44:33:11:23:45", "switchbot")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="switchbot", local_name="switchbot",
service_data={"cba20d00-224d-11e6-9fb8-0002a5d5c51b": b"x"}, service_data={"cba20d00-224d-11e6-9fb8-0002a5d5c51b": b"x"},
) )
@ -1773,7 +1813,7 @@ async def test_register_callback_by_service_data_uuid_and_address(
inject_advertisement(hass, switchbot_dev, switchbot_adv) inject_advertisement(hass, switchbot_dev, switchbot_adv)
switchbot_missing_service_uuid_dev = BLEDevice("44:44:33:11:23:45", "switchbot") switchbot_missing_service_uuid_dev = BLEDevice("44:44:33:11:23:45", "switchbot")
switchbot_missing_service_uuid_adv = AdvertisementData( switchbot_missing_service_uuid_adv = generate_advertisement_data(
local_name="switchbot", local_name="switchbot",
) )
@ -1783,7 +1823,7 @@ async def test_register_callback_by_service_data_uuid_and_address(
await hass.async_block_till_done() await hass.async_block_till_done()
service_uuid_wrong_address_dev = BLEDevice("44:44:33:11:23:22", "switchbot2") service_uuid_wrong_address_dev = BLEDevice("44:44:33:11:23:22", "switchbot2")
service_uuid_wrong_address_adv = AdvertisementData( service_uuid_wrong_address_adv = generate_advertisement_data(
local_name="switchbot2", local_name="switchbot2",
service_data={"cba20d00-224d-11e6-9fb8-0002a5d5c51b": b"x"}, service_data={"cba20d00-224d-11e6-9fb8-0002a5d5c51b": b"x"},
) )
@ -1831,7 +1871,7 @@ async def test_register_callback_by_local_name(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
rtx_device = BLEDevice("44:44:33:11:23:45", "rtx") rtx_device = BLEDevice("44:44:33:11:23:45", "rtx")
rtx_adv = AdvertisementData( rtx_adv = generate_advertisement_data(
local_name="rtx", local_name="rtx",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1839,12 +1879,12 @@ async def test_register_callback_by_local_name(
inject_advertisement(hass, rtx_device, rtx_adv) inject_advertisement(hass, rtx_device, rtx_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
rtx_device_2 = BLEDevice("44:44:33:11:23:45", "rtx") rtx_device_2 = BLEDevice("44:44:33:11:23:45", "rtx")
rtx_adv_2 = AdvertisementData( rtx_adv_2 = generate_advertisement_data(
local_name="rtx2", local_name="rtx2",
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
) )
@ -1927,7 +1967,7 @@ async def test_register_callback_by_service_data_uuid(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
apple_device = BLEDevice("44:44:33:11:23:45", "xiaomi") apple_device = BLEDevice("44:44:33:11:23:45", "xiaomi")
apple_adv = AdvertisementData( apple_adv = generate_advertisement_data(
local_name="xiaomi", local_name="xiaomi",
service_data={ service_data={
"0000fe95-0000-1000-8000-00805f9b34fb": b"\xd8.\xad\xcd\r\x85" "0000fe95-0000-1000-8000-00805f9b34fb": b"\xd8.\xad\xcd\r\x85"
@ -1937,7 +1977,7 @@ async def test_register_callback_by_service_data_uuid(
inject_advertisement(hass, apple_device, apple_adv) inject_advertisement(hass, apple_device, apple_adv)
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
inject_advertisement(hass, empty_device, empty_adv) inject_advertisement(hass, empty_device, empty_adv)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1981,13 +2021,13 @@ async def test_register_callback_survives_reload(
assert len(mock_bleak_scanner_start.mock_calls) == 1 assert len(mock_bleak_scanner_start.mock_calls) == 1
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["zba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["zba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["zba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["zba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
@ -2035,7 +2075,7 @@ async def test_process_advertisements_bail_on_good_advertisement(
while not done.done(): while not done.done():
device = BLEDevice("aa:44:33:11:23:45", "wohand") device = BLEDevice("aa:44:33:11:23:45", "wohand")
adv = AdvertisementData( adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
@ -2060,13 +2100,13 @@ async def test_process_advertisements_ignore_bad_advertisement(
return_value = asyncio.Event() return_value = asyncio.Event()
device = BLEDevice("aa:44:33:11:23:45", "wohand") device = BLEDevice("aa:44:33:11:23:45", "wohand")
adv = AdvertisementData( adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fa": b""}, service_data={"00000d00-0000-1000-8000-00805f9b34fa": b""},
) )
adv2 = AdvertisementData( adv2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51a"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
@ -2142,20 +2182,20 @@ async def test_wrapped_instance_with_filter(
detected.append((device, advertisement_data)) detected.append((device, advertisement_data))
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
assert _get_manager() is not None assert _get_manager() is not None
scanner = models.HaBleakScannerWrapper( scanner = models.HaBleakScannerWrapper(
@ -2214,20 +2254,20 @@ async def test_wrapped_instance_with_service_uuids(
detected.append((device, advertisement_data)) detected.append((device, advertisement_data))
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
assert _get_manager() is not None assert _get_manager() is not None
scanner = models.HaBleakScannerWrapper( scanner = models.HaBleakScannerWrapper(
@ -2272,7 +2312,7 @@ async def test_wrapped_instance_with_broken_callbacks(
detected.append((device, advertisement_data)) detected.append((device, advertisement_data))
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
@ -2313,20 +2353,20 @@ async def test_wrapped_instance_changes_uuids(
detected.append((device, advertisement_data)) detected.append((device, advertisement_data))
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
empty_device = BLEDevice("11:22:33:44:55:66", "empty") empty_device = BLEDevice("11:22:33:44:55:66", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
assert _get_manager() is not None assert _get_manager() is not None
scanner = models.HaBleakScannerWrapper() scanner = models.HaBleakScannerWrapper()
@ -2368,20 +2408,20 @@ async def test_wrapped_instance_changes_filters(
detected.append((device, advertisement_data)) detected.append((device, advertisement_data))
switchbot_device = BLEDevice("44:44:33:11:23:42", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:42", "wohand")
switchbot_adv = AdvertisementData( switchbot_adv = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
switchbot_adv_2 = AdvertisementData( switchbot_adv_2 = generate_advertisement_data(
local_name="wohand", local_name="wohand",
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x84"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
) )
empty_device = BLEDevice("11:22:33:44:55:62", "empty") empty_device = BLEDevice("11:22:33:44:55:62", "empty")
empty_adv = AdvertisementData(local_name="empty") empty_adv = generate_advertisement_data(local_name="empty")
assert _get_manager() is not None assert _get_manager() is not None
scanner = models.HaBleakScannerWrapper() scanner = models.HaBleakScannerWrapper()
@ -2434,8 +2474,8 @@ async def test_async_ble_device_from_address(
with patch( with patch(
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt "homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
), patch( ), patch(
"bleak.BleakScanner.discovered_devices", # Must patch before we setup "bleak.BleakScanner.discovered_devices_and_advertisement_data", # Must patch before we setup
[MagicMock(address="44:44:33:11:23:45")], {"44:44:33:11:23:45": (MagicMock(address="44:44:33:11:23:45"), MagicMock())},
): ):
assert not bluetooth.async_discovered_service_info(hass) assert not bluetooth.async_discovered_service_info(hass)
assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22") assert not bluetooth.async_address_present(hass, "44:44:22:22:11:22")
@ -2453,7 +2493,9 @@ async def test_async_ble_device_from_address(
assert not bluetooth.async_discovered_service_info(hass) assert not bluetooth.async_discovered_service_info(hass)
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData(local_name="wohand", service_uuids=[]) switchbot_adv = generate_advertisement_data(
local_name="wohand", service_uuids=[]
)
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
await hass.async_block_till_done() await hass.async_block_till_done()

View file

@ -1,8 +1,9 @@
"""Tests for the Bluetooth integration manager.""" """Tests for the Bluetooth integration manager."""
import time
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from bleak.backends.scanner import AdvertisementData, BLEDevice from bleak.backends.scanner import BLEDevice
from bluetooth_adapters import AdvertisementHistory from bluetooth_adapters import AdvertisementHistory
from homeassistant.components import bluetooth from homeassistant.components import bluetooth
@ -12,8 +13,10 @@ from homeassistant.components.bluetooth.manager import (
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from . import ( from . import (
generate_advertisement_data,
inject_advertisement_with_source, inject_advertisement_with_source,
inject_advertisement_with_time_and_source, inject_advertisement_with_time_and_source,
inject_advertisement_with_time_and_source_connectable,
) )
@ -25,7 +28,7 @@ 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 = BLEDevice(address, "wohand_signal_100", rssi=-100) switchbot_device_signal_100 = BLEDevice(address, "wohand_signal_100", rssi=-100)
switchbot_adv_signal_100 = AdvertisementData( switchbot_adv_signal_100 = generate_advertisement_data(
local_name="wohand_signal_100", service_uuids=[] local_name="wohand_signal_100", service_uuids=[]
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -38,7 +41,7 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason(
) )
switchbot_device_signal_99 = BLEDevice(address, "wohand_signal_99", rssi=-99) switchbot_device_signal_99 = BLEDevice(address, "wohand_signal_99", rssi=-99)
switchbot_adv_signal_99 = AdvertisementData( switchbot_adv_signal_99 = generate_advertisement_data(
local_name="wohand_signal_99", service_uuids=[] local_name="wohand_signal_99", service_uuids=[]
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -51,7 +54,7 @@ async def test_advertisements_do_not_switch_adapters_for_no_reason(
) )
switchbot_device_signal_98 = BLEDevice(address, "wohand_good_signal", rssi=-98) switchbot_device_signal_98 = BLEDevice(address, "wohand_good_signal", rssi=-98)
switchbot_adv_signal_98 = AdvertisementData( switchbot_adv_signal_98 = generate_advertisement_data(
local_name="wohand_good_signal", service_uuids=[] local_name="wohand_good_signal", service_uuids=[]
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -70,9 +73,9 @@ async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth):
address = "44:44:33:11:23:45" address = "44:44:33:11:23:45"
switchbot_device_poor_signal = BLEDevice(address, "wohand_poor_signal", rssi=-100) switchbot_device_poor_signal = BLEDevice(address, "wohand_poor_signal")
switchbot_adv_poor_signal = AdvertisementData( switchbot_adv_poor_signal = generate_advertisement_data(
local_name="wohand_poor_signal", service_uuids=[] local_name="wohand_poor_signal", service_uuids=[], rssi=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0" hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, "hci0"
@ -83,9 +86,9 @@ async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth):
is switchbot_device_poor_signal is switchbot_device_poor_signal
) )
switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal", rssi=-60) switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal")
switchbot_adv_good_signal = AdvertisementData( switchbot_adv_good_signal = generate_advertisement_data(
local_name="wohand_good_signal", service_uuids=[] local_name="wohand_good_signal", service_uuids=[], rssi=-60
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci1" hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci1"
@ -105,11 +108,9 @@ async def test_switching_adapters_based_on_rssi(hass, enable_bluetooth):
) )
# We should not switch adapters unless the signal hits the threshold # We should not switch adapters unless the signal hits the threshold
switchbot_device_similar_signal = BLEDevice( switchbot_device_similar_signal = BLEDevice(address, "wohand_similar_signal")
address, "wohand_similar_signal", rssi=-62 switchbot_adv_similar_signal = generate_advertisement_data(
) local_name="wohand_similar_signal", service_uuids=[], rssi=-62
switchbot_adv_similar_signal = AdvertisementData(
local_name="wohand_similar_signal", service_uuids=[]
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -126,9 +127,9 @@ async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth):
address = "44:44:33:11:23:45" address = "44:44:33:11:23:45"
switchbot_device_no_rssi = BLEDevice(address, "wohand_poor_signal", rssi=0) switchbot_device_no_rssi = BLEDevice(address, "wohand_poor_signal")
switchbot_adv_no_rssi = AdvertisementData( switchbot_adv_no_rssi = generate_advertisement_data(
local_name="wohand_no_rssi", service_uuids=[] local_name="wohand_no_rssi", service_uuids=[], rssi=0
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_no_rssi, switchbot_adv_no_rssi, "hci0" hass, switchbot_device_no_rssi, switchbot_adv_no_rssi, "hci0"
@ -139,9 +140,9 @@ async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth):
is switchbot_device_no_rssi is switchbot_device_no_rssi
) )
switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal", rssi=-60) switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal")
switchbot_adv_good_signal = AdvertisementData( switchbot_adv_good_signal = generate_advertisement_data(
local_name="wohand_good_signal", service_uuids=[] local_name="wohand_good_signal", service_uuids=[], rssi=-60
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci1" hass, switchbot_device_good_signal, switchbot_adv_good_signal, "hci1"
@ -161,11 +162,9 @@ async def test_switching_adapters_based_on_zero_rssi(hass, enable_bluetooth):
) )
# We should not switch adapters unless the signal hits the threshold # We should not switch adapters unless the signal hits the threshold
switchbot_device_similar_signal = BLEDevice( switchbot_device_similar_signal = BLEDevice(address, "wohand_similar_signal")
address, "wohand_similar_signal", rssi=-62 switchbot_adv_similar_signal = generate_advertisement_data(
) local_name="wohand_similar_signal", service_uuids=[], rssi=-62
switchbot_adv_similar_signal = AdvertisementData(
local_name="wohand_similar_signal", service_uuids=[]
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -183,11 +182,9 @@ async def test_switching_adapters_based_on_stale(hass, enable_bluetooth):
address = "44:44:33:11:23:41" address = "44:44:33:11:23:41"
start_time_monotonic = 50.0 start_time_monotonic = 50.0
switchbot_device_poor_signal_hci0 = BLEDevice( switchbot_device_poor_signal_hci0 = BLEDevice(address, "wohand_poor_signal_hci0")
address, "wohand_poor_signal_hci0", rssi=-100 switchbot_adv_poor_signal_hci0 = generate_advertisement_data(
) local_name="wohand_poor_signal_hci0", service_uuids=[], rssi=-100
switchbot_adv_poor_signal_hci0 = AdvertisementData(
local_name="wohand_poor_signal_hci0", service_uuids=[]
) )
inject_advertisement_with_time_and_source( inject_advertisement_with_time_and_source(
hass, hass,
@ -202,11 +199,9 @@ async def test_switching_adapters_based_on_stale(hass, enable_bluetooth):
is switchbot_device_poor_signal_hci0 is switchbot_device_poor_signal_hci0
) )
switchbot_device_poor_signal_hci1 = BLEDevice( switchbot_device_poor_signal_hci1 = BLEDevice(address, "wohand_poor_signal_hci1")
address, "wohand_poor_signal_hci1", rssi=-99 switchbot_adv_poor_signal_hci1 = generate_advertisement_data(
) local_name="wohand_poor_signal_hci1", service_uuids=[], rssi=-99
switchbot_adv_poor_signal_hci1 = AdvertisementData(
local_name="wohand_poor_signal_hci1", service_uuids=[]
) )
inject_advertisement_with_time_and_source( inject_advertisement_with_time_and_source(
hass, hass,
@ -246,7 +241,7 @@ async def test_restore_history_from_dbus(hass, one_adapter):
ble_device = BLEDevice(address, "name") ble_device = BLEDevice(address, "name")
history = { history = {
address: AdvertisementHistory( address: AdvertisementHistory(
ble_device, AdvertisementData(local_name="name"), "hci0" ble_device, generate_advertisement_data(local_name="name"), "hci0"
) )
} }
@ -258,3 +253,86 @@ async def test_restore_history_from_dbus(hass, one_adapter):
await hass.async_block_till_done() await hass.async_block_till_done()
assert bluetooth.async_ble_device_from_address(hass, address) is ble_device assert bluetooth.async_ble_device_from_address(hass, address) is ble_device
async def test_switching_adapters_based_on_rssi_connectable_to_non_connectable(
hass, enable_bluetooth
):
"""Test switching adapters based on rssi from connectable to non connectable."""
address = "44:44:33:11:23:45"
now = time.monotonic()
switchbot_device_poor_signal = BLEDevice(address, "wohand_poor_signal")
switchbot_adv_poor_signal = generate_advertisement_data(
local_name="wohand_poor_signal", service_uuids=[], rssi=-100
)
inject_advertisement_with_time_and_source_connectable(
hass, switchbot_device_poor_signal, switchbot_adv_poor_signal, now, "hci0", True
)
assert (
bluetooth.async_ble_device_from_address(hass, address, False)
is switchbot_device_poor_signal
)
assert (
bluetooth.async_ble_device_from_address(hass, address, True)
is switchbot_device_poor_signal
)
switchbot_device_good_signal = BLEDevice(address, "wohand_good_signal")
switchbot_adv_good_signal = generate_advertisement_data(
local_name="wohand_good_signal", service_uuids=[], rssi=-60
)
inject_advertisement_with_time_and_source_connectable(
hass,
switchbot_device_good_signal,
switchbot_adv_good_signal,
now,
"hci1",
False,
)
assert (
bluetooth.async_ble_device_from_address(hass, address, False)
is switchbot_device_good_signal
)
assert (
bluetooth.async_ble_device_from_address(hass, address, True)
is switchbot_device_poor_signal
)
inject_advertisement_with_time_and_source_connectable(
hass,
switchbot_device_good_signal,
switchbot_adv_poor_signal,
now,
"hci0",
False,
)
assert (
bluetooth.async_ble_device_from_address(hass, address, False)
is switchbot_device_good_signal
)
assert (
bluetooth.async_ble_device_from_address(hass, address, True)
is switchbot_device_poor_signal
)
switchbot_device_excellent_signal = BLEDevice(address, "wohand_excellent_signal")
switchbot_adv_excellent_signal = generate_advertisement_data(
local_name="wohand_excellent_signal", service_uuids=[], rssi=-25
)
inject_advertisement_with_time_and_source_connectable(
hass,
switchbot_device_excellent_signal,
switchbot_adv_excellent_signal,
now,
"hci2",
False,
)
assert (
bluetooth.async_ble_device_from_address(hass, address, False)
is switchbot_device_excellent_signal
)
assert (
bluetooth.async_ble_device_from_address(hass, address, True)
is switchbot_device_poor_signal
)

View file

@ -16,7 +16,12 @@ from homeassistant.components.bluetooth.models import (
HaBluetoothConnector, HaBluetoothConnector,
) )
from . import _get_manager, inject_advertisement, inject_advertisement_with_source from . import (
_get_manager,
generate_advertisement_data,
inject_advertisement,
inject_advertisement_with_source,
)
class MockBleakClient(BleakClient): class MockBleakClient(BleakClient):
@ -49,7 +54,7 @@ async def test_wrapped_bleak_scanner(hass, enable_bluetooth):
"""Test wrapped bleak scanner dispatches calls as expected.""" """Test wrapped bleak scanner dispatches calls as expected."""
scanner = HaBleakScannerWrapper() scanner = HaBleakScannerWrapper()
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
switchbot_adv = AdvertisementData( 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"}
) )
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
@ -84,7 +89,7 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected(
switchbot_device = BLEDevice( switchbot_device = BLEDevice(
"44:44:33:11:23:45", "wohand", {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"} "44:44:33:11:23:45", "wohand", {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"}
) )
switchbot_adv = AdvertisementData( 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"}
) )
inject_advertisement(hass, switchbot_device, switchbot_adv) inject_advertisement(hass, switchbot_device, switchbot_adv)
@ -116,7 +121,7 @@ async def test_ble_device_with_proxy_client_out_of_connections(
}, },
rssi=-30, rssi=-30,
) )
switchbot_adv = AdvertisementData( 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"}
) )
@ -153,6 +158,11 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
), ),
"path": "/org/bluez/hci0/dev_44_44_33_11_23_45", "path": "/org/bluez/hci0/dev_44_44_33_11_23_45",
}, },
)
switchbot_proxy_device_adv_no_connection_slot = generate_advertisement_data(
local_name="wohand",
service_uuids=[],
manufacturer_data={1: b"\x01"},
rssi=-30, rssi=-30,
) )
switchbot_proxy_device_has_connection_slot = BLEDevice( switchbot_proxy_device_has_connection_slot = BLEDevice(
@ -166,14 +176,19 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
}, },
rssi=-40, rssi=-40,
) )
switchbot_proxy_device_adv_has_connection_slot = generate_advertisement_data(
local_name="wohand",
service_uuids=[],
manufacturer_data={1: b"\x01"},
rssi=-40,
)
switchbot_device = BLEDevice( switchbot_device = BLEDevice(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand", "wohand",
{"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"}, {"path": "/org/bluez/hci0/dev_44_44_33_11_23_45"},
rssi=-100,
) )
switchbot_adv = AdvertisementData( 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=-100
) )
inject_advertisement_with_source( inject_advertisement_with_source(
@ -182,21 +197,28 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
inject_advertisement_with_source( inject_advertisement_with_source(
hass, hass,
switchbot_proxy_device_has_connection_slot, switchbot_proxy_device_has_connection_slot,
switchbot_adv, switchbot_proxy_device_adv_has_connection_slot,
"esp32_has_connection_slot", "esp32_has_connection_slot",
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, hass,
switchbot_proxy_device_no_connection_slot, switchbot_proxy_device_no_connection_slot,
switchbot_adv, switchbot_proxy_device_adv_no_connection_slot,
"esp32_no_connection_slot", "esp32_no_connection_slot",
) )
class FakeScanner(BaseHaScanner): class FakeScanner(BaseHaScanner):
@property @property
def discovered_devices(self) -> list[BLEDevice]: def discovered_devices_and_advertisement_data(
self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
return [switchbot_proxy_device_has_connection_slot] return {
switchbot_proxy_device_has_connection_slot.address: (
switchbot_proxy_device_has_connection_slot,
switchbot_proxy_device_adv_has_connection_slot,
)
}
async def async_get_device_by_address(self, address: str) -> BLEDevice | None: async def async_get_device_by_address(self, address: str) -> BLEDevice | None:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
@ -237,7 +259,12 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
rssi=-30, rssi=-30,
) )
switchbot_proxy_device_no_connection_slot.metadata["delegate"] = 0 switchbot_proxy_device_no_connection_slot.metadata["delegate"] = 0
switchbot_proxy_device_no_connection_slot_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=[],
manufacturer_data={1: b"\x01"},
rssi=-30,
)
switchbot_proxy_device_has_connection_slot = BLEDevice( switchbot_proxy_device_has_connection_slot = BLEDevice(
"44:44:33:11:23:45", "44:44:33:11:23:45",
"wohand_has_connection_slot", "wohand_has_connection_slot",
@ -247,9 +274,14 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
), ),
"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_has_connection_slot.metadata["delegate"] = 0 switchbot_proxy_device_has_connection_slot.metadata["delegate"] = 0
switchbot_proxy_device_has_connection_slot_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=[],
manufacturer_data={1: b"\x01"},
rssi=-40,
)
switchbot_device = BLEDevice( switchbot_device = BLEDevice(
"44:44:33:11:23:45", "44:44:33:11:23:45",
@ -258,31 +290,41 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
rssi=-100, rssi=-100,
) )
switchbot_device.metadata["delegate"] = 0 switchbot_device.metadata["delegate"] = 0
switchbot_adv = AdvertisementData( switchbot_device_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=-100,
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, switchbot_device, switchbot_adv, "00:00:00:00:00:01" hass, switchbot_device, switchbot_device_adv, "00:00:00:00:00:01"
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, hass,
switchbot_proxy_device_has_connection_slot, switchbot_proxy_device_has_connection_slot,
switchbot_adv, switchbot_proxy_device_has_connection_slot_adv,
"esp32_has_connection_slot", "esp32_has_connection_slot",
) )
inject_advertisement_with_source( inject_advertisement_with_source(
hass, hass,
switchbot_proxy_device_no_connection_slot, switchbot_proxy_device_no_connection_slot,
switchbot_adv, switchbot_proxy_device_no_connection_slot_adv,
"esp32_no_connection_slot", "esp32_no_connection_slot",
) )
class FakeScanner(BaseHaScanner): class FakeScanner(BaseHaScanner):
@property @property
def discovered_devices(self) -> list[BLEDevice]: def discovered_devices_and_advertisement_data(
self,
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""
return [switchbot_proxy_device_has_connection_slot] return {
switchbot_proxy_device_has_connection_slot.address: (
switchbot_proxy_device_has_connection_slot,
switchbot_proxy_device_has_connection_slot_adv,
)
}
async def async_get_device_by_address(self, address: str) -> BLEDevice | None: async def async_get_device_by_address(self, address: str) -> BLEDevice | None:
"""Return a list of discovered devices.""" """Return a list of discovered devices."""

View file

@ -127,8 +127,8 @@ async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
): ):
"""Test that the coordinator goes unavailable when the bluetooth stack no longer sees the device.""" """Test that the coordinator goes unavailable when the bluetooth stack no longer sees the device."""
with patch( with patch(
"bleak.BleakScanner.discovered_devices", # Must patch before we setup "bleak.BleakScanner.discovered_devices_and_advertisement_data", # Must patch before we setup
[MagicMock(address="44:44:33:11:23:45")], {"44:44:33:11:23:45": (MagicMock(address="44:44:33:11:23:45"), MagicMock())},
): ):
await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done() await hass.async_block_till_done()

View file

@ -201,8 +201,8 @@ async def test_unavailable_after_no_data(
): ):
"""Test that the coordinator is unavailable after no data for a while.""" """Test that the coordinator is unavailable after no data for a while."""
with patch( with patch(
"bleak.BleakScanner.discovered_devices", # Must patch before we setup "bleak.BleakScanner.discovered_devices_and_advertisement_data", # Must patch before we setup
[MagicMock(address="44:44:33:11:23:45")], {"44:44:33:11:23:45": (MagicMock(address="44:44:33:11:23:45"), MagicMock())},
): ):
await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
await hass.async_block_till_done() await hass.async_block_till_done()

View file

@ -4,11 +4,7 @@ import time
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from bleak import BleakError from bleak import BleakError
from bleak.backends.scanner import ( from bleak.backends.scanner import AdvertisementDataCallback, BLEDevice
AdvertisementData,
AdvertisementDataCallback,
BLEDevice,
)
from dbus_fast import InvalidMessageError from dbus_fast import InvalidMessageError
import pytest import pytest
@ -22,7 +18,7 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import _get_manager, async_setup_with_one_adapter from . import _get_manager, async_setup_with_one_adapter, generate_advertisement_data
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
@ -222,7 +218,7 @@ async def test_recovery_from_dbus_restart(hass, one_adapter):
): ):
_callback( _callback(
BLEDevice("44:44:33:11:23:42", "any_name"), BLEDevice("44:44:33:11:23:42", "any_name"),
AdvertisementData(local_name="any_name"), generate_advertisement_data(local_name="any_name"),
) )
# Ensure we don't restart the scanner if we don't need to # Ensure we don't restart the scanner if we don't need to

View file

@ -5,7 +5,7 @@ from datetime import timedelta
from unittest.mock import patch from unittest.mock import patch
from bleak import BleakError from bleak import BleakError
from bleak.backends.scanner import AdvertisementData, BLEDevice from bleak.backends.scanner import BLEDevice
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.components.bluetooth_le_tracker import device_tracker from homeassistant.components.bluetooth_le_tracker import device_tracker
@ -23,6 +23,7 @@ from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util, slugify from homeassistant.util import dt as dt_util, slugify
from tests.common import async_fire_time_changed from tests.common import async_fire_time_changed
from tests.components.bluetooth import generate_advertisement_data
class MockBleakClient: class MockBleakClient:
@ -89,7 +90,7 @@ async def test_preserve_new_tracked_device_name(
service_uuids=[], service_uuids=[],
source="local", source="local",
device=BLEDevice(address, None), device=BLEDevice(address, None),
advertisement=AdvertisementData(local_name="empty"), advertisement=generate_advertisement_data(local_name="empty"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -114,7 +115,7 @@ async def test_preserve_new_tracked_device_name(
service_uuids=[], service_uuids=[],
source="local", source="local",
device=BLEDevice(address, None), device=BLEDevice(address, None),
advertisement=AdvertisementData(local_name="empty"), advertisement=generate_advertisement_data(local_name="empty"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -158,7 +159,7 @@ async def test_tracking_battery_times_out(
service_uuids=[], service_uuids=[],
source="local", source="local",
device=BLEDevice(address, None), device=BLEDevice(address, None),
advertisement=AdvertisementData(local_name="empty"), advertisement=generate_advertisement_data(local_name="empty"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -224,7 +225,7 @@ async def test_tracking_battery_fails(hass, mock_bluetooth, mock_device_tracker_
service_uuids=[], service_uuids=[],
source="local", source="local",
device=BLEDevice(address, None), device=BLEDevice(address, None),
advertisement=AdvertisementData(local_name="empty"), advertisement=generate_advertisement_data(local_name="empty"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -292,7 +293,7 @@ async def test_tracking_battery_successful(
service_uuids=[], service_uuids=[],
source="local", source="local",
device=BLEDevice(address, None), device=BLEDevice(address, None),
advertisement=AdvertisementData(local_name="empty"), advertisement=generate_advertisement_data(local_name="empty"),
time=0, time=0,
connectable=True, connectable=True,
) )

View file

@ -1,10 +1,11 @@
"""Tests for the BTHome integration.""" """Tests for the BTHome integration."""
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
TEMP_HUMI_SERVICE_INFO = BluetoothServiceInfoBleak( TEMP_HUMI_SERVICE_INFO = BluetoothServiceInfoBleak(
name="ATC 8D18B2", name="ATC 8D18B2",
address="A4:C1:38:8D:18:B2", address="A4:C1:38:8D:18:B2",
@ -16,7 +17,7 @@ TEMP_HUMI_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -32,7 +33,7 @@ TEMP_HUMI_ENCRYPTED_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000181e-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181e-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -48,7 +49,7 @@ PRST_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="prst"), advertisement=generate_advertisement_data(local_name="prst"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -64,7 +65,7 @@ INVALID_PAYLOAD = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -78,7 +79,7 @@ NOT_BTHOME_SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
service_uuids=[], service_uuids=[],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -97,7 +98,7 @@ def make_advertisement(address: str, payload: bytes) -> BluetoothServiceInfoBlea
}, },
service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Test Device"), advertisement=generate_advertisement_data(local_name="Test Device"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -118,7 +119,7 @@ def make_encrypted_advertisement(
}, },
service_uuids=["0000181e-0000-1000-8000-00805f9b34fb"], service_uuids=["0000181e-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="ATC 8F80A5"), advertisement=generate_advertisement_data(local_name="ATC 8F80A5"),
time=0, time=0,
connectable=False, connectable=False,
) )

View file

@ -2,10 +2,11 @@
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
COOKER_SERVICE_INFO = BluetoothServiceInfoBleak( COOKER_SERVICE_INFO = BluetoothServiceInfoBleak(
name="COOKERHOOD_FJAR", name="COOKERHOOD_FJAR",
address="AA:BB:CC:DD:EE:FF", address="AA:BB:CC:DD:EE:FF",
@ -15,7 +16,7 @@ COOKER_SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="COOKERHOOD_FJAR"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="COOKERHOOD_FJAR"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )

View file

@ -2,11 +2,12 @@
from unittest.mock import patch from unittest.mock import patch
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.const import CONF_ADDRESS from homeassistant.const import CONF_ADDRESS
from tests.components.bluetooth import generate_advertisement_data
DOMAIN = "keymitt_ble" DOMAIN = "keymitt_ble"
ENTRY_CONFIG = { ENTRY_CONFIG = {
@ -38,7 +39,7 @@ SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="mibp", local_name="mibp",
manufacturer_data={}, manufacturer_data={},
service_uuids=["0000abcd-0000-1000-8000-00805f9b34fb"], service_uuids=["0000abcd-0000-1000-8000-00805f9b34fb"],

View file

@ -1,9 +1,10 @@
"""Tests for the LED BLE Bluetooth integration.""" """Tests for the LED BLE Bluetooth integration."""
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
LED_BLE_DISCOVERY_INFO = BluetoothServiceInfoBleak( LED_BLE_DISCOVERY_INFO = BluetoothServiceInfoBleak(
name="Triones:F30200000152C", name="Triones:F30200000152C",
address="AA:BB:CC:DD:EE:FF", address="AA:BB:CC:DD:EE:FF",
@ -13,7 +14,7 @@ LED_BLE_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Triones:F30200000152C"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Triones:F30200000152C"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -27,7 +28,7 @@ UNSUPPORTED_LED_BLE_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="LEDnetWFF30200000152C"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="LEDnetWFF30200000152C"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -45,7 +46,7 @@ NOT_LED_BLE_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )

View file

@ -5,7 +5,6 @@ from __future__ import annotations
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from melnor_bluetooth.device import Device from melnor_bluetooth.device import Device
from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak
@ -14,6 +13,7 @@ from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.bluetooth import generate_advertisement_data
FAKE_ADDRESS_1 = "FAKE-ADDRESS-1" FAKE_ADDRESS_1 = "FAKE-ADDRESS-1"
FAKE_ADDRESS_2 = "FAKE-ADDRESS-2" FAKE_ADDRESS_2 = "FAKE-ADDRESS-2"
@ -30,7 +30,7 @@ FAKE_SERVICE_INFO_1 = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(FAKE_ADDRESS_1, None), device=BLEDevice(FAKE_ADDRESS_1, None),
advertisement=AdvertisementData(local_name=""), advertisement=generate_advertisement_data(local_name=""),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -46,7 +46,7 @@ FAKE_SERVICE_INFO_2 = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(FAKE_ADDRESS_2, None), device=BLEDevice(FAKE_ADDRESS_2, None),
advertisement=AdvertisementData(local_name=""), advertisement=generate_advertisement_data(local_name=""),
time=0, time=0,
connectable=True, connectable=True,
) )

View file

@ -2,13 +2,13 @@
from unittest.mock import patch from unittest.mock import patch
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from homeassistant.const import CONF_ADDRESS from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.components.bluetooth import generate_advertisement_data
DOMAIN = "switchbot" DOMAIN = "switchbot"
@ -62,7 +62,7 @@ WOHAND_SERVICE_INFO = BluetoothServiceInfoBleak(
address="AA:BB:CC:DD:EE:FF", address="AA:BB:CC:DD:EE:FF",
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="WoHand", local_name="WoHand",
manufacturer_data={89: b"\xfd`0U\x92W"}, manufacturer_data={89: b"\xfd`0U\x92W"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"},
@ -82,7 +82,7 @@ WOHAND_SERVICE_INFO_NOT_CONNECTABLE = BluetoothServiceInfoBleak(
address="aa:bb:cc:dd:ee:ff", address="aa:bb:cc:dd:ee:ff",
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="WoHand", local_name="WoHand",
manufacturer_data={89: b"\xfd`0U\x92W"}, manufacturer_data={89: b"\xfd`0U\x92W"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"},
@ -102,7 +102,7 @@ WOHAND_ENCRYPTED_SERVICE_INFO = BluetoothServiceInfoBleak(
address="798A8547-2A3D-C609-55FF-73FA824B923B", address="798A8547-2A3D-C609-55FF-73FA824B923B",
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="WoHand", local_name="WoHand",
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"}, manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"\xc8\x10\xcf"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"\xc8\x10\xcf"},
@ -122,7 +122,7 @@ WOHAND_SERVICE_ALT_ADDRESS_INFO = BluetoothServiceInfoBleak(
address="cc:cc:cc:cc:cc:cc", address="cc:cc:cc:cc:cc:cc",
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="WoHand", local_name="WoHand",
manufacturer_data={89: b"\xfd`0U\x92W"}, manufacturer_data={89: b"\xfd`0U\x92W"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x90\xd9"},
@ -140,7 +140,7 @@ WOCURTAIN_SERVICE_INFO = BluetoothServiceInfoBleak(
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
local_name="WoCurtain", local_name="WoCurtain",
manufacturer_data={89: b"\xc1\xc7'}U\xab"}, manufacturer_data={89: b"\xc1\xc7'}U\xab"},
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"c\xd0Y\x00\x11\x04"}, service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"c\xd0Y\x00\x11\x04"},
@ -159,7 +159,7 @@ WOSENSORTH_SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"T\x00d\x00\x96\xac"}, service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"T\x00d\x00\x96\xac"},
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
manufacturer_data={2409: b"\xda,\x1e\xb1\x86Au\x03\x00\x96\xac"}, manufacturer_data={2409: b"\xda,\x1e\xb1\x86Au\x03\x00\x96\xac"},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"T\x00d\x00\x96\xac"}, service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"T\x00d\x00\x96\xac"},
), ),
@ -176,7 +176,7 @@ NOT_SWITCHBOT_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
rssi=-60, rssi=-60,
source="local", source="local",
advertisement=AdvertisementData( advertisement=generate_advertisement_data(
manufacturer_data={}, manufacturer_data={},
service_data={}, service_data={},
), ),

View file

@ -1,10 +1,11 @@
"""Tests for the SensorPush integration.""" """Tests for the SensorPush integration."""
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
NOT_SENSOR_PUSH_SERVICE_INFO = BluetoothServiceInfoBleak( NOT_SENSOR_PUSH_SERVICE_INFO = BluetoothServiceInfoBleak(
name="Not it", name="Not it",
address="00:00:00:00:00:00", address="00:00:00:00:00:00",
@ -14,7 +15,7 @@ NOT_SENSOR_PUSH_SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
service_uuids=[], service_uuids=[],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -30,7 +31,7 @@ LYWSDCGQ_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -46,7 +47,7 @@ MMC_T201_1_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -62,7 +63,7 @@ JTYJGD03MI_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -78,7 +79,7 @@ YLKG07YL_SERVICE_INFO = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -94,7 +95,7 @@ MISSING_PAYLOAD_ENCRYPTED = BluetoothServiceInfoBleak(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Not it"), advertisement=generate_advertisement_data(local_name="Not it"),
time=0, time=0,
connectable=False, connectable=False,
) )
@ -115,7 +116,7 @@ def make_advertisement(
}, },
service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"], service_uuids=["0000fe95-0000-1000-8000-00805f9b34fb"],
source="local", source="local",
advertisement=AdvertisementData(local_name="Test Device"), advertisement=generate_advertisement_data(local_name="Test Device"),
time=0, time=0,
connectable=connectable, connectable=connectable,
) )

View file

@ -1,9 +1,10 @@
"""Tests for the Yale Access Bluetooth integration.""" """Tests for the Yale Access Bluetooth integration."""
from bleak.backends.device import BLEDevice from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
from tests.components.bluetooth import generate_advertisement_data
YALE_ACCESS_LOCK_DISCOVERY_INFO = BluetoothServiceInfoBleak( YALE_ACCESS_LOCK_DISCOVERY_INFO = BluetoothServiceInfoBleak(
name="M1012LU", name="M1012LU",
address="AA:BB:CC:DD:EE:FF", address="AA:BB:CC:DD:EE:FF",
@ -16,7 +17,7 @@ YALE_ACCESS_LOCK_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="M1012LU"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="M1012LU"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -34,7 +35,7 @@ LOCK_DISCOVERY_INFO_UUID_ADDRESS = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="M1012LU"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="M1012LU"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -51,7 +52,7 @@ OLD_FIRMWARE_LOCK_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )
@ -69,7 +70,7 @@ NOT_YALE_DISCOVERY_INFO = BluetoothServiceInfoBleak(
service_data={}, service_data={},
source="local", source="local",
device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"), device=BLEDevice(address="AA:BB:CC:DD:EE:FF", name="Aug"),
advertisement=AdvertisementData(), advertisement=generate_advertisement_data(),
time=0, time=0,
connectable=True, connectable=True,
) )