Relocate base Bluetooth scanner code into an external library (#104930)
This commit is contained in:
parent
c8bb72935d
commit
28584ad240
14 changed files with 51 additions and 460 deletions
|
@ -21,6 +21,7 @@ from bluetooth_adapters import (
|
||||||
adapter_unique_name,
|
adapter_unique_name,
|
||||||
get_adapters,
|
get_adapters,
|
||||||
)
|
)
|
||||||
|
from habluetooth import HaBluetoothConnector
|
||||||
from home_assistant_bluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak
|
from home_assistant_bluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak
|
||||||
|
|
||||||
from homeassistant.components import usb
|
from homeassistant.components import usb
|
||||||
|
@ -77,12 +78,7 @@ from .const import (
|
||||||
)
|
)
|
||||||
from .manager import BluetoothManager
|
from .manager import BluetoothManager
|
||||||
from .match import BluetoothCallbackMatcher, IntegrationMatcher
|
from .match import BluetoothCallbackMatcher, IntegrationMatcher
|
||||||
from .models import (
|
from .models import BluetoothCallback, BluetoothChange, BluetoothScanningMode
|
||||||
BluetoothCallback,
|
|
||||||
BluetoothChange,
|
|
||||||
BluetoothScanningMode,
|
|
||||||
HaBluetoothConnector,
|
|
||||||
)
|
|
||||||
from .scanner import MONOTONIC_TIME, HaScanner, ScannerStartError
|
from .scanner import MONOTONIC_TIME, HaScanner, ScannerStartError
|
||||||
from .storage import BluetoothStorage
|
from .storage import BluetoothStorage
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
"""Base classes for HA Bluetooth scanners for bluetooth."""
|
"""Base classes for HA Bluetooth scanners for bluetooth."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from abc import abstractmethod
|
from collections.abc import Callable
|
||||||
import asyncio
|
|
||||||
from collections.abc import Callable, Generator
|
|
||||||
from contextlib import contextmanager
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
from typing import Any
|
||||||
from typing import Any, Final, final
|
|
||||||
|
|
||||||
from bleak.backends.device import BLEDevice
|
from bleak.backends.device import BLEDevice
|
||||||
from bleak.backends.scanner import AdvertisementData
|
from bleak.backends.scanner import AdvertisementData
|
||||||
from bleak_retry_connector import NO_RSSI_VALUE
|
from bluetooth_adapters import DiscoveredDeviceAdvertisementData
|
||||||
from bluetooth_adapters import DiscoveredDeviceAdvertisementData, adapter_human_name
|
from habluetooth import BaseHaRemoteScanner, BaseHaScanner, HaBluetoothConnector
|
||||||
from bluetooth_data_tools import monotonic_time_coarse
|
|
||||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||||
|
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
|
@ -25,16 +20,6 @@ from homeassistant.core import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import models
|
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)
|
@dataclass(slots=True)
|
||||||
|
@ -46,363 +31,6 @@ class BluetoothScannerDevice:
|
||||||
advertisement: AdvertisementData
|
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):
|
class HomeAssistantRemoteScanner(BaseHaRemoteScanner):
|
||||||
"""Home Assistant remote BLE scanner.
|
"""Home Assistant remote BLE scanner.
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
"""Constants for the Bluetooth integration."""
|
"""Constants for the Bluetooth integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
from typing import Final
|
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"
|
DOMAIN = "bluetooth"
|
||||||
|
|
||||||
CONF_ADAPTER = "adapter"
|
CONF_ADAPTER = "adapter"
|
||||||
|
@ -19,42 +25,6 @@ UNAVAILABLE_TRACK_SECONDS: Final = 60 * 5
|
||||||
|
|
||||||
START_TIMEOUT = 15
|
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
|
# When the linux kernel is configured with
|
||||||
# CONFIG_FW_LOADER_USER_HELPER_FALLBACK it
|
# CONFIG_FW_LOADER_USER_HELPER_FALLBACK it
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"bluetooth-adapters==0.16.1",
|
"bluetooth-adapters==0.16.1",
|
||||||
"bluetooth-auto-recovery==1.2.3",
|
"bluetooth-auto-recovery==1.2.3",
|
||||||
"bluetooth-data-tools==1.17.0",
|
"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 __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, Final
|
from typing import TYPE_CHECKING, Final
|
||||||
|
|
||||||
from bleak import BaseBleakClient
|
|
||||||
from bluetooth_data_tools import monotonic_time_coarse
|
from bluetooth_data_tools import monotonic_time_coarse
|
||||||
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||||
|
|
||||||
|
@ -19,15 +17,6 @@ MANAGER: BluetoothManager | None = None
|
||||||
MONOTONIC_TIME: Final = monotonic_time_coarse
|
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):
|
class BluetoothScanningMode(Enum):
|
||||||
"""The mode of scanning for bluetooth devices."""
|
"""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.backends.scanner import AdvertisementData, AdvertisementDataCallback
|
||||||
from bleak_retry_connector import restore_discoveries
|
from bleak_retry_connector import restore_discoveries
|
||||||
from bluetooth_adapters import DEFAULT_ADDRESS
|
from bluetooth_adapters import DEFAULT_ADDRESS
|
||||||
|
from bluetooth_data_tools import monotonic_time_coarse as MONOTONIC_TIME
|
||||||
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
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.util.package import is_docker_env
|
from homeassistant.util.package import is_docker_env
|
||||||
|
|
||||||
from .base_scanner import MONOTONIC_TIME, BaseHaScanner
|
from .base_scanner import BaseHaScanner
|
||||||
from .const import (
|
from .const import (
|
||||||
SCANNER_WATCHDOG_INTERVAL,
|
SCANNER_WATCHDOG_INTERVAL,
|
||||||
SCANNER_WATCHDOG_TIMEOUT,
|
SCANNER_WATCHDOG_TIMEOUT,
|
||||||
|
|
|
@ -23,6 +23,7 @@ dbus-fast==2.14.0
|
||||||
fnv-hash-fast==0.5.0
|
fnv-hash-fast==0.5.0
|
||||||
ha-av==10.1.1
|
ha-av==10.1.1
|
||||||
ha-ffmpeg==3.1.0
|
ha-ffmpeg==3.1.0
|
||||||
|
habluetooth==0.1.0
|
||||||
hass-nabucasa==0.74.0
|
hass-nabucasa==0.74.0
|
||||||
hassil==1.5.1
|
hassil==1.5.1
|
||||||
home-assistant-bluetooth==1.10.4
|
home-assistant-bluetooth==1.10.4
|
||||||
|
|
|
@ -983,6 +983,9 @@ ha-philipsjs==3.1.1
|
||||||
# homeassistant.components.habitica
|
# homeassistant.components.habitica
|
||||||
habitipy==0.2.0
|
habitipy==0.2.0
|
||||||
|
|
||||||
|
# homeassistant.components.bluetooth
|
||||||
|
habluetooth==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.cloud
|
# homeassistant.components.cloud
|
||||||
hass-nabucasa==0.74.0
|
hass-nabucasa==0.74.0
|
||||||
|
|
||||||
|
|
|
@ -782,6 +782,9 @@ ha-philipsjs==3.1.1
|
||||||
# homeassistant.components.habitica
|
# homeassistant.components.habitica
|
||||||
habitipy==0.2.0
|
habitipy==0.2.0
|
||||||
|
|
||||||
|
# homeassistant.components.bluetooth
|
||||||
|
habluetooth==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.cloud
|
# homeassistant.components.cloud
|
||||||
hass-nabucasa==0.74.0
|
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
|
switchbot_device_went_unavailable = False
|
||||||
|
|
||||||
scanner = FakeScanner(hass, "new", "fake_adapter")
|
scanner = FakeScanner("new", "fake_adapter")
|
||||||
cancel_scanner = async_register_scanner(hass, scanner, False)
|
cancel_scanner = async_register_scanner(hass, scanner, False)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -215,7 +215,7 @@ async def test_remote_scanner_expires_connectable(
|
||||||
seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||||
)
|
)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=expire_monotonic,
|
return_value=expire_monotonic,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, expire_utc)
|
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
|
seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||||
)
|
)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=expire_monotonic,
|
return_value=expire_monotonic,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, expire_utc)
|
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
|
seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
||||||
)
|
)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=expire_monotonic,
|
return_value=expire_monotonic,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, expire_utc)
|
async_fire_time_changed(hass, expire_utc)
|
||||||
|
@ -515,7 +515,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=new_time,
|
return_value=new_time,
|
||||||
):
|
):
|
||||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
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):
|
for _ in range(1, 20):
|
||||||
new_time += advertising_interval
|
new_time += advertising_interval
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=new_time,
|
return_value=new_time,
|
||||||
):
|
):
|
||||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
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",
|
"homeassistant.components.bluetooth.manager.MONOTONIC_TIME",
|
||||||
return_value=missed_advertisement_future_time,
|
return_value=missed_advertisement_future_time,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=missed_advertisement_future_time,
|
return_value=missed_advertisement_future_time,
|
||||||
):
|
):
|
||||||
# Fire once for the scanner to expire the device
|
# 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
|
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=failure_reached_time,
|
return_value=failure_reached_time,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
failure_reached_time += 1
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=failure_reached_time,
|
return_value=failure_reached_time,
|
||||||
):
|
):
|
||||||
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
|
||||||
|
|
|
@ -2815,7 +2815,7 @@ async def test_scanner_count_connectable(
|
||||||
hass: HomeAssistant, enable_bluetooth: None
|
hass: HomeAssistant, enable_bluetooth: None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test getting the connectable scanner count."""
|
"""Test getting the connectable scanner count."""
|
||||||
scanner = FakeScanner(hass, "any", "any")
|
scanner = FakeScanner("any", "any")
|
||||||
cancel = bluetooth.async_register_scanner(hass, scanner, False)
|
cancel = bluetooth.async_register_scanner(hass, scanner, False)
|
||||||
assert bluetooth.async_scanner_count(hass, connectable=True) == 1
|
assert bluetooth.async_scanner_count(hass, connectable=True) == 1
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -2823,7 +2823,7 @@ async def test_scanner_count_connectable(
|
||||||
|
|
||||||
async def test_scanner_count(hass: HomeAssistant, enable_bluetooth: None) -> None:
|
async def test_scanner_count(hass: HomeAssistant, enable_bluetooth: None) -> None:
|
||||||
"""Test getting the connectable and non-connectable scanner count."""
|
"""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)
|
cancel = bluetooth.async_register_scanner(hass, scanner, False)
|
||||||
assert bluetooth.async_scanner_count(hass, connectable=False) == 2
|
assert bluetooth.async_scanner_count(hass, connectable=False) == 2
|
||||||
cancel()
|
cancel()
|
||||||
|
|
|
@ -107,7 +107,6 @@ async def test_wrapped_bleak_client_local_adapter_only(
|
||||||
return None
|
return None
|
||||||
|
|
||||||
scanner = FakeScanner(
|
scanner = FakeScanner(
|
||||||
hass,
|
|
||||||
"00:00:00:00:00:01",
|
"00:00:00:00:00:01",
|
||||||
"hci0",
|
"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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 10,
|
return_value=start_time_monotonic + 10,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# Fire a callback to reset the timer
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic,
|
return_value=start_time_monotonic,
|
||||||
):
|
):
|
||||||
_callback(
|
_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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 20,
|
return_value=start_time_monotonic + 20,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# We hit the timer, so we restart the scanner
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + 20,
|
return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + 20,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
|
@ -303,7 +303,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
|
||||||
start_time_monotonic = time.monotonic()
|
start_time_monotonic = time.monotonic()
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic,
|
return_value=start_time_monotonic,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
"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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 10,
|
return_value=start_time_monotonic + 10,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 20,
|
return_value=start_time_monotonic + 20,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic
|
return_value=start_time_monotonic
|
||||||
+ SCANNER_WATCHDOG_TIMEOUT
|
+ SCANNER_WATCHDOG_TIMEOUT
|
||||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||||
|
@ -392,7 +392,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
|
||||||
start_time_monotonic = time.monotonic()
|
start_time_monotonic = time.monotonic()
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic,
|
return_value=start_time_monotonic,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
"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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 10,
|
return_value=start_time_monotonic + 10,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# Ensure we don't restart the scanner if we don't need to
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic + 20,
|
return_value=start_time_monotonic + 20,
|
||||||
):
|
):
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
|
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
|
# We hit the timer with no detections, so we reset the adapter and restart the scanner
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic
|
return_value=start_time_monotonic
|
||||||
+ SCANNER_WATCHDOG_TIMEOUT
|
+ SCANNER_WATCHDOG_TIMEOUT
|
||||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
+ 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 hit the timer again the previous start call failed, make sure
|
||||||
# we try again
|
# we try again
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic
|
return_value=start_time_monotonic
|
||||||
+ SCANNER_WATCHDOG_TIMEOUT
|
+ SCANNER_WATCHDOG_TIMEOUT
|
||||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
+ 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",
|
"homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
|
||||||
0,
|
0,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic,
|
return_value=start_time_monotonic,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
"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",
|
"homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
|
||||||
0,
|
0,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic,
|
return_value=start_time_monotonic,
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
"homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
|
||||||
|
@ -572,7 +572,7 @@ async def test_restart_takes_longer_than_watchdog_time(
|
||||||
# Now force a recover adapter 2x
|
# Now force a recover adapter 2x
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
|
"habluetooth.base_scanner.MONOTONIC_TIME",
|
||||||
return_value=start_time_monotonic
|
return_value=start_time_monotonic
|
||||||
+ SCANNER_WATCHDOG_TIMEOUT
|
+ SCANNER_WATCHDOG_TIMEOUT
|
||||||
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
+ SCANNER_WATCHDOG_INTERVAL.total_seconds(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue