diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 5948d4d135817a249a167960f75753ab0967f8d6..a0eb263757a7ac5781497ee75c8ae652c479cb30 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -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 diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index f7696c2e90b3eb32eebdca286496bee769574f7e..8267a73fd71e3d04b85f80a304d54bb4d9784812 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -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. diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index 150239eec021eaab7b655d60aab119ab283a73ff..fa8efabcb1d7620680b3873170c165f9f5b833f9 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -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 diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index bb052a3a042343b18eb4471e4a164a931b89f7f4..74cec5edfe20886129b6eb6a069f31642a9dc768 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -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" ] } diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index 1b8c12c6eb3f994f00546031c82bbf15d0fbd29c..48ba021cd6c2d559a0391d8a05f83258897d9be5 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -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.""" diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py index 712fe1c0d9ab28b63b1e27a2697f232cb7566551..203bdac68a6c4f65b43c5fe4a35a24f53c01d3ff 100644 --- a/homeassistant/components/bluetooth/scanner.py +++ b/homeassistant/components/bluetooth/scanner.py @@ -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, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 06892125940ddd46daa9d6f2a1d8e2d7eecd2b22..f7f67dcbf7adfbcadde46b4e7d6d615754cc5c99 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -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 diff --git a/requirements_all.txt b/requirements_all.txt index 94895a49444c26f70640fc06c4197ba62e63ef61..640c050e05fa9f60d47bd5221f8cdec323187c93 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e3424447c2fec8be37654fa37735cf7a5a4aef83..608a53b46f66d3f7393465a122ee84dfa64b9a95 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/bluetooth/test_advertisement_tracker.py b/tests/components/bluetooth/test_advertisement_tracker.py index f04ea2873f01c0136477b4124f9a7e20a04d3f96..6ae847ba84a3133be7858a3390ffd3c2abf08ab0 100644 --- a/tests/components/bluetooth/test_advertisement_tracker.py +++ b/tests/components/bluetooth/test_advertisement_tracker.py @@ -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 diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py index a39f18e037ec047121da45e3534bfb786dc75ef8..5886cc10aac17aa805f00dff4f8ca31258573b81 100644 --- a/tests/components/bluetooth/test_base_scanner.py +++ b/tests/components/bluetooth/test_base_scanner.py @@ -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) diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 21fade843f54c5bc35fa17f4b20adba62e0b504e..b24bb97e1e384a63f97cabb2a7086e0124fb067a 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -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() diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py index 1d07ab75a485ca540733ffad569860e7e58c098f..8cffbe685b6590709b02c189b097704e5d2c8bcb 100644 --- a/tests/components/bluetooth/test_models.py +++ b/tests/components/bluetooth/test_models.py @@ -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", ) diff --git a/tests/components/bluetooth/test_scanner.py b/tests/components/bluetooth/test_scanner.py index bc32a5b302d4831adc1014671921a78e67c05ad2..b660be74aa91fe0f6ac522eec01b893cf77ad7d9 100644 --- a/tests/components/bluetooth/test_scanner.py +++ b/tests/components/bluetooth/test_scanner.py @@ -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(),