Relocate base Bluetooth scanner code into an external library (#104930)
parent
c8bb72935d
commit
28584ad240
|
@ -21,6 +21,7 @@ from bluetooth_adapters import (
|
|||
adapter_unique_name,
|
||||
get_adapters,
|
||||
)
|
||||
from habluetooth import HaBluetoothConnector
|
||||
from home_assistant_bluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak
|
||||
|
||||
from homeassistant.components import usb
|
||||
|
@ -77,12 +78,7 @@ from .const import (
|
|||
)
|
||||
from .manager import BluetoothManager
|
||||
from .match import BluetoothCallbackMatcher, IntegrationMatcher
|
||||
from .models import (
|
||||
BluetoothCallback,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
HaBluetoothConnector,
|
||||
)
|
||||
from .models import BluetoothCallback, BluetoothChange, BluetoothScanningMode
|
||||
from .scanner import MONOTONIC_TIME, HaScanner, ScannerStartError
|
||||
from .storage import BluetoothStorage
|
||||
|
||||
|
|
|
@ -1,19 +1,14 @@
|
|||
"""Base classes for HA Bluetooth scanners for bluetooth."""
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
import asyncio
|
||||
from collections.abc import Callable, Generator
|
||||
from contextlib import contextmanager
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any, Final, final
|
||||
from typing import Any
|
||||
|
||||
from bleak.backends.device import BLEDevice
|
||||
from bleak.backends.scanner import AdvertisementData
|
||||
from bleak_retry_connector import NO_RSSI_VALUE
|
||||
from bluetooth_adapters import DiscoveredDeviceAdvertisementData, adapter_human_name
|
||||
from bluetooth_data_tools import monotonic_time_coarse
|
||||
from bluetooth_adapters import DiscoveredDeviceAdvertisementData
|
||||
from habluetooth import BaseHaRemoteScanner, BaseHaScanner, HaBluetoothConnector
|
||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
|
@ -25,16 +20,6 @@ from homeassistant.core import (
|
|||
)
|
||||
|
||||
from . import models
|
||||
from .const import (
|
||||
CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
)
|
||||
from .models import HaBluetoothConnector
|
||||
|
||||
SCANNER_WATCHDOG_INTERVAL_SECONDS: Final = SCANNER_WATCHDOG_INTERVAL.total_seconds()
|
||||
MONOTONIC_TIME: Final = monotonic_time_coarse
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
|
@ -46,363 +31,6 @@ class BluetoothScannerDevice:
|
|||
advertisement: AdvertisementData
|
||||
|
||||
|
||||
class BaseHaScanner:
|
||||
"""Base class for high availability BLE scanners."""
|
||||
|
||||
__slots__ = (
|
||||
"adapter",
|
||||
"connectable",
|
||||
"source",
|
||||
"connector",
|
||||
"_connecting",
|
||||
"name",
|
||||
"scanning",
|
||||
"_last_detection",
|
||||
"_start_time",
|
||||
"_cancel_watchdog",
|
||||
"_loop",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
source: str,
|
||||
adapter: str,
|
||||
connector: HaBluetoothConnector | None = None,
|
||||
) -> None:
|
||||
"""Initialize the scanner."""
|
||||
self.connectable = False
|
||||
self.source = source
|
||||
self.connector = connector
|
||||
self._connecting = 0
|
||||
self.adapter = adapter
|
||||
self.name = adapter_human_name(adapter, source) if adapter != source else source
|
||||
self.scanning = True
|
||||
self._last_detection = 0.0
|
||||
self._start_time = 0.0
|
||||
self._cancel_watchdog: asyncio.TimerHandle | None = None
|
||||
self._loop: asyncio.AbstractEventLoop | None = None
|
||||
|
||||
@hass_callback
|
||||
def async_setup(self) -> CALLBACK_TYPE:
|
||||
"""Set up the scanner."""
|
||||
self._loop = asyncio.get_running_loop()
|
||||
return self._unsetup
|
||||
|
||||
@hass_callback
|
||||
def _async_stop_scanner_watchdog(self) -> None:
|
||||
"""Stop the scanner watchdog."""
|
||||
if self._cancel_watchdog:
|
||||
self._cancel_watchdog.cancel()
|
||||
self._cancel_watchdog = None
|
||||
|
||||
@hass_callback
|
||||
def _async_setup_scanner_watchdog(self) -> None:
|
||||
"""If something has restarted or updated, we need to restart the scanner."""
|
||||
self._start_time = self._last_detection = MONOTONIC_TIME()
|
||||
if not self._cancel_watchdog:
|
||||
self._schedule_watchdog()
|
||||
|
||||
def _schedule_watchdog(self) -> None:
|
||||
"""Schedule the watchdog."""
|
||||
loop = self._loop
|
||||
assert loop is not None
|
||||
self._cancel_watchdog = loop.call_at(
|
||||
loop.time() + SCANNER_WATCHDOG_INTERVAL_SECONDS,
|
||||
self._async_call_scanner_watchdog,
|
||||
)
|
||||
|
||||
@final
|
||||
def _async_call_scanner_watchdog(self) -> None:
|
||||
"""Call the scanner watchdog and schedule the next one."""
|
||||
self._async_scanner_watchdog()
|
||||
self._schedule_watchdog()
|
||||
|
||||
@hass_callback
|
||||
def _async_watchdog_triggered(self) -> bool:
|
||||
"""Check if the watchdog has been triggered."""
|
||||
time_since_last_detection = MONOTONIC_TIME() - self._last_detection
|
||||
_LOGGER.debug(
|
||||
"%s: Scanner watchdog time_since_last_detection: %s",
|
||||
self.name,
|
||||
time_since_last_detection,
|
||||
)
|
||||
return time_since_last_detection > SCANNER_WATCHDOG_TIMEOUT
|
||||
|
||||
@hass_callback
|
||||
def _async_scanner_watchdog(self) -> None:
|
||||
"""Check if the scanner is running.
|
||||
|
||||
Override this method if you need to do something else when the watchdog
|
||||
is triggered.
|
||||
"""
|
||||
if self._async_watchdog_triggered():
|
||||
_LOGGER.info(
|
||||
(
|
||||
"%s: Bluetooth scanner has gone quiet for %ss, check logs on the"
|
||||
" scanner device for more information"
|
||||
),
|
||||
self.name,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
)
|
||||
self.scanning = False
|
||||
return
|
||||
self.scanning = not self._connecting
|
||||
|
||||
@hass_callback
|
||||
def _unsetup(self) -> None:
|
||||
"""Unset up the scanner."""
|
||||
|
||||
@contextmanager
|
||||
def connecting(self) -> Generator[None, None, None]:
|
||||
"""Context manager to track connecting state."""
|
||||
self._connecting += 1
|
||||
self.scanning = not self._connecting
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self._connecting -= 1
|
||||
self.scanning = not self._connecting
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def discovered_devices(self) -> list[BLEDevice]:
|
||||
"""Return a list of discovered devices."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def discovered_devices_and_advertisement_data(
|
||||
self,
|
||||
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
"""Return a list of discovered devices and their advertisement data."""
|
||||
|
||||
async def async_diagnostics(self) -> dict[str, Any]:
|
||||
"""Return diagnostic information about the scanner."""
|
||||
device_adv_datas = self.discovered_devices_and_advertisement_data.values()
|
||||
return {
|
||||
"name": self.name,
|
||||
"start_time": self._start_time,
|
||||
"source": self.source,
|
||||
"scanning": self.scanning,
|
||||
"type": self.__class__.__name__,
|
||||
"last_detection": self._last_detection,
|
||||
"monotonic_time": MONOTONIC_TIME(),
|
||||
"discovered_devices_and_advertisement_data": [
|
||||
{
|
||||
"name": device.name,
|
||||
"address": device.address,
|
||||
"rssi": advertisement_data.rssi,
|
||||
"advertisement_data": advertisement_data,
|
||||
"details": device.details,
|
||||
}
|
||||
for device, advertisement_data in device_adv_datas
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class BaseHaRemoteScanner(BaseHaScanner):
|
||||
"""Base class for a high availability remote BLE scanner."""
|
||||
|
||||
__slots__ = (
|
||||
"_new_info_callback",
|
||||
"_discovered_device_advertisement_datas",
|
||||
"_discovered_device_timestamps",
|
||||
"_details",
|
||||
"_expire_seconds",
|
||||
"_cancel_track",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scanner_id: str,
|
||||
name: str,
|
||||
new_info_callback: Callable[[BluetoothServiceInfoBleak], None],
|
||||
connector: HaBluetoothConnector | None,
|
||||
connectable: bool,
|
||||
) -> None:
|
||||
"""Initialize the scanner."""
|
||||
super().__init__(scanner_id, name, connector)
|
||||
self._new_info_callback = new_info_callback
|
||||
self._discovered_device_advertisement_datas: dict[
|
||||
str, tuple[BLEDevice, AdvertisementData]
|
||||
] = {}
|
||||
self._discovered_device_timestamps: dict[str, float] = {}
|
||||
self.connectable = connectable
|
||||
self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id}
|
||||
# Scanners only care about connectable devices. The manager
|
||||
# will handle taking care of availability for non-connectable devices
|
||||
self._expire_seconds = CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
|
||||
self._cancel_track: asyncio.TimerHandle | None = None
|
||||
|
||||
def _cancel_expire_devices(self) -> None:
|
||||
"""Cancel the expiration of old devices."""
|
||||
if self._cancel_track:
|
||||
self._cancel_track.cancel()
|
||||
self._cancel_track = None
|
||||
|
||||
@hass_callback
|
||||
def _unsetup(self) -> None:
|
||||
"""Unset up the scanner."""
|
||||
self._async_stop_scanner_watchdog()
|
||||
self._cancel_expire_devices()
|
||||
|
||||
@hass_callback
|
||||
def async_setup(self) -> CALLBACK_TYPE:
|
||||
"""Set up the scanner."""
|
||||
super().async_setup()
|
||||
self._schedule_expire_devices()
|
||||
self._async_setup_scanner_watchdog()
|
||||
return self._unsetup
|
||||
|
||||
def _schedule_expire_devices(self) -> None:
|
||||
"""Schedule the expiration of old devices."""
|
||||
loop = self._loop
|
||||
assert loop is not None
|
||||
self._cancel_expire_devices()
|
||||
self._cancel_track = loop.call_at(loop.time() + 30, self._async_expire_devices)
|
||||
|
||||
@hass_callback
|
||||
def _async_expire_devices(self) -> None:
|
||||
"""Expire old devices."""
|
||||
now = MONOTONIC_TIME()
|
||||
expired = [
|
||||
address
|
||||
for address, timestamp in self._discovered_device_timestamps.items()
|
||||
if now - timestamp > self._expire_seconds
|
||||
]
|
||||
for address in expired:
|
||||
del self._discovered_device_advertisement_datas[address]
|
||||
del self._discovered_device_timestamps[address]
|
||||
self._schedule_expire_devices()
|
||||
|
||||
@property
|
||||
def discovered_devices(self) -> list[BLEDevice]:
|
||||
"""Return a list of discovered devices."""
|
||||
device_adv_datas = self._discovered_device_advertisement_datas.values()
|
||||
return [
|
||||
device_advertisement_data[0]
|
||||
for device_advertisement_data in device_adv_datas
|
||||
]
|
||||
|
||||
@property
|
||||
def discovered_devices_and_advertisement_data(
|
||||
self,
|
||||
) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
|
||||
"""Return a list of discovered devices and advertisement data."""
|
||||
return self._discovered_device_advertisement_datas
|
||||
|
||||
@hass_callback
|
||||
def _async_on_advertisement(
|
||||
self,
|
||||
address: str,
|
||||
rssi: int,
|
||||
local_name: str | None,
|
||||
service_uuids: list[str],
|
||||
service_data: dict[str, bytes],
|
||||
manufacturer_data: dict[int, bytes],
|
||||
tx_power: int | None,
|
||||
details: dict[Any, Any],
|
||||
advertisement_monotonic_time: float,
|
||||
) -> None:
|
||||
"""Call the registered callback."""
|
||||
self.scanning = not self._connecting
|
||||
self._last_detection = advertisement_monotonic_time
|
||||
try:
|
||||
prev_discovery = self._discovered_device_advertisement_datas[address]
|
||||
except KeyError:
|
||||
# We expect this is the rare case and since py3.11+ has
|
||||
# near zero cost try on success, and we can avoid .get()
|
||||
# which is slower than [] we use the try/except pattern.
|
||||
device = BLEDevice(
|
||||
address=address,
|
||||
name=local_name,
|
||||
details=self._details | details,
|
||||
rssi=rssi, # deprecated, will be removed in newer bleak
|
||||
)
|
||||
else:
|
||||
# Merge the new data with the old data
|
||||
# to function the same as BlueZ which
|
||||
# merges the dicts on PropertiesChanged
|
||||
prev_device = prev_discovery[0]
|
||||
prev_advertisement = prev_discovery[1]
|
||||
prev_service_uuids = prev_advertisement.service_uuids
|
||||
prev_service_data = prev_advertisement.service_data
|
||||
prev_manufacturer_data = prev_advertisement.manufacturer_data
|
||||
prev_name = prev_device.name
|
||||
|
||||
if prev_name and (not local_name or len(prev_name) > len(local_name)):
|
||||
local_name = prev_name
|
||||
|
||||
if service_uuids and service_uuids != prev_service_uuids:
|
||||
service_uuids = list({*service_uuids, *prev_service_uuids})
|
||||
elif not service_uuids:
|
||||
service_uuids = prev_service_uuids
|
||||
|
||||
if service_data and service_data != prev_service_data:
|
||||
service_data = prev_service_data | service_data
|
||||
elif not service_data:
|
||||
service_data = prev_service_data
|
||||
|
||||
if manufacturer_data and manufacturer_data != prev_manufacturer_data:
|
||||
manufacturer_data = prev_manufacturer_data | manufacturer_data
|
||||
elif not manufacturer_data:
|
||||
manufacturer_data = prev_manufacturer_data
|
||||
#
|
||||
# Bleak updates the BLEDevice via create_or_update_device.
|
||||
# We need to do the same to ensure integrations that already
|
||||
# have the BLEDevice object get the updated details when they
|
||||
# change.
|
||||
#
|
||||
# https://github.com/hbldh/bleak/blob/222618b7747f0467dbb32bd3679f8cfaa19b1668/bleak/backends/scanner.py#L203
|
||||
#
|
||||
device = prev_device
|
||||
device.name = local_name
|
||||
device.details = self._details | details
|
||||
# pylint: disable-next=protected-access
|
||||
device._rssi = rssi # deprecated, will be removed in newer bleak
|
||||
|
||||
advertisement_data = AdvertisementData(
|
||||
local_name=None if local_name == "" else local_name,
|
||||
manufacturer_data=manufacturer_data,
|
||||
service_data=service_data,
|
||||
service_uuids=service_uuids,
|
||||
tx_power=NO_RSSI_VALUE if tx_power is None else tx_power,
|
||||
rssi=rssi,
|
||||
platform_data=(),
|
||||
)
|
||||
self._discovered_device_advertisement_datas[address] = (
|
||||
device,
|
||||
advertisement_data,
|
||||
)
|
||||
self._discovered_device_timestamps[address] = advertisement_monotonic_time
|
||||
self._new_info_callback(
|
||||
BluetoothServiceInfoBleak(
|
||||
name=local_name or address,
|
||||
address=address,
|
||||
rssi=rssi,
|
||||
manufacturer_data=manufacturer_data,
|
||||
service_data=service_data,
|
||||
service_uuids=service_uuids,
|
||||
source=self.source,
|
||||
device=device,
|
||||
advertisement=advertisement_data,
|
||||
connectable=self.connectable,
|
||||
time=advertisement_monotonic_time,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_diagnostics(self) -> dict[str, Any]:
|
||||
"""Return diagnostic information about the scanner."""
|
||||
now = MONOTONIC_TIME()
|
||||
return await super().async_diagnostics() | {
|
||||
"connectable": self.connectable,
|
||||
"discovered_device_timestamps": self._discovered_device_timestamps,
|
||||
"time_since_last_device_detection": {
|
||||
address: now - timestamp
|
||||
for address, timestamp in self._discovered_device_timestamps.items()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class HomeAssistantRemoteScanner(BaseHaRemoteScanner):
|
||||
"""Home Assistant remote BLE scanner.
|
||||
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
"""Constants for the Bluetooth integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Final
|
||||
|
||||
from habluetooth import ( # noqa: F401
|
||||
CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
)
|
||||
|
||||
DOMAIN = "bluetooth"
|
||||
|
||||
CONF_ADAPTER = "adapter"
|
||||
|
@ -19,42 +25,6 @@ UNAVAILABLE_TRACK_SECONDS: Final = 60 * 5
|
|||
|
||||
START_TIMEOUT = 15
|
||||
|
||||
# The maximum time between advertisements for a device to be considered
|
||||
# stale when the advertisement tracker cannot determine the interval.
|
||||
#
|
||||
# We have to set this quite high as we don't know
|
||||
# when devices fall out of the ESPHome device (and other non-local scanners)'s
|
||||
# stack like we do with BlueZ so its safer to assume its available
|
||||
# since if it does go out of range and it is in range
|
||||
# of another device the timeout is much shorter and it will
|
||||
# switch over to using that adapter anyways.
|
||||
#
|
||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 60 * 15
|
||||
|
||||
# The maximum time between advertisements for a device to be considered
|
||||
# stale when the advertisement tracker can determine the interval for
|
||||
# connectable devices.
|
||||
#
|
||||
# BlueZ uses 180 seconds by default but we give it a bit more time
|
||||
# to account for the esp32's bluetooth stack being a bit slower
|
||||
# than BlueZ's.
|
||||
CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195
|
||||
|
||||
|
||||
# We must recover before we hit the 180s mark
|
||||
# where the device is removed from the stack
|
||||
# or the devices will go unavailable. Since
|
||||
# we only check every 30s, we need this number
|
||||
# to be
|
||||
# 180s Time when device is removed from stack
|
||||
# - 30s check interval
|
||||
# - 30s scanner restart time * 2
|
||||
#
|
||||
SCANNER_WATCHDOG_TIMEOUT: Final = 90
|
||||
# How often to check if the scanner has reached
|
||||
# the SCANNER_WATCHDOG_TIMEOUT without seeing anything
|
||||
SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
|
||||
# When the linux kernel is configured with
|
||||
# CONFIG_FW_LOADER_USER_HELPER_FALLBACK it
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"bluetooth-adapters==0.16.1",
|
||||
"bluetooth-auto-recovery==1.2.3",
|
||||
"bluetooth-data-tools==1.17.0",
|
||||
"dbus-fast==2.14.0"
|
||||
"dbus-fast==2.14.0",
|
||||
"habluetooth==0.1.0"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from bleak import BaseBleakClient
|
||||
from bluetooth_data_tools import monotonic_time_coarse
|
||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||
|
||||
|
@ -19,15 +17,6 @@ MANAGER: BluetoothManager | None = None
|
|||
MONOTONIC_TIME: Final = monotonic_time_coarse
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class HaBluetoothConnector:
|
||||
"""Data for how to connect a BLEDevice from a given scanner."""
|
||||
|
||||
client: type[BaseBleakClient]
|
||||
source: str
|
||||
can_connect: Callable[[], bool]
|
||||
|
||||
|
||||
class BluetoothScanningMode(Enum):
|
||||
"""The mode of scanning for bluetooth devices."""
|
||||
|
||||
|
|
|
@ -16,13 +16,14 @@ from bleak.backends.device import BLEDevice
|
|||
from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback
|
||||
from bleak_retry_connector import restore_discoveries
|
||||
from bluetooth_adapters import DEFAULT_ADDRESS
|
||||
from bluetooth_data_tools import monotonic_time_coarse as MONOTONIC_TIME
|
||||
from dbus_fast import InvalidMessageError
|
||||
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util.package import is_docker_env
|
||||
|
||||
from .base_scanner import MONOTONIC_TIME, BaseHaScanner
|
||||
from .base_scanner import BaseHaScanner
|
||||
from .const import (
|
||||
SCANNER_WATCHDOG_INTERVAL,
|
||||
SCANNER_WATCHDOG_TIMEOUT,
|
||||
|
|
|
@ -23,6 +23,7 @@ dbus-fast==2.14.0
|
|||
fnv-hash-fast==0.5.0
|
||||
ha-av==10.1.1
|
||||
ha-ffmpeg==3.1.0
|
||||
habluetooth==0.1.0
|
||||
hass-nabucasa==0.74.0
|
||||
hassil==1.5.1
|
||||
home-assistant-bluetooth==1.10.4
|
||||
|
|
|
@ -983,6 +983,9 @@ ha-philipsjs==3.1.1
|
|||
# homeassistant.components.habitica
|
||||
habitipy==0.2.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==0.1.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.74.0
|
||||
|
||||
|
|
|
@ -782,6 +782,9 @@ ha-philipsjs==3.1.1
|
|||
# homeassistant.components.habitica
|
||||
habitipy==0.2.0
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
habluetooth==0.1.0
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.74.0
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
|
|||
)
|
||||
switchbot_device_went_unavailable = False
|
||||
|
||||
scanner = FakeScanner(hass, "new", "fake_adapter")
|
||||
scanner = FakeScanner("new", "fake_adapter")
|
||||
cancel_scanner = async_register_scanner(hass, scanner, False)
|
||||
|
||||
@callback
|
||||
|
|
|
@ -215,7 +215,7 @@ async def test_remote_scanner_expires_connectable(
|
|||
seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=expire_monotonic,
|
||||
):
|
||||
async_fire_time_changed(hass, expire_utc)
|
||||
|
@ -298,7 +298,7 @@ async def test_remote_scanner_expires_non_connectable(
|
|||
seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=expire_monotonic,
|
||||
):
|
||||
async_fire_time_changed(hass, expire_utc)
|
||||
|
@ -314,7 +314,7 @@ async def test_remote_scanner_expires_non_connectable(
|
|||
seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=expire_monotonic,
|
||||
):
|
||||
async_fire_time_changed(hass, expire_utc)
|
||||
|
@ -515,7 +515,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
|||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=new_time,
|
||||
):
|
||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
||||
|
@ -528,7 +528,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
|||
for _ in range(1, 20):
|
||||
new_time += advertising_interval
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=new_time,
|
||||
):
|
||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
||||
|
@ -562,7 +562,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
|||
"homeassistant.components.bluetooth.manager.MONOTONIC_TIME",
|
||||
return_value=missed_advertisement_future_time,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=missed_advertisement_future_time,
|
||||
):
|
||||
# Fire once for the scanner to expire the device
|
||||
|
@ -629,7 +629,7 @@ async def test_scanner_stops_responding(
|
|||
)
|
||||
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=failure_reached_time,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -653,7 +653,7 @@ async def test_scanner_stops_responding(
|
|||
failure_reached_time += 1
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=failure_reached_time,
|
||||
):
|
||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
||||
|
|
|
@ -2815,7 +2815,7 @@ async def test_scanner_count_connectable(
|
|||
hass: HomeAssistant, enable_bluetooth: None
|
||||
) -> None:
|
||||
"""Test getting the connectable scanner count."""
|
||||
scanner = FakeScanner(hass, "any", "any")
|
||||
scanner = FakeScanner("any", "any")
|
||||
cancel = bluetooth.async_register_scanner(hass, scanner, False)
|
||||
assert bluetooth.async_scanner_count(hass, connectable=True) == 1
|
||||
cancel()
|
||||
|
@ -2823,7 +2823,7 @@ async def test_scanner_count_connectable(
|
|||
|
||||
async def test_scanner_count(hass: HomeAssistant, enable_bluetooth: None) -> None:
|
||||
"""Test getting the connectable and non-connectable scanner count."""
|
||||
scanner = FakeScanner(hass, "any", "any")
|
||||
scanner = FakeScanner("any", "any")
|
||||
cancel = bluetooth.async_register_scanner(hass, scanner, False)
|
||||
assert bluetooth.async_scanner_count(hass, connectable=False) == 2
|
||||
cancel()
|
||||
|
|
|
@ -107,7 +107,6 @@ async def test_wrapped_bleak_client_local_adapter_only(
|
|||
return None
|
||||
|
||||
scanner = FakeScanner(
|
||||
hass,
|
||||
"00:00:00:00:00:01",
|
||||
"hci0",
|
||||
)
|
||||
|
|
|
@ -228,7 +228,7 @@ async def test_recovery_from_dbus_restart(
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 10,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -238,7 +238,7 @@ async def test_recovery_from_dbus_restart(
|
|||
|
||||
# Fire a callback to reset the timer
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
):
|
||||
_callback(
|
||||
|
@ -248,7 +248,7 @@ async def test_recovery_from_dbus_restart(
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 20,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -258,7 +258,7 @@ async def test_recovery_from_dbus_restart(
|
|||
|
||||
# We hit the timer, so we restart the scanner
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + 20,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
|
@ -303,7 +303,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
|
|||
start_time_monotonic = time.monotonic()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
||||
|
@ -318,7 +318,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 10,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -328,7 +328,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 20,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -338,7 +338,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
|
|||
|
||||
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic
|
||||
+ SCANNER_WATCHDOG_TIMEOUT
|
||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||
|
@ -392,7 +392,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
|||
start_time_monotonic = time.monotonic()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
||||
|
@ -407,7 +407,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 10,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -417,7 +417,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
|||
|
||||
# Ensure we don't restart the scanner if we don't need to
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic + 20,
|
||||
):
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
||||
|
@ -427,7 +427,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
|||
|
||||
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic
|
||||
+ SCANNER_WATCHDOG_TIMEOUT
|
||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||
|
@ -443,7 +443,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
|||
# We hit the timer again the previous start call failed, make sure
|
||||
# we try again
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic
|
||||
+ SCANNER_WATCHDOG_TIMEOUT
|
||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||
|
@ -506,7 +506,7 @@ async def test_adapter_fails_to_start_and_takes_a_bit_to_init(
|
|||
"homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
|
||||
0,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
||||
|
@ -557,7 +557,7 @@ async def test_restart_takes_longer_than_watchdog_time(
|
|||
"homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
|
||||
0,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic,
|
||||
), patch(
|
||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
||||
|
@ -572,7 +572,7 @@ async def test_restart_takes_longer_than_watchdog_time(
|
|||
# Now force a recover adapter 2x
|
||||
for _ in range(2):
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
||||
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||
return_value=start_time_monotonic
|
||||
+ SCANNER_WATCHDOG_TIMEOUT
|
||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||
|
|
Loading…
Reference in New Issue