Update to bleak 0.15 (#75941)
This commit is contained in:
parent
c4ad6d46ae
commit
80a9659524
39 changed files with 223 additions and 152 deletions
|
@ -8,7 +8,7 @@ from dataclasses import dataclass
|
|||
from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
import logging
|
||||
from typing import Final, Union
|
||||
from typing import Final
|
||||
|
||||
import async_timeout
|
||||
from bleak import BleakError
|
||||
|
@ -96,12 +96,8 @@ SCANNING_MODE_TO_BLEAK = {
|
|||
|
||||
|
||||
BluetoothChange = Enum("BluetoothChange", "ADVERTISEMENT")
|
||||
BluetoothCallback = Callable[
|
||||
[Union[BluetoothServiceInfoBleak, BluetoothServiceInfo], BluetoothChange], None
|
||||
]
|
||||
ProcessAdvertisementCallback = Callable[
|
||||
[Union[BluetoothServiceInfoBleak, BluetoothServiceInfo]], bool
|
||||
]
|
||||
BluetoothCallback = Callable[[BluetoothServiceInfoBleak, BluetoothChange], None]
|
||||
ProcessAdvertisementCallback = Callable[[BluetoothServiceInfoBleak], bool]
|
||||
|
||||
|
||||
@hass_callback
|
||||
|
@ -157,9 +153,15 @@ def async_register_callback(
|
|||
hass: HomeAssistant,
|
||||
callback: BluetoothCallback,
|
||||
match_dict: BluetoothCallbackMatcher | None,
|
||||
mode: BluetoothScanningMode,
|
||||
) -> Callable[[], None]:
|
||||
"""Register to receive a callback on bluetooth change.
|
||||
|
||||
mode is currently not used as we only support active scanning.
|
||||
Passive scanning will be available in the future. The flag
|
||||
is required to be present to avoid a future breaking change
|
||||
when we support passive scanning.
|
||||
|
||||
Returns a callback that can be used to cancel the registration.
|
||||
"""
|
||||
manager: BluetoothManager = hass.data[DOMAIN]
|
||||
|
@ -170,19 +172,20 @@ async def async_process_advertisements(
|
|||
hass: HomeAssistant,
|
||||
callback: ProcessAdvertisementCallback,
|
||||
match_dict: BluetoothCallbackMatcher,
|
||||
mode: BluetoothScanningMode,
|
||||
timeout: int,
|
||||
) -> BluetoothServiceInfo:
|
||||
) -> BluetoothServiceInfoBleak:
|
||||
"""Process advertisements until callback returns true or timeout expires."""
|
||||
done: Future[BluetoothServiceInfo] = Future()
|
||||
done: Future[BluetoothServiceInfoBleak] = Future()
|
||||
|
||||
@hass_callback
|
||||
def _async_discovered_device(
|
||||
service_info: BluetoothServiceInfo, change: BluetoothChange
|
||||
service_info: BluetoothServiceInfoBleak, change: BluetoothChange
|
||||
) -> None:
|
||||
if callback(service_info):
|
||||
done.set_result(service_info)
|
||||
|
||||
unload = async_register_callback(hass, _async_discovered_device, match_dict)
|
||||
unload = async_register_callback(hass, _async_discovered_device, match_dict, mode)
|
||||
|
||||
try:
|
||||
async with async_timeout.timeout(timeout):
|
||||
|
@ -333,7 +336,7 @@ class BluetoothManager:
|
|||
)
|
||||
try:
|
||||
async with async_timeout.timeout(START_TIMEOUT):
|
||||
await self.scanner.start()
|
||||
await self.scanner.start() # type: ignore[no-untyped-call]
|
||||
except asyncio.TimeoutError as ex:
|
||||
self._cancel_device_detected()
|
||||
raise ConfigEntryNotReady(
|
||||
|
@ -500,7 +503,7 @@ class BluetoothManager:
|
|||
self._cancel_unavailable_tracking = None
|
||||
if self.scanner:
|
||||
try:
|
||||
await self.scanner.stop()
|
||||
await self.scanner.stop() # type: ignore[no-untyped-call]
|
||||
except BleakError as ex:
|
||||
# This is not fatal, and they may want to reload
|
||||
# the config entry to restart the scanner if they
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/bluetooth",
|
||||
"dependencies": ["websocket_api"],
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["bleak==0.14.3", "bluetooth-adapters==0.1.2"],
|
||||
"requirements": ["bleak==0.15.0", "bluetooth-adapters==0.1.2"],
|
||||
"codeowners": ["@bdraco"],
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push"
|
||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
from typing import Any, Final, cast
|
||||
from typing import Any, Final
|
||||
|
||||
from bleak import BleakScanner
|
||||
from bleak.backends.device import BLEDevice
|
||||
|
@ -32,7 +32,7 @@ def _dispatch_callback(
|
|||
"""Dispatch the callback."""
|
||||
if not callback:
|
||||
# Callback destroyed right before being called, ignore
|
||||
return
|
||||
return # type: ignore[unreachable]
|
||||
|
||||
if (uuids := filters.get(FILTER_UUIDS)) and not uuids.intersection(
|
||||
advertisement_data.service_uuids
|
||||
|
@ -45,7 +45,7 @@ def _dispatch_callback(
|
|||
_LOGGER.exception("Error in callback: %s", callback)
|
||||
|
||||
|
||||
class HaBleakScanner(BleakScanner): # type: ignore[misc]
|
||||
class HaBleakScanner(BleakScanner):
|
||||
"""BleakScanner that cannot be stopped."""
|
||||
|
||||
def __init__( # pylint: disable=super-init-not-called
|
||||
|
@ -106,16 +106,29 @@ class HaBleakScanner(BleakScanner): # type: ignore[misc]
|
|||
_dispatch_callback(*callback_filters, device, advertisement_data)
|
||||
|
||||
|
||||
class HaBleakScannerWrapper(BaseBleakScanner): # type: ignore[misc]
|
||||
class HaBleakScannerWrapper(BaseBleakScanner):
|
||||
"""A wrapper that uses the single instance."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
*args: Any,
|
||||
detection_callback: AdvertisementDataCallback | None = None,
|
||||
service_uuids: list[str] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize the BleakScanner."""
|
||||
self._detection_cancel: CALLBACK_TYPE | None = None
|
||||
self._mapped_filters: dict[str, set[str]] = {}
|
||||
self._adv_data_callback: AdvertisementDataCallback | None = None
|
||||
self._map_filters(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
remapped_kwargs = {
|
||||
"detection_callback": detection_callback,
|
||||
"service_uuids": service_uuids or [],
|
||||
**kwargs,
|
||||
}
|
||||
self._map_filters(*args, **remapped_kwargs)
|
||||
super().__init__(
|
||||
detection_callback=detection_callback, service_uuids=service_uuids or []
|
||||
)
|
||||
|
||||
async def stop(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Stop scanning for devices."""
|
||||
|
@ -153,9 +166,11 @@ class HaBleakScannerWrapper(BaseBleakScanner): # type: ignore[misc]
|
|||
def discovered_devices(self) -> list[BLEDevice]:
|
||||
"""Return a list of discovered devices."""
|
||||
assert HA_BLEAK_SCANNER is not None
|
||||
return cast(list[BLEDevice], HA_BLEAK_SCANNER.discovered_devices)
|
||||
return HA_BLEAK_SCANNER.discovered_devices
|
||||
|
||||
def register_detection_callback(self, callback: AdvertisementDataCallback) -> None:
|
||||
def register_detection_callback(
|
||||
self, callback: AdvertisementDataCallback | None
|
||||
) -> None:
|
||||
"""Register a callback that is called when a device is discovered or has a property changed.
|
||||
|
||||
This method takes the callback and registers it with the long running
|
||||
|
@ -171,6 +186,7 @@ class HaBleakScannerWrapper(BaseBleakScanner): # type: ignore[misc]
|
|||
self._cancel_callback()
|
||||
super().register_detection_callback(self._adv_data_callback)
|
||||
assert HA_BLEAK_SCANNER is not None
|
||||
assert self._callback is not None
|
||||
self._detection_cancel = HA_BLEAK_SCANNER.async_register_callback(
|
||||
self._callback, self._mapped_filters
|
||||
)
|
||||
|
|
|
@ -6,10 +6,9 @@ import logging
|
|||
from typing import Any
|
||||
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import BluetoothChange
|
||||
from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak
|
||||
from .update_coordinator import BasePassiveBluetoothCoordinator
|
||||
|
||||
|
||||
|
@ -25,9 +24,10 @@ class PassiveBluetoothDataUpdateCoordinator(BasePassiveBluetoothCoordinator):
|
|||
hass: HomeAssistant,
|
||||
logger: logging.Logger,
|
||||
address: str,
|
||||
mode: BluetoothScanningMode,
|
||||
) -> None:
|
||||
"""Initialize PassiveBluetoothDataUpdateCoordinator."""
|
||||
super().__init__(hass, logger, address)
|
||||
super().__init__(hass, logger, address, mode)
|
||||
self._listeners: dict[CALLBACK_TYPE, tuple[CALLBACK_TYPE, object | None]] = {}
|
||||
|
||||
@callback
|
||||
|
@ -65,7 +65,7 @@ class PassiveBluetoothDataUpdateCoordinator(BasePassiveBluetoothCoordinator):
|
|||
@callback
|
||||
def _async_handle_bluetooth_event(
|
||||
self,
|
||||
service_info: BluetoothServiceInfo,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
change: BluetoothChange,
|
||||
) -> None:
|
||||
"""Handle a Bluetooth event."""
|
||||
|
|
|
@ -6,14 +6,12 @@ import dataclasses
|
|||
import logging
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
from home_assistant_bluetooth import BluetoothServiceInfo
|
||||
|
||||
from homeassistant.const import ATTR_IDENTIFIERS, ATTR_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import BluetoothChange
|
||||
from . import BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak
|
||||
from .const import DOMAIN
|
||||
from .update_coordinator import BasePassiveBluetoothCoordinator
|
||||
|
||||
|
@ -62,9 +60,10 @@ class PassiveBluetoothProcessorCoordinator(BasePassiveBluetoothCoordinator):
|
|||
hass: HomeAssistant,
|
||||
logger: logging.Logger,
|
||||
address: str,
|
||||
mode: BluetoothScanningMode,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(hass, logger, address)
|
||||
super().__init__(hass, logger, address, mode)
|
||||
self._processors: list[PassiveBluetoothDataProcessor] = []
|
||||
|
||||
@callback
|
||||
|
@ -92,7 +91,7 @@ class PassiveBluetoothProcessorCoordinator(BasePassiveBluetoothCoordinator):
|
|||
@callback
|
||||
def _async_handle_bluetooth_event(
|
||||
self,
|
||||
service_info: BluetoothServiceInfo,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
change: BluetoothChange,
|
||||
) -> None:
|
||||
"""Handle a Bluetooth event."""
|
||||
|
@ -122,7 +121,7 @@ class PassiveBluetoothDataProcessor(Generic[_T]):
|
|||
The processor will call the update_method every time the bluetooth device
|
||||
receives a new advertisement data from the coordinator with the following signature:
|
||||
|
||||
update_method(service_info: BluetoothServiceInfo) -> PassiveBluetoothDataUpdate
|
||||
update_method(service_info: BluetoothServiceInfoBleak) -> PassiveBluetoothDataUpdate
|
||||
|
||||
As the size of each advertisement is limited, the update_method should
|
||||
return a PassiveBluetoothDataUpdate object that contains only data that
|
||||
|
@ -135,7 +134,9 @@ class PassiveBluetoothDataProcessor(Generic[_T]):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
update_method: Callable[[BluetoothServiceInfo], PassiveBluetoothDataUpdate[_T]],
|
||||
update_method: Callable[
|
||||
[BluetoothServiceInfoBleak], PassiveBluetoothDataUpdate[_T]
|
||||
],
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
self.coordinator: PassiveBluetoothProcessorCoordinator
|
||||
|
@ -241,7 +242,7 @@ class PassiveBluetoothDataProcessor(Generic[_T]):
|
|||
@callback
|
||||
def async_handle_bluetooth_event(
|
||||
self,
|
||||
service_info: BluetoothServiceInfo,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
change: BluetoothChange,
|
||||
) -> None:
|
||||
"""Handle a Bluetooth event."""
|
||||
|
|
|
@ -4,13 +4,13 @@ from __future__ import annotations
|
|||
import logging
|
||||
import time
|
||||
|
||||
from home_assistant_bluetooth import BluetoothServiceInfo
|
||||
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
|
||||
from . import (
|
||||
BluetoothCallbackMatcher,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_register_callback,
|
||||
async_track_unavailable,
|
||||
)
|
||||
|
@ -27,6 +27,7 @@ class BasePassiveBluetoothCoordinator:
|
|||
hass: HomeAssistant,
|
||||
logger: logging.Logger,
|
||||
address: str,
|
||||
mode: BluetoothScanningMode,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
self.hass = hass
|
||||
|
@ -36,6 +37,7 @@ class BasePassiveBluetoothCoordinator:
|
|||
self._cancel_track_unavailable: CALLBACK_TYPE | None = None
|
||||
self._cancel_bluetooth_advertisements: CALLBACK_TYPE | None = None
|
||||
self._present = False
|
||||
self.mode = mode
|
||||
self.last_seen = 0.0
|
||||
|
||||
@callback
|
||||
|
@ -61,6 +63,7 @@ class BasePassiveBluetoothCoordinator:
|
|||
self.hass,
|
||||
self._async_handle_bluetooth_event,
|
||||
BluetoothCallbackMatcher(address=self.address),
|
||||
self.mode,
|
||||
)
|
||||
self._cancel_track_unavailable = async_track_unavailable(
|
||||
self.hass,
|
||||
|
@ -86,7 +89,7 @@ class BasePassiveBluetoothCoordinator:
|
|||
@callback
|
||||
def _async_handle_bluetooth_event(
|
||||
self,
|
||||
service_info: BluetoothServiceInfo,
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
change: BluetoothChange,
|
||||
) -> None:
|
||||
"""Handle a Bluetooth event."""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""bluetooth usage utility to handle multiple instances."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bleak
|
||||
|
@ -10,9 +11,9 @@ ORIGINAL_BLEAK_SCANNER = bleak.BleakScanner
|
|||
|
||||
def install_multiple_bleak_catcher() -> None:
|
||||
"""Wrap the bleak classes to return the shared instance if multiple instances are detected."""
|
||||
bleak.BleakScanner = HaBleakScannerWrapper
|
||||
bleak.BleakScanner = HaBleakScannerWrapper # type: ignore[misc, assignment]
|
||||
|
||||
|
||||
def uninstall_multiple_bleak_catcher() -> None:
|
||||
"""Unwrap the bleak classes."""
|
||||
bleak.BleakScanner = ORIGINAL_BLEAK_SCANNER
|
||||
bleak.BleakScanner = ORIGINAL_BLEAK_SCANNER # type: ignore[misc]
|
||||
|
|
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import UUID
|
||||
|
||||
from bleak import BleakClient, BleakError
|
||||
|
@ -31,9 +30,6 @@ from homeassistant.helpers.event import async_track_time_interval
|
|||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bleak.backends.device import BLEDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Base UUID: 00000000-0000-1000-8000-00805F9B34FB
|
||||
|
@ -139,15 +135,12 @@ async def async_setup_scanner( # noqa: C901
|
|||
async def _async_see_update_ble_battery(
|
||||
mac: str,
|
||||
now: datetime,
|
||||
service_info: bluetooth.BluetoothServiceInfo,
|
||||
service_info: bluetooth.BluetoothServiceInfoBleak,
|
||||
) -> None:
|
||||
"""Lookup Bluetooth LE devices and update status."""
|
||||
battery = None
|
||||
ble_device: BLEDevice | str = (
|
||||
bluetooth.async_ble_device_from_address(hass, mac) or mac
|
||||
)
|
||||
try:
|
||||
async with BleakClient(ble_device) as client:
|
||||
async with BleakClient(service_info.device) as client:
|
||||
bat_char = await client.read_gatt_char(BATTERY_CHARACTERISTIC_UUID)
|
||||
battery = ord(bat_char)
|
||||
except asyncio.TimeoutError:
|
||||
|
@ -168,7 +161,8 @@ async def async_setup_scanner( # noqa: C901
|
|||
|
||||
@callback
|
||||
def _async_update_ble(
|
||||
service_info: bluetooth.BluetoothServiceInfo, change: bluetooth.BluetoothChange
|
||||
service_info: bluetooth.BluetoothServiceInfoBleak,
|
||||
change: bluetooth.BluetoothChange,
|
||||
) -> None:
|
||||
"""Update from a ble callback."""
|
||||
mac = service_info.address
|
||||
|
@ -202,7 +196,9 @@ async def async_setup_scanner( # noqa: C901
|
|||
_async_update_ble(service_info, bluetooth.BluetoothChange.ADVERTISEMENT)
|
||||
|
||||
cancels = [
|
||||
bluetooth.async_register_callback(hass, _async_update_ble, None),
|
||||
bluetooth.async_register_callback(
|
||||
hass, _async_update_ble, None, bluetooth.BluetoothScanningMode.ACTIVE
|
||||
),
|
||||
async_track_time_interval(hass, _async_refresh_ble, interval),
|
||||
]
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothScanningMode
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
|
@ -27,6 +28,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass,
|
||||
_LOGGER,
|
||||
address=address,
|
||||
mode=BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(
|
||||
|
|
|
@ -7,7 +7,7 @@ from govee_ble import GoveeBluetoothDeviceData as DeviceData
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfo,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
@ -24,12 +24,12 @@ class GoveeConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._discovery_info: BluetoothServiceInfo | None = None
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_device: DeviceData | None = None
|
||||
self._discovered_devices: dict[str, str] = {}
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
|
|
|
@ -20,13 +20,16 @@ from homeassistant.components import zeroconf
|
|||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.service_info import bluetooth
|
||||
|
||||
from .connection import HKDevice
|
||||
from .const import DOMAIN, KNOWN_DEVICES
|
||||
from .storage import async_get_entity_storage
|
||||
from .utils import async_get_controller
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.components import bluetooth
|
||||
|
||||
|
||||
HOMEKIT_DIR = ".homekit"
|
||||
HOMEKIT_BRIDGE_DOMAIN = "homekit"
|
||||
|
||||
|
@ -359,7 +362,7 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self._async_step_pair_show_form()
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: bluetooth.BluetoothServiceInfo
|
||||
self, discovery_info: bluetooth.BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
if not aiohomekit_const.BLE_TRANSPORT_SUPPORTED:
|
||||
|
|
|
@ -32,7 +32,7 @@ async def async_get_controller(hass: HomeAssistant) -> Controller:
|
|||
|
||||
controller = Controller(
|
||||
async_zeroconf_instance=async_zeroconf_instance,
|
||||
bleak_scanner_instance=bleak_scanner_instance,
|
||||
bleak_scanner_instance=bleak_scanner_instance, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
hass.data[CONTROLLER] = controller
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothScanningMode
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
|
@ -24,9 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||
entry.entry_id
|
||||
] = PassiveBluetoothProcessorCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
address=address,
|
||||
hass, _LOGGER, address=address, mode=BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(
|
||||
|
|
|
@ -7,7 +7,7 @@ from inkbird_ble import INKBIRDBluetoothDeviceData as DeviceData
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfo,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
@ -24,12 +24,12 @@ class INKBIRDConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._discovery_info: BluetoothServiceInfo | None = None
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_device: DeviceData | None = None
|
||||
self._discovered_devices: dict[str, str] = {}
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothScanningMode
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
|
@ -24,9 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||
entry.entry_id
|
||||
] = PassiveBluetoothProcessorCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
address=address,
|
||||
hass, _LOGGER, address=address, mode=BluetoothScanningMode.PASSIVE
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(
|
||||
|
|
|
@ -7,7 +7,7 @@ from moat_ble import MoatBluetoothDeviceData as DeviceData
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfo,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
@ -24,12 +24,12 @@ class MoatConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._discovery_info: BluetoothServiceInfo | None = None
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_device: DeviceData | None = None
|
||||
self._discovered_devices: dict[str, str] = {}
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothScanningMode
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
|
@ -24,9 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||
entry.entry_id
|
||||
] = PassiveBluetoothProcessorCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
address=address,
|
||||
hass, _LOGGER, address=address, mode=BluetoothScanningMode.PASSIVE
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(
|
||||
|
|
|
@ -7,7 +7,7 @@ from sensorpush_ble import SensorPushBluetoothDeviceData as DeviceData
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfo,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
@ -24,12 +24,12 @@ class SensorPushConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._discovery_info: BluetoothServiceInfo | None = None
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_device: DeviceData | None = None
|
||||
self._discovered_devices: dict[str, str] = {}
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from switchbot import SwitchBotAdvertisement, parse_advertisement_data
|
||||
import voluptuous as vol
|
||||
|
@ -15,7 +15,6 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
|
|||
from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||
|
||||
from .const import CONF_RETRY_COUNT, DEFAULT_RETRY_COUNT, DOMAIN, SUPPORTED_MODEL_TYPES
|
||||
|
||||
|
@ -46,15 +45,14 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self._discovered_advs: dict[str, SwitchBotAdvertisement] = {}
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
_LOGGER.debug("Discovered bluetooth device: %s", discovery_info)
|
||||
await self.async_set_unique_id(format_unique_id(discovery_info.address))
|
||||
self._abort_if_unique_id_configured()
|
||||
discovery_info_bleak = cast(BluetoothServiceInfoBleak, discovery_info)
|
||||
parsed = parse_advertisement_data(
|
||||
discovery_info_bleak.device, discovery_info_bleak.advertisement
|
||||
discovery_info.device, discovery_info.advertisement
|
||||
)
|
||||
if not parsed or parsed.data.get("modelName") not in SUPPORTED_MODEL_TYPES:
|
||||
return self.async_abort(reason="not_supported")
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import switchbot
|
||||
|
||||
|
@ -39,7 +39,9 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
|||
device: switchbot.SwitchbotDevice,
|
||||
) -> None:
|
||||
"""Initialize global switchbot data updater."""
|
||||
super().__init__(hass, logger, ble_device.address)
|
||||
super().__init__(
|
||||
hass, logger, ble_device.address, bluetooth.BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
self.ble_device = ble_device
|
||||
self.device = device
|
||||
self.data: dict[str, Any] = {}
|
||||
|
@ -48,14 +50,13 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
|||
@callback
|
||||
def _async_handle_bluetooth_event(
|
||||
self,
|
||||
service_info: bluetooth.BluetoothServiceInfo,
|
||||
service_info: bluetooth.BluetoothServiceInfoBleak,
|
||||
change: bluetooth.BluetoothChange,
|
||||
) -> None:
|
||||
"""Handle a Bluetooth event."""
|
||||
super()._async_handle_bluetooth_event(service_info, change)
|
||||
discovery_info_bleak = cast(bluetooth.BluetoothServiceInfoBleak, service_info)
|
||||
if adv := switchbot.parse_advertisement_data(
|
||||
discovery_info_bleak.device, discovery_info_bleak.advertisement
|
||||
service_info.device, service_info.advertisement
|
||||
):
|
||||
self.data = flatten_sensors_data(adv.data)
|
||||
if "modelName" in self.data:
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothScanningMode
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothProcessorCoordinator,
|
||||
)
|
||||
|
@ -24,9 +25,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||
entry.entry_id
|
||||
] = PassiveBluetoothProcessorCoordinator(
|
||||
hass,
|
||||
_LOGGER,
|
||||
address=address,
|
||||
hass, _LOGGER, address=address, mode=BluetoothScanningMode.PASSIVE
|
||||
)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
entry.async_on_unload(
|
||||
|
|
|
@ -11,7 +11,8 @@ from xiaomi_ble.parser import EncryptionScheme
|
|||
|
||||
from homeassistant.components import onboarding
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfo,
|
||||
BluetoothScanningMode,
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
async_process_advertisements,
|
||||
)
|
||||
|
@ -30,11 +31,11 @@ class Discovery:
|
|||
"""A discovered bluetooth device."""
|
||||
|
||||
title: str
|
||||
discovery_info: BluetoothServiceInfo
|
||||
discovery_info: BluetoothServiceInfoBleak
|
||||
device: DeviceData
|
||||
|
||||
|
||||
def _title(discovery_info: BluetoothServiceInfo, device: DeviceData) -> str:
|
||||
def _title(discovery_info: BluetoothServiceInfoBleak, device: DeviceData) -> str:
|
||||
return device.title or device.get_device_name() or discovery_info.name
|
||||
|
||||
|
||||
|
@ -45,18 +46,20 @@ class XiaomiConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._discovery_info: BluetoothServiceInfo | None = None
|
||||
self._discovery_info: BluetoothServiceInfoBleak | None = None
|
||||
self._discovered_device: DeviceData | None = None
|
||||
self._discovered_devices: dict[str, Discovery] = {}
|
||||
|
||||
async def _async_wait_for_full_advertisement(
|
||||
self, discovery_info: BluetoothServiceInfo, device: DeviceData
|
||||
) -> BluetoothServiceInfo:
|
||||
self, discovery_info: BluetoothServiceInfoBleak, device: DeviceData
|
||||
) -> BluetoothServiceInfoBleak:
|
||||
"""Sometimes first advertisement we receive is blank or incomplete. Wait until we get a useful one."""
|
||||
if not device.pending:
|
||||
return discovery_info
|
||||
|
||||
def _process_more_advertisements(service_info: BluetoothServiceInfo) -> bool:
|
||||
def _process_more_advertisements(
|
||||
service_info: BluetoothServiceInfoBleak,
|
||||
) -> bool:
|
||||
device.update(service_info)
|
||||
return not device.pending
|
||||
|
||||
|
@ -64,11 +67,12 @@ class XiaomiConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
self.hass,
|
||||
_process_more_advertisements,
|
||||
{"address": discovery_info.address},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
ADDITIONAL_DISCOVERY_TIMEOUT,
|
||||
)
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle the bluetooth discovery step."""
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
|
|
|
@ -27,12 +27,12 @@ from .util import uuid as uuid_util
|
|||
from .util.decorator import Registry
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .components.bluetooth import BluetoothServiceInfoBleak
|
||||
from .components.dhcp import DhcpServiceInfo
|
||||
from .components.hassio import HassioServiceInfo
|
||||
from .components.ssdp import SsdpServiceInfo
|
||||
from .components.usb import UsbServiceInfo
|
||||
from .components.zeroconf import ZeroconfServiceInfo
|
||||
from .helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||
from .helpers.service_info.mqtt import MqttServiceInfo
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -1485,7 +1485,7 @@ class ConfigFlow(data_entry_flow.FlowHandler):
|
|||
)
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle a flow initialized by Bluetooth discovery."""
|
||||
return await self._async_step_discovery_without_unique_id()
|
||||
|
|
|
@ -15,11 +15,11 @@ from .typing import DiscoveryInfoType
|
|||
if TYPE_CHECKING:
|
||||
import asyncio
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
|
||||
from homeassistant.components.dhcp import DhcpServiceInfo
|
||||
from homeassistant.components.ssdp import SsdpServiceInfo
|
||||
from homeassistant.components.zeroconf import ZeroconfServiceInfo
|
||||
|
||||
from .service_info.bluetooth import BluetoothServiceInfo
|
||||
from .service_info.mqtt import MqttServiceInfo
|
||||
|
||||
_R = TypeVar("_R", bound="Awaitable[bool] | bool")
|
||||
|
@ -97,7 +97,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
|
|||
return await self.async_step_confirm()
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
self, discovery_info: BluetoothServiceInfoBleak
|
||||
) -> FlowResult:
|
||||
"""Handle a flow initialized by bluetooth discovery."""
|
||||
if self._async_in_progress() or self._async_current_entries():
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""The bluetooth integration service info."""
|
||||
|
||||
from home_assistant_bluetooth import BluetoothServiceInfo
|
||||
|
||||
__all__ = ["BluetoothServiceInfo"]
|
||||
|
|
|
@ -10,7 +10,7 @@ atomicwrites-homeassistant==1.4.1
|
|||
attrs==21.2.0
|
||||
awesomeversion==22.6.0
|
||||
bcrypt==3.1.7
|
||||
bleak==0.14.3
|
||||
bleak==0.15.0
|
||||
bluetooth-adapters==0.1.2
|
||||
certifi>=2021.5.30
|
||||
ciso8601==2.2.0
|
||||
|
|
|
@ -405,7 +405,7 @@ bimmer_connected==0.10.1
|
|||
bizkaibus==0.1.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bleak==0.14.3
|
||||
bleak==0.15.0
|
||||
|
||||
# homeassistant.components.blebox
|
||||
blebox_uniapi==2.0.2
|
||||
|
|
|
@ -326,7 +326,7 @@ bellows==0.31.2
|
|||
bimmer_connected==0.10.1
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
bleak==0.14.3
|
||||
bleak==0.15.0
|
||||
|
||||
# homeassistant.components.blebox
|
||||
blebox_uniapi==2.0.2
|
||||
|
|
|
@ -12,6 +12,7 @@ from homeassistant.components.bluetooth import (
|
|||
SOURCE_LOCAL,
|
||||
UNAVAILABLE_TRACK_SECONDS,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
BluetoothServiceInfo,
|
||||
async_process_advertisements,
|
||||
async_track_unavailable,
|
||||
|
@ -675,6 +676,7 @@ async def test_register_callbacks(hass, mock_bleak_scanner_start, enable_bluetoo
|
|||
hass,
|
||||
_fake_subscriber,
|
||||
{"service_uuids": {"cba20d00-224d-11e6-9fb8-0002a5d5c51b"}},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
@ -760,6 +762,7 @@ async def test_register_callback_by_address(
|
|||
hass,
|
||||
_fake_subscriber,
|
||||
{"address": "44:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
@ -799,6 +802,7 @@ async def test_register_callback_by_address(
|
|||
hass,
|
||||
_fake_subscriber,
|
||||
{"address": "44:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
cancel()
|
||||
|
||||
|
@ -808,6 +812,7 @@ async def test_register_callback_by_address(
|
|||
hass,
|
||||
_fake_subscriber,
|
||||
{"address": "44:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
cancel()
|
||||
|
||||
|
@ -832,7 +837,11 @@ async def test_process_advertisements_bail_on_good_advertisement(
|
|||
|
||||
handle = hass.async_create_task(
|
||||
async_process_advertisements(
|
||||
hass, _callback, {"address": "aa:44:33:11:23:45"}, 5
|
||||
hass,
|
||||
_callback,
|
||||
{"address": "aa:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
5,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -873,7 +882,11 @@ async def test_process_advertisements_ignore_bad_advertisement(
|
|||
|
||||
handle = hass.async_create_task(
|
||||
async_process_advertisements(
|
||||
hass, _callback, {"address": "aa:44:33:11:23:45"}, 5
|
||||
hass,
|
||||
_callback,
|
||||
{"address": "aa:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
5,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -903,7 +916,9 @@ async def test_process_advertisements_timeout(
|
|||
return False
|
||||
|
||||
with pytest.raises(asyncio.TimeoutError):
|
||||
await async_process_advertisements(hass, _callback, {}, 0)
|
||||
await async_process_advertisements(
|
||||
hass, _callback, {}, BluetoothScanningMode.ACTIVE, 0
|
||||
)
|
||||
|
||||
|
||||
async def test_wrapped_instance_with_filter(
|
||||
|
|
|
@ -10,6 +10,7 @@ from homeassistant.components.bluetooth import (
|
|||
DOMAIN,
|
||||
UNAVAILABLE_TRACK_SECONDS,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
)
|
||||
from homeassistant.components.bluetooth.passive_update_coordinator import (
|
||||
PassiveBluetoothCoordinatorEntity,
|
||||
|
@ -42,9 +43,9 @@ GENERIC_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo(
|
|||
class MyCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
||||
"""An example coordinator that subclasses PassiveBluetoothDataUpdateCoordinator."""
|
||||
|
||||
def __init__(self, hass, logger, device_id) -> None:
|
||||
def __init__(self, hass, logger, device_id, mode) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(hass, logger, device_id)
|
||||
super().__init__(hass, logger, device_id, mode)
|
||||
self.data: dict[str, Any] = {}
|
||||
|
||||
def _async_handle_bluetooth_event(
|
||||
|
@ -60,11 +61,13 @@ class MyCoordinator(PassiveBluetoothDataUpdateCoordinator):
|
|||
async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||
"""Test basic usage of the PassiveBluetoothDataUpdateCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
coordinator = MyCoordinator(hass, _LOGGER, "aa:bb:cc:dd:ee:ff")
|
||||
coordinator = MyCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -100,11 +103,13 @@ async def test_context_compatiblity_with_data_update_coordinator(
|
|||
):
|
||||
"""Test contexts can be passed for compatibility with DataUpdateCoordinator."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
coordinator = MyCoordinator(hass, _LOGGER, "aa:bb:cc:dd:ee:ff")
|
||||
coordinator = MyCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -149,11 +154,13 @@ async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
|
|||
):
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
coordinator = MyCoordinator(hass, _LOGGER, "aa:bb:cc:dd:ee:ff")
|
||||
coordinator = MyCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.PASSIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -208,13 +215,15 @@ async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
|
|||
async def test_passive_bluetooth_coordinator_entity(hass, mock_bleak_scanner_start):
|
||||
"""Test integration of PassiveBluetoothDataUpdateCoordinator with PassiveBluetoothCoordinatorEntity."""
|
||||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
coordinator = MyCoordinator(hass, _LOGGER, "aa:bb:cc:dd:ee:ff")
|
||||
coordinator = MyCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
entity = PassiveBluetoothCoordinatorEntity(coordinator)
|
||||
assert entity.available is False
|
||||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -16,6 +16,7 @@ from homeassistant.components.bluetooth import (
|
|||
DOMAIN,
|
||||
UNAVAILABLE_TRACK_SECONDS,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
)
|
||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||
PassiveBluetoothDataProcessor,
|
||||
|
@ -90,12 +91,12 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
|||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -192,12 +193,12 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
|||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -277,12 +278,12 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
|||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -336,12 +337,12 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
|||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -389,12 +390,12 @@ async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
|||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -731,12 +732,12 @@ async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
|||
return GOVEE_B5178_REMOTE_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -841,12 +842,12 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
|||
return NO_DEVICES_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -905,12 +906,12 @@ async def test_passive_bluetooth_entity_with_entity_platform(
|
|||
return NO_DEVICES_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -992,12 +993,12 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
|||
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
coordinator = PassiveBluetoothProcessorCoordinator(
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff"
|
||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
||||
)
|
||||
assert coordinator.available is False # no data yet
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -5,8 +5,9 @@ from datetime import timedelta
|
|||
from unittest.mock import patch
|
||||
|
||||
from bleak import BleakError
|
||||
from bleak.backends.scanner import AdvertisementData, BLEDevice
|
||||
|
||||
from homeassistant.components.bluetooth import BluetoothServiceInfo
|
||||
from homeassistant.components.bluetooth import BluetoothServiceInfoBleak
|
||||
from homeassistant.components.bluetooth_le_tracker import device_tracker
|
||||
from homeassistant.components.bluetooth_le_tracker.device_tracker import (
|
||||
CONF_TRACK_BATTERY,
|
||||
|
@ -79,7 +80,7 @@ async def test_preserve_new_tracked_device_name(
|
|||
device_tracker, "MIN_SEEN_NEW", 3
|
||||
):
|
||||
|
||||
device = BluetoothServiceInfo(
|
||||
device = BluetoothServiceInfoBleak(
|
||||
name=name,
|
||||
address=address,
|
||||
rssi=-19,
|
||||
|
@ -87,6 +88,8 @@ async def test_preserve_new_tracked_device_name(
|
|||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
device=BLEDevice(address, None),
|
||||
advertisement=AdvertisementData(local_name="empty"),
|
||||
)
|
||||
# Return with name when seen first time
|
||||
mock_async_discovered_service_info.return_value = [device]
|
||||
|
@ -100,7 +103,7 @@ async def test_preserve_new_tracked_device_name(
|
|||
assert result
|
||||
|
||||
# Seen once here; return without name when seen subsequent times
|
||||
device = BluetoothServiceInfo(
|
||||
device = BluetoothServiceInfoBleak(
|
||||
name=None,
|
||||
address=address,
|
||||
rssi=-19,
|
||||
|
@ -108,6 +111,8 @@ async def test_preserve_new_tracked_device_name(
|
|||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
device=BLEDevice(address, None),
|
||||
advertisement=AdvertisementData(local_name="empty"),
|
||||
)
|
||||
# Return with name when seen first time
|
||||
mock_async_discovered_service_info.return_value = [device]
|
||||
|
@ -140,7 +145,7 @@ async def test_tracking_battery_times_out(
|
|||
device_tracker, "MIN_SEEN_NEW", 3
|
||||
):
|
||||
|
||||
device = BluetoothServiceInfo(
|
||||
device = BluetoothServiceInfoBleak(
|
||||
name=name,
|
||||
address=address,
|
||||
rssi=-19,
|
||||
|
@ -148,6 +153,8 @@ async def test_tracking_battery_times_out(
|
|||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
device=BLEDevice(address, None),
|
||||
advertisement=AdvertisementData(local_name="empty"),
|
||||
)
|
||||
# Return with name when seen first time
|
||||
mock_async_discovered_service_info.return_value = [device]
|
||||
|
@ -202,7 +209,7 @@ async def test_tracking_battery_fails(hass, mock_bluetooth, mock_device_tracker_
|
|||
device_tracker, "MIN_SEEN_NEW", 3
|
||||
):
|
||||
|
||||
device = BluetoothServiceInfo(
|
||||
device = BluetoothServiceInfoBleak(
|
||||
name=name,
|
||||
address=address,
|
||||
rssi=-19,
|
||||
|
@ -210,6 +217,8 @@ async def test_tracking_battery_fails(hass, mock_bluetooth, mock_device_tracker_
|
|||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
device=BLEDevice(address, None),
|
||||
advertisement=AdvertisementData(local_name="empty"),
|
||||
)
|
||||
# Return with name when seen first time
|
||||
mock_async_discovered_service_info.return_value = [device]
|
||||
|
@ -266,7 +275,7 @@ async def test_tracking_battery_successful(
|
|||
device_tracker, "MIN_SEEN_NEW", 3
|
||||
):
|
||||
|
||||
device = BluetoothServiceInfo(
|
||||
device = BluetoothServiceInfoBleak(
|
||||
name=name,
|
||||
address=address,
|
||||
rssi=-19,
|
||||
|
@ -274,6 +283,8 @@ async def test_tracking_battery_successful(
|
|||
service_data={},
|
||||
service_uuids=[],
|
||||
source="local",
|
||||
device=BLEDevice(address, None),
|
||||
advertisement=AdvertisementData(local_name="empty"),
|
||||
)
|
||||
# Return with name when seen first time
|
||||
mock_async_discovered_service_info.return_value = [device]
|
||||
|
|
|
@ -17,6 +17,12 @@ def fixture_scanner(hass):
|
|||
class MockScanner(BaseBleakScanner):
|
||||
"""Mock Scanner."""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
"""Initialize the scanner."""
|
||||
super().__init__(
|
||||
detection_callback=kwargs.pop("detection_callback"), service_uuids=[]
|
||||
)
|
||||
|
||||
async def start(self):
|
||||
"""Start scanning for devices."""
|
||||
for device in devices:
|
||||
|
|
|
@ -22,7 +22,7 @@ async def test_sensors(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -22,7 +22,7 @@ async def test_sensors(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -22,7 +22,7 @@ async def test_sensors(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -22,7 +22,7 @@ async def test_sensors(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
|
@ -59,7 +59,9 @@ async def test_async_step_bluetooth_valid_device_but_missing_payload(hass):
|
|||
async def test_async_step_bluetooth_valid_device_but_missing_payload_then_full(hass):
|
||||
"""Test discovering a valid device. Payload is too short, but later we get full one."""
|
||||
|
||||
async def _async_process_advertisements(_hass, _callback, _matcher, _timeout):
|
||||
async def _async_process_advertisements(
|
||||
_hass, _callback, _matcher, _mode, _timeout
|
||||
):
|
||||
service_info = make_advertisement(
|
||||
"A4:C1:38:56:53:84",
|
||||
b"XX\xe4\x16,\x84SV8\xc1\xa4+n\xf2\xe9\x12\x00\x00l\x88M\x9e",
|
||||
|
@ -378,7 +380,9 @@ async def test_async_step_user_short_payload_then_full(hass):
|
|||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
async def _async_process_advertisements(_hass, _callback, _matcher, _timeout):
|
||||
async def _async_process_advertisements(
|
||||
_hass, _callback, _matcher, _mode, _timeout
|
||||
):
|
||||
service_info = make_advertisement(
|
||||
"A4:C1:38:56:53:84",
|
||||
b"XX\xe4\x16,\x84SV8\xc1\xa4+n\xf2\xe9\x12\x00\x00l\x88M\x9e",
|
||||
|
|
|
@ -22,7 +22,7 @@ async def test_sensors(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -60,7 +60,7 @@ async def test_xiaomi_formaldeyhde(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -107,7 +107,7 @@ async def test_xiaomi_consumable(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -154,7 +154,7 @@ async def test_xiaomi_battery_voltage(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -208,7 +208,7 @@ async def test_xiaomi_HHCCJCY01(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
@ -291,7 +291,7 @@ async def test_xiaomi_CGDK2(hass):
|
|||
|
||||
saved_callback = None
|
||||
|
||||
def _async_register_callback(_hass, _callback, _matcher):
|
||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
||||
nonlocal saved_callback
|
||||
saved_callback = _callback
|
||||
return lambda: None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue