mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Refactor bluetooth scanners for better seperation of concerns (#104909)
This commit is contained in:
parent
7a9c3819e0
commit
dd9c22672a
@ -59,7 +59,11 @@ from .api import (
|
|||||||
async_set_fallback_availability_interval,
|
async_set_fallback_availability_interval,
|
||||||
async_track_unavailable,
|
async_track_unavailable,
|
||||||
)
|
)
|
||||||
from .base_scanner import BaseHaRemoteScanner, BaseHaScanner, BluetoothScannerDevice
|
from .base_scanner import (
|
||||||
|
BaseHaScanner,
|
||||||
|
BluetoothScannerDevice,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
|
)
|
||||||
from .const import (
|
from .const import (
|
||||||
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
|
||||||
CONF_ADAPTER,
|
CONF_ADAPTER,
|
||||||
@ -103,7 +107,7 @@ __all__ = [
|
|||||||
"async_scanner_count",
|
"async_scanner_count",
|
||||||
"async_scanner_devices_by_address",
|
"async_scanner_devices_by_address",
|
||||||
"BaseHaScanner",
|
"BaseHaScanner",
|
||||||
"BaseHaRemoteScanner",
|
"HomeAssistantRemoteScanner",
|
||||||
"BluetoothCallbackMatcher",
|
"BluetoothCallbackMatcher",
|
||||||
"BluetoothChange",
|
"BluetoothChange",
|
||||||
"BluetoothServiceInfo",
|
"BluetoothServiceInfo",
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
"""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 ABC, abstractmethod
|
from abc import abstractmethod
|
||||||
|
import asyncio
|
||||||
from collections.abc import Callable, Generator
|
from collections.abc import Callable, Generator
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import datetime
|
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final
|
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
|
||||||
@ -24,8 +23,6 @@ from homeassistant.core import (
|
|||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
callback as hass_callback,
|
callback as hass_callback,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
|
||||||
import homeassistant.util.dt as dt_util
|
|
||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -35,6 +32,7 @@ from .const import (
|
|||||||
)
|
)
|
||||||
from .models import HaBluetoothConnector
|
from .models import HaBluetoothConnector
|
||||||
|
|
||||||
|
SCANNER_WATCHDOG_INTERVAL_SECONDS: Final = SCANNER_WATCHDOG_INTERVAL.total_seconds()
|
||||||
MONOTONIC_TIME: Final = monotonic_time_coarse
|
MONOTONIC_TIME: Final = monotonic_time_coarse
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -48,11 +46,10 @@ class BluetoothScannerDevice:
|
|||||||
advertisement: AdvertisementData
|
advertisement: AdvertisementData
|
||||||
|
|
||||||
|
|
||||||
class BaseHaScanner(ABC):
|
class BaseHaScanner:
|
||||||
"""Base class for Ha Scanners."""
|
"""Base class for high availability BLE scanners."""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"hass",
|
|
||||||
"adapter",
|
"adapter",
|
||||||
"connectable",
|
"connectable",
|
||||||
"source",
|
"source",
|
||||||
@ -63,17 +60,16 @@ class BaseHaScanner(ABC):
|
|||||||
"_last_detection",
|
"_last_detection",
|
||||||
"_start_time",
|
"_start_time",
|
||||||
"_cancel_watchdog",
|
"_cancel_watchdog",
|
||||||
|
"_loop",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
|
||||||
source: str,
|
source: str,
|
||||||
adapter: str,
|
adapter: str,
|
||||||
connector: HaBluetoothConnector | None = None,
|
connector: HaBluetoothConnector | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the scanner."""
|
"""Initialize the scanner."""
|
||||||
self.hass = hass
|
|
||||||
self.connectable = False
|
self.connectable = False
|
||||||
self.source = source
|
self.source = source
|
||||||
self.connector = connector
|
self.connector = connector
|
||||||
@ -83,13 +79,20 @@ class BaseHaScanner(ABC):
|
|||||||
self.scanning = True
|
self.scanning = True
|
||||||
self._last_detection = 0.0
|
self._last_detection = 0.0
|
||||||
self._start_time = 0.0
|
self._start_time = 0.0
|
||||||
self._cancel_watchdog: CALLBACK_TYPE | None = None
|
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
|
@hass_callback
|
||||||
def _async_stop_scanner_watchdog(self) -> None:
|
def _async_stop_scanner_watchdog(self) -> None:
|
||||||
"""Stop the scanner watchdog."""
|
"""Stop the scanner watchdog."""
|
||||||
if self._cancel_watchdog:
|
if self._cancel_watchdog:
|
||||||
self._cancel_watchdog()
|
self._cancel_watchdog.cancel()
|
||||||
self._cancel_watchdog = None
|
self._cancel_watchdog = None
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
@ -97,12 +100,22 @@ class BaseHaScanner(ABC):
|
|||||||
"""If something has restarted or updated, we need to restart the scanner."""
|
"""If something has restarted or updated, we need to restart the scanner."""
|
||||||
self._start_time = self._last_detection = MONOTONIC_TIME()
|
self._start_time = self._last_detection = MONOTONIC_TIME()
|
||||||
if not self._cancel_watchdog:
|
if not self._cancel_watchdog:
|
||||||
self._cancel_watchdog = async_track_time_interval(
|
self._schedule_watchdog()
|
||||||
self.hass,
|
|
||||||
self._async_scanner_watchdog,
|
def _schedule_watchdog(self) -> None:
|
||||||
SCANNER_WATCHDOG_INTERVAL,
|
"""Schedule the watchdog."""
|
||||||
name=f"{self.name} Bluetooth scanner 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
|
@hass_callback
|
||||||
def _async_watchdog_triggered(self) -> bool:
|
def _async_watchdog_triggered(self) -> bool:
|
||||||
@ -116,7 +129,7 @@ class BaseHaScanner(ABC):
|
|||||||
return time_since_last_detection > SCANNER_WATCHDOG_TIMEOUT
|
return time_since_last_detection > SCANNER_WATCHDOG_TIMEOUT
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def _async_scanner_watchdog(self, now: datetime.datetime) -> None:
|
def _async_scanner_watchdog(self) -> None:
|
||||||
"""Check if the scanner is running.
|
"""Check if the scanner is running.
|
||||||
|
|
||||||
Override this method if you need to do something else when the watchdog
|
Override this method if you need to do something else when the watchdog
|
||||||
@ -135,6 +148,10 @@ class BaseHaScanner(ABC):
|
|||||||
return
|
return
|
||||||
self.scanning = not self._connecting
|
self.scanning = not self._connecting
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _unsetup(self) -> None:
|
||||||
|
"""Unset up the scanner."""
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def connecting(self) -> Generator[None, None, None]:
|
def connecting(self) -> Generator[None, None, None]:
|
||||||
"""Context manager to track connecting state."""
|
"""Context manager to track connecting state."""
|
||||||
@ -183,7 +200,7 @@ class BaseHaScanner(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class BaseHaRemoteScanner(BaseHaScanner):
|
class BaseHaRemoteScanner(BaseHaScanner):
|
||||||
"""Base class for a Home Assistant remote BLE scanner."""
|
"""Base class for a high availability remote BLE scanner."""
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
"_new_info_callback",
|
"_new_info_callback",
|
||||||
@ -191,12 +208,11 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
"_discovered_device_timestamps",
|
"_discovered_device_timestamps",
|
||||||
"_details",
|
"_details",
|
||||||
"_expire_seconds",
|
"_expire_seconds",
|
||||||
"_storage",
|
"_cancel_track",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
|
||||||
scanner_id: str,
|
scanner_id: str,
|
||||||
name: str,
|
name: str,
|
||||||
new_info_callback: Callable[[BluetoothServiceInfoBleak], None],
|
new_info_callback: Callable[[BluetoothServiceInfoBleak], None],
|
||||||
@ -204,7 +220,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
connectable: bool,
|
connectable: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the scanner."""
|
"""Initialize the scanner."""
|
||||||
super().__init__(hass, scanner_id, name, connector)
|
super().__init__(scanner_id, name, connector)
|
||||||
self._new_info_callback = new_info_callback
|
self._new_info_callback = new_info_callback
|
||||||
self._discovered_device_advertisement_datas: dict[
|
self._discovered_device_advertisement_datas: dict[
|
||||||
str, tuple[BLEDevice, AdvertisementData]
|
str, tuple[BLEDevice, AdvertisementData]
|
||||||
@ -215,55 +231,37 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
# Scanners only care about connectable devices. The manager
|
# Scanners only care about connectable devices. The manager
|
||||||
# will handle taking care of availability for non-connectable devices
|
# will handle taking care of availability for non-connectable devices
|
||||||
self._expire_seconds = CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
|
self._expire_seconds = CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
|
||||||
assert models.MANAGER is not None
|
self._cancel_track: asyncio.TimerHandle | None = None
|
||||||
self._storage = models.MANAGER.storage
|
|
||||||
|
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
|
@hass_callback
|
||||||
def async_setup(self) -> CALLBACK_TYPE:
|
def async_setup(self) -> CALLBACK_TYPE:
|
||||||
"""Set up the scanner."""
|
"""Set up the scanner."""
|
||||||
if history := self._storage.async_get_advertisement_history(self.source):
|
super().async_setup()
|
||||||
self._discovered_device_advertisement_datas = (
|
self._schedule_expire_devices()
|
||||||
history.discovered_device_advertisement_datas
|
|
||||||
)
|
|
||||||
self._discovered_device_timestamps = history.discovered_device_timestamps
|
|
||||||
# Expire anything that is too old
|
|
||||||
self._async_expire_devices(dt_util.utcnow())
|
|
||||||
|
|
||||||
cancel_track = async_track_time_interval(
|
|
||||||
self.hass,
|
|
||||||
self._async_expire_devices,
|
|
||||||
timedelta(seconds=30),
|
|
||||||
name=f"{self.name} Bluetooth scanner device expire",
|
|
||||||
)
|
|
||||||
cancel_stop = self.hass.bus.async_listen(
|
|
||||||
EVENT_HOMEASSISTANT_STOP, self._async_save_history
|
|
||||||
)
|
|
||||||
self._async_setup_scanner_watchdog()
|
self._async_setup_scanner_watchdog()
|
||||||
|
return self._unsetup
|
||||||
|
|
||||||
@hass_callback
|
def _schedule_expire_devices(self) -> None:
|
||||||
def _cancel() -> None:
|
"""Schedule the expiration of old devices."""
|
||||||
self._async_save_history()
|
loop = self._loop
|
||||||
self._async_stop_scanner_watchdog()
|
assert loop is not None
|
||||||
cancel_track()
|
self._cancel_expire_devices()
|
||||||
cancel_stop()
|
self._cancel_track = loop.call_at(loop.time() + 30, self._async_expire_devices)
|
||||||
|
|
||||||
return _cancel
|
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def _async_save_history(self, event: Event | None = None) -> None:
|
def _async_expire_devices(self) -> None:
|
||||||
"""Save the history."""
|
|
||||||
self._storage.async_set_advertisement_history(
|
|
||||||
self.source,
|
|
||||||
DiscoveredDeviceAdvertisementData(
|
|
||||||
self.connectable,
|
|
||||||
self._expire_seconds,
|
|
||||||
self._discovered_device_advertisement_datas,
|
|
||||||
self._discovered_device_timestamps,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@hass_callback
|
|
||||||
def _async_expire_devices(self, _datetime: datetime.datetime) -> None:
|
|
||||||
"""Expire old devices."""
|
"""Expire old devices."""
|
||||||
now = MONOTONIC_TIME()
|
now = MONOTONIC_TIME()
|
||||||
expired = [
|
expired = [
|
||||||
@ -274,6 +272,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
for address in expired:
|
for address in expired:
|
||||||
del self._discovered_device_advertisement_datas[address]
|
del self._discovered_device_advertisement_datas[address]
|
||||||
del self._discovered_device_timestamps[address]
|
del self._discovered_device_timestamps[address]
|
||||||
|
self._schedule_expire_devices()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
@ -395,9 +394,6 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
"""Return diagnostic information about the scanner."""
|
"""Return diagnostic information about the scanner."""
|
||||||
now = MONOTONIC_TIME()
|
now = MONOTONIC_TIME()
|
||||||
return await super().async_diagnostics() | {
|
return await super().async_diagnostics() | {
|
||||||
"storage": self._storage.async_get_advertisement_history_as_dict(
|
|
||||||
self.source
|
|
||||||
),
|
|
||||||
"connectable": self.connectable,
|
"connectable": self.connectable,
|
||||||
"discovered_device_timestamps": self._discovered_device_timestamps,
|
"discovered_device_timestamps": self._discovered_device_timestamps,
|
||||||
"time_since_last_device_detection": {
|
"time_since_last_device_detection": {
|
||||||
@ -405,3 +401,79 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||||||
for address, timestamp in self._discovered_device_timestamps.items()
|
for address, timestamp in self._discovered_device_timestamps.items()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class HomeAssistantRemoteScanner(BaseHaRemoteScanner):
|
||||||
|
"""Home Assistant remote BLE scanner.
|
||||||
|
|
||||||
|
This is the only object that should know about
|
||||||
|
the hass object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"hass",
|
||||||
|
"_storage",
|
||||||
|
"_cancel_stop",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
scanner_id: str,
|
||||||
|
name: str,
|
||||||
|
new_info_callback: Callable[[BluetoothServiceInfoBleak], None],
|
||||||
|
connector: HaBluetoothConnector | None,
|
||||||
|
connectable: bool,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the scanner."""
|
||||||
|
self.hass = hass
|
||||||
|
assert models.MANAGER is not None
|
||||||
|
self._storage = models.MANAGER.storage
|
||||||
|
self._cancel_stop: CALLBACK_TYPE | None = None
|
||||||
|
super().__init__(scanner_id, name, new_info_callback, connector, connectable)
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def async_setup(self) -> CALLBACK_TYPE:
|
||||||
|
"""Set up the scanner."""
|
||||||
|
super().async_setup()
|
||||||
|
if history := self._storage.async_get_advertisement_history(self.source):
|
||||||
|
self._discovered_device_advertisement_datas = (
|
||||||
|
history.discovered_device_advertisement_datas
|
||||||
|
)
|
||||||
|
self._discovered_device_timestamps = history.discovered_device_timestamps
|
||||||
|
# Expire anything that is too old
|
||||||
|
self._async_expire_devices()
|
||||||
|
|
||||||
|
self._cancel_stop = self.hass.bus.async_listen(
|
||||||
|
EVENT_HOMEASSISTANT_STOP, self._async_save_history
|
||||||
|
)
|
||||||
|
return self._unsetup
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _unsetup(self) -> None:
|
||||||
|
super()._unsetup()
|
||||||
|
self._async_save_history()
|
||||||
|
if self._cancel_stop:
|
||||||
|
self._cancel_stop()
|
||||||
|
self._cancel_stop = None
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _async_save_history(self, event: Event | None = None) -> None:
|
||||||
|
"""Save the history."""
|
||||||
|
self._storage.async_set_advertisement_history(
|
||||||
|
self.source,
|
||||||
|
DiscoveredDeviceAdvertisementData(
|
||||||
|
self.connectable,
|
||||||
|
self._expire_seconds,
|
||||||
|
self._discovered_device_advertisement_datas,
|
||||||
|
self._discovered_device_timestamps,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_diagnostics(self) -> dict[str, Any]:
|
||||||
|
"""Return diagnostic information about the scanner."""
|
||||||
|
diag = await super().async_diagnostics()
|
||||||
|
diag["storage"] = self._storage.async_get_advertisement_history_as_dict(
|
||||||
|
self.source
|
||||||
|
)
|
||||||
|
return diag
|
||||||
|
@ -3,7 +3,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -19,7 +18,7 @@ from bleak_retry_connector import restore_discoveries
|
|||||||
from bluetooth_adapters import DEFAULT_ADDRESS
|
from bluetooth_adapters import DEFAULT_ADDRESS
|
||||||
from dbus_fast import InvalidMessageError
|
from dbus_fast import InvalidMessageError
|
||||||
|
|
||||||
from homeassistant.core import 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
|
||||||
|
|
||||||
@ -133,12 +132,13 @@ class HaScanner(BaseHaScanner):
|
|||||||
"""Init bluetooth discovery."""
|
"""Init bluetooth discovery."""
|
||||||
self.mac_address = address
|
self.mac_address = address
|
||||||
source = address if address != DEFAULT_ADDRESS else adapter or SOURCE_LOCAL
|
source = address if address != DEFAULT_ADDRESS else adapter or SOURCE_LOCAL
|
||||||
super().__init__(hass, source, adapter)
|
super().__init__(source, adapter)
|
||||||
self.connectable = True
|
self.connectable = True
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self._start_stop_lock = asyncio.Lock()
|
self._start_stop_lock = asyncio.Lock()
|
||||||
self._new_info_callback = new_info_callback
|
self._new_info_callback = new_info_callback
|
||||||
self.scanning = False
|
self.scanning = False
|
||||||
|
self.hass = hass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
@ -153,11 +153,13 @@ class HaScanner(BaseHaScanner):
|
|||||||
return self.scanner.discovered_devices_and_advertisement_data
|
return self.scanner.discovered_devices_and_advertisement_data
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def async_setup(self) -> None:
|
def async_setup(self) -> CALLBACK_TYPE:
|
||||||
"""Set up the scanner."""
|
"""Set up the scanner."""
|
||||||
|
super().async_setup()
|
||||||
self.scanner = create_bleak_scanner(
|
self.scanner = create_bleak_scanner(
|
||||||
self._async_detection_callback, self.mode, self.adapter
|
self._async_detection_callback, self.mode, self.adapter
|
||||||
)
|
)
|
||||||
|
return self._unsetup
|
||||||
|
|
||||||
async def async_diagnostics(self) -> dict[str, Any]:
|
async def async_diagnostics(self) -> dict[str, Any]:
|
||||||
"""Return diagnostic information about the scanner."""
|
"""Return diagnostic information about the scanner."""
|
||||||
@ -314,7 +316,7 @@ class HaScanner(BaseHaScanner):
|
|||||||
await restore_discoveries(self.scanner, self.adapter)
|
await restore_discoveries(self.scanner, self.adapter)
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def _async_scanner_watchdog(self, now: datetime) -> None:
|
def _async_scanner_watchdog(self) -> None:
|
||||||
"""Check if the scanner is running."""
|
"""Check if the scanner is running."""
|
||||||
if not self._async_watchdog_triggered():
|
if not self._async_watchdog_triggered():
|
||||||
return
|
return
|
||||||
|
@ -7,11 +7,14 @@ from bluetooth_data_tools import (
|
|||||||
parse_advertisement_data_tuple,
|
parse_advertisement_data_tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import MONOTONIC_TIME, BaseHaRemoteScanner
|
from homeassistant.components.bluetooth import (
|
||||||
|
MONOTONIC_TIME,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
|
||||||
class ESPHomeScanner(BaseHaRemoteScanner):
|
class ESPHomeScanner(HomeAssistantRemoteScanner):
|
||||||
"""Scanner for esphome."""
|
"""Scanner for esphome."""
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
@ -10,7 +10,7 @@ from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
|||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
HomeAssistantRemoteScanner,
|
||||||
async_get_advertisement_callback,
|
async_get_advertisement_callback,
|
||||||
async_register_scanner,
|
async_register_scanner,
|
||||||
)
|
)
|
||||||
@ -22,7 +22,7 @@ from .coordinator import RuuviGatewayUpdateCoordinator
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class RuuviGatewayScanner(BaseHaRemoteScanner):
|
class RuuviGatewayScanner(HomeAssistantRemoteScanner):
|
||||||
"""Scanner for Ruuvi Gateway."""
|
"""Scanner for Ruuvi Gateway."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -6,13 +6,16 @@ from typing import Any
|
|||||||
from aioshelly.ble import parse_ble_scan_result_event
|
from aioshelly.ble import parse_ble_scan_result_event
|
||||||
from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT, BLE_SCAN_RESULT_VERSION
|
from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT, BLE_SCAN_RESULT_VERSION
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import MONOTONIC_TIME, BaseHaRemoteScanner
|
from homeassistant.components.bluetooth import (
|
||||||
|
MONOTONIC_TIME,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from ..const import LOGGER
|
from ..const import LOGGER
|
||||||
|
|
||||||
|
|
||||||
class ShellyBLEScanner(BaseHaRemoteScanner):
|
class ShellyBLEScanner(HomeAssistantRemoteScanner):
|
||||||
"""Scanner for shelly."""
|
"""Scanner for shelly."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -7,9 +7,9 @@ import pytest
|
|||||||
from homeassistant.components import bluetooth
|
from homeassistant.components import bluetooth
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
|
||||||
BaseHaScanner,
|
BaseHaScanner,
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
async_scanner_by_source,
|
async_scanner_by_source,
|
||||||
async_scanner_devices_by_address,
|
async_scanner_devices_by_address,
|
||||||
)
|
)
|
||||||
@ -27,7 +27,7 @@ from . import (
|
|||||||
async def test_scanner_by_source(hass: HomeAssistant, enable_bluetooth: None) -> None:
|
async def test_scanner_by_source(hass: HomeAssistant, enable_bluetooth: None) -> None:
|
||||||
"""Test we can get a scanner by source."""
|
"""Test we can get a scanner by source."""
|
||||||
|
|
||||||
hci2_scanner = FakeScanner(hass, "hci2", "hci2")
|
hci2_scanner = FakeScanner("hci2", "hci2")
|
||||||
cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True)
|
cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True)
|
||||||
|
|
||||||
assert async_scanner_by_source(hass, "hci2") is hci2_scanner
|
assert async_scanner_by_source(hass, "hci2") is hci2_scanner
|
||||||
@ -46,7 +46,7 @@ async def test_async_scanner_devices_by_address_connectable(
|
|||||||
"""Test getting scanner devices by address with connectable devices."""
|
"""Test getting scanner devices by address with connectable devices."""
|
||||||
manager = _get_manager()
|
manager = _get_manager()
|
||||||
|
|
||||||
class FakeInjectableScanner(BaseHaRemoteScanner):
|
class FakeInjectableScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -135,7 +135,7 @@ async def test_async_scanner_devices_by_address_non_connectable(
|
|||||||
connector = (
|
connector = (
|
||||||
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
|
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
|
||||||
)
|
)
|
||||||
scanner = FakeStaticScanner(hass, "esp32", "esp32", connector)
|
scanner = FakeStaticScanner("esp32", "esp32", connector)
|
||||||
cancel = manager.async_register_scanner(scanner, False)
|
cancel = manager.async_register_scanner(scanner, False)
|
||||||
|
|
||||||
assert scanner.discovered_devices_and_advertisement_data == {
|
assert scanner.discovered_devices_and_advertisement_data == {
|
||||||
|
@ -13,8 +13,8 @@ import pytest
|
|||||||
from homeassistant.components import bluetooth
|
from homeassistant.components import bluetooth
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
storage,
|
storage,
|
||||||
)
|
)
|
||||||
from homeassistant.components.bluetooth.advertisement_tracker import (
|
from homeassistant.components.bluetooth.advertisement_tracker import (
|
||||||
@ -89,7 +89,7 @@ async def test_remote_scanner(
|
|||||||
rssi=-100,
|
rssi=-100,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -173,7 +173,7 @@ async def test_remote_scanner_expires_connectable(
|
|||||||
rssi=-100,
|
rssi=-100,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -248,7 +248,7 @@ async def test_remote_scanner_expires_non_connectable(
|
|||||||
rssi=-100,
|
rssi=-100,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -346,7 +346,7 @@ async def test_base_scanner_connecting_behavior(
|
|||||||
rssi=-100,
|
rssi=-100,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -418,7 +418,7 @@ async def test_restore_history_remote_adapter(
|
|||||||
connector = (
|
connector = (
|
||||||
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
|
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
|
||||||
)
|
)
|
||||||
scanner = BaseHaRemoteScanner(
|
scanner = HomeAssistantRemoteScanner(
|
||||||
hass,
|
hass,
|
||||||
"atom-bluetooth-proxy-ceaac4",
|
"atom-bluetooth-proxy-ceaac4",
|
||||||
"atom-bluetooth-proxy-ceaac4",
|
"atom-bluetooth-proxy-ceaac4",
|
||||||
@ -434,7 +434,7 @@ async def test_restore_history_remote_adapter(
|
|||||||
cancel()
|
cancel()
|
||||||
unsetup()
|
unsetup()
|
||||||
|
|
||||||
scanner = BaseHaRemoteScanner(
|
scanner = HomeAssistantRemoteScanner(
|
||||||
hass,
|
hass,
|
||||||
"atom-bluetooth-proxy-ceaac4",
|
"atom-bluetooth-proxy-ceaac4",
|
||||||
"atom-bluetooth-proxy-ceaac4",
|
"atom-bluetooth-proxy-ceaac4",
|
||||||
@ -470,7 +470,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
|||||||
rssi=-100,
|
rssi=-100,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -592,7 +592,7 @@ async def test_scanner_stops_responding(
|
|||||||
"""Test we mark a scanner are not scanning when it stops responding."""
|
"""Test we mark a scanner are not scanning when it stops responding."""
|
||||||
manager = _get_manager()
|
manager = _get_manager()
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
"""A fake remote scanner."""
|
"""A fake remote scanner."""
|
||||||
|
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
|
@ -7,8 +7,8 @@ from bluetooth_adapters import DEFAULT_ADDRESS
|
|||||||
from homeassistant.components import bluetooth
|
from homeassistant.components import bluetooth
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ async def test_diagnostics_remote_adapter(
|
|||||||
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -12,12 +12,12 @@ import pytest
|
|||||||
from homeassistant.components import bluetooth
|
from homeassistant.components import bluetooth
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
|
||||||
BluetoothChange,
|
BluetoothChange,
|
||||||
BluetoothScanningMode,
|
BluetoothScanningMode,
|
||||||
BluetoothServiceInfo,
|
BluetoothServiceInfo,
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
async_ble_device_from_address,
|
async_ble_device_from_address,
|
||||||
async_get_advertisement_callback,
|
async_get_advertisement_callback,
|
||||||
async_get_fallback_availability_interval,
|
async_get_fallback_availability_interval,
|
||||||
@ -56,7 +56,7 @@ from tests.common import async_fire_time_changed, load_fixture
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def register_hci0_scanner(hass: HomeAssistant) -> Generator[None, None, None]:
|
def register_hci0_scanner(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||||
"""Register an hci0 scanner."""
|
"""Register an hci0 scanner."""
|
||||||
hci0_scanner = FakeScanner(hass, "hci0", "hci0")
|
hci0_scanner = FakeScanner("hci0", "hci0")
|
||||||
cancel = bluetooth.async_register_scanner(hass, hci0_scanner, True)
|
cancel = bluetooth.async_register_scanner(hass, hci0_scanner, True)
|
||||||
yield
|
yield
|
||||||
cancel()
|
cancel()
|
||||||
@ -65,7 +65,7 @@ def register_hci0_scanner(hass: HomeAssistant) -> Generator[None, None, None]:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def register_hci1_scanner(hass: HomeAssistant) -> Generator[None, None, None]:
|
def register_hci1_scanner(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||||
"""Register an hci1 scanner."""
|
"""Register an hci1 scanner."""
|
||||||
hci1_scanner = FakeScanner(hass, "hci1", "hci1")
|
hci1_scanner = FakeScanner("hci1", "hci1")
|
||||||
cancel = bluetooth.async_register_scanner(hass, hci1_scanner, True)
|
cancel = bluetooth.async_register_scanner(hass, hci1_scanner, True)
|
||||||
yield
|
yield
|
||||||
cancel()
|
cancel()
|
||||||
@ -562,7 +562,7 @@ async def test_switching_adapters_when_one_goes_away(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test switching adapters when one goes away."""
|
"""Test switching adapters when one goes away."""
|
||||||
cancel_hci2 = bluetooth.async_register_scanner(
|
cancel_hci2 = bluetooth.async_register_scanner(
|
||||||
hass, FakeScanner(hass, "hci2", "hci2"), True
|
hass, FakeScanner("hci2", "hci2"), True
|
||||||
)
|
)
|
||||||
|
|
||||||
address = "44:44:33:11:23:45"
|
address = "44:44:33:11:23:45"
|
||||||
@ -612,7 +612,7 @@ async def test_switching_adapters_when_one_stop_scanning(
|
|||||||
hass: HomeAssistant, enable_bluetooth: None, register_hci0_scanner: None
|
hass: HomeAssistant, enable_bluetooth: None, register_hci0_scanner: None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test switching adapters when stops scanning."""
|
"""Test switching adapters when stops scanning."""
|
||||||
hci2_scanner = FakeScanner(hass, "hci2", "hci2")
|
hci2_scanner = FakeScanner("hci2", "hci2")
|
||||||
cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True)
|
cancel_hci2 = bluetooth.async_register_scanner(hass, hci2_scanner, True)
|
||||||
|
|
||||||
address = "44:44:33:11:23:45"
|
address = "44:44:33:11:23:45"
|
||||||
@ -704,7 +704,7 @@ async def test_goes_unavailable_connectable_only_and_recovers(
|
|||||||
BluetoothScanningMode.ACTIVE,
|
BluetoothScanningMode.ACTIVE,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -877,7 +877,7 @@ async def test_goes_unavailable_dismisses_discovery_and_makes_discoverable(
|
|||||||
BluetoothScanningMode.ACTIVE,
|
BluetoothScanningMode.ACTIVE,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
def inject_advertisement(
|
def inject_advertisement(
|
||||||
self, device: BLEDevice, advertisement_data: AdvertisementData
|
self, device: BLEDevice, advertisement_data: AdvertisementData
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -10,9 +10,9 @@ from bleak.backends.scanner import AdvertisementData
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
BaseHaRemoteScanner,
|
|
||||||
BaseHaScanner,
|
BaseHaScanner,
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
)
|
)
|
||||||
from homeassistant.components.bluetooth.wrappers import (
|
from homeassistant.components.bluetooth.wrappers import (
|
||||||
HaBleakClientWrapper,
|
HaBleakClientWrapper,
|
||||||
@ -158,7 +158,7 @@ async def test_wrapped_bleak_client_set_disconnected_callback_after_connected(
|
|||||||
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-100
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}, rssi=-100
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
"""Return a list of discovered devices."""
|
"""Return a list of discovered devices."""
|
||||||
@ -271,7 +271,7 @@ async def test_ble_device_with_proxy_client_out_of_connections(
|
|||||||
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
"""Return a list of discovered devices."""
|
"""Return a list of discovered devices."""
|
||||||
@ -336,7 +336,7 @@ async def test_ble_device_with_proxy_clear_cache(
|
|||||||
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"}
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
"""Return a list of discovered devices."""
|
"""Return a list of discovered devices."""
|
||||||
@ -439,7 +439,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
|
|||||||
"esp32_no_connection_slot",
|
"esp32_no_connection_slot",
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
"""Return a list of discovered devices."""
|
"""Return a list of discovered devices."""
|
||||||
@ -553,7 +553,7 @@ async def test_ble_device_with_proxy_client_out_of_connections_uses_best_availab
|
|||||||
"esp32_no_connection_slot",
|
"esp32_no_connection_slot",
|
||||||
)
|
)
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
@property
|
@property
|
||||||
def discovered_devices(self) -> list[BLEDevice]:
|
def discovered_devices(self) -> list[BLEDevice]:
|
||||||
"""Return a list of discovered devices."""
|
"""Return a list of discovered devices."""
|
||||||
|
@ -12,9 +12,9 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
MONOTONIC_TIME,
|
MONOTONIC_TIME,
|
||||||
BaseHaRemoteScanner,
|
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
HaBluetoothConnector,
|
HaBluetoothConnector,
|
||||||
|
HomeAssistantRemoteScanner,
|
||||||
async_get_advertisement_callback,
|
async_get_advertisement_callback,
|
||||||
)
|
)
|
||||||
from homeassistant.components.bluetooth.usage import (
|
from homeassistant.components.bluetooth.usage import (
|
||||||
@ -26,7 +26,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from . import _get_manager, generate_advertisement_data, generate_ble_device
|
from . import _get_manager, generate_advertisement_data, generate_ble_device
|
||||||
|
|
||||||
|
|
||||||
class FakeScanner(BaseHaRemoteScanner):
|
class FakeScanner(HomeAssistantRemoteScanner):
|
||||||
"""Fake scanner."""
|
"""Fake scanner."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user