From 80a9659524a47868070a16cdfa7f654fba7f6bb7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Jul 2022 14:53:33 -1000 Subject: [PATCH] Update to bleak 0.15 (#75941) --- .../components/bluetooth/__init__.py | 29 ++++++++------- .../components/bluetooth/manifest.json | 2 +- homeassistant/components/bluetooth/models.py | 34 ++++++++++++----- .../bluetooth/passive_update_coordinator.py | 8 ++-- .../bluetooth/passive_update_processor.py | 17 +++++---- .../bluetooth/update_coordinator.py | 9 +++-- homeassistant/components/bluetooth/usage.py | 5 ++- .../bluetooth_le_tracker/device_tracker.py | 18 ++++----- .../components/govee_ble/__init__.py | 2 + .../components/govee_ble/config_flow.py | 6 +-- .../homekit_controller/config_flow.py | 7 +++- .../components/homekit_controller/utils.py | 2 +- homeassistant/components/inkbird/__init__.py | 5 +-- .../components/inkbird/config_flow.py | 6 +-- homeassistant/components/moat/__init__.py | 5 +-- homeassistant/components/moat/config_flow.py | 6 +-- .../components/sensorpush/__init__.py | 5 +-- .../components/sensorpush/config_flow.py | 6 +-- .../components/switchbot/config_flow.py | 8 ++-- .../components/switchbot/coordinator.py | 11 +++--- .../components/xiaomi_ble/__init__.py | 5 +-- .../components/xiaomi_ble/config_flow.py | 20 ++++++---- homeassistant/config_entries.py | 4 +- homeassistant/helpers/config_entry_flow.py | 4 +- .../helpers/service_info/bluetooth.py | 1 + homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/bluetooth/test_init.py | 21 +++++++++-- .../test_passive_update_coordinator.py | 29 ++++++++++----- .../test_passive_update_processor.py | 37 ++++++++++--------- .../test_device_tracker.py | 23 +++++++++--- tests/components/fjaraskupan/conftest.py | 6 +++ tests/components/govee_ble/test_sensor.py | 2 +- tests/components/inkbird/test_sensor.py | 2 +- tests/components/moat/test_sensor.py | 2 +- tests/components/sensorpush/test_sensor.py | 2 +- .../components/xiaomi_ble/test_config_flow.py | 8 +++- tests/components/xiaomi_ble/test_sensor.py | 12 +++--- 39 files changed, 223 insertions(+), 152 deletions(-) diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index eb8e31baef0..9adaac84333 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -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 diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index c6ca8b11400..f215e8fa161 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -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" diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index 408e0698879..6f814c7b66b 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -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 ) diff --git a/homeassistant/components/bluetooth/passive_update_coordinator.py b/homeassistant/components/bluetooth/passive_update_coordinator.py index 97e7ddc49ee..31a6b065830 100644 --- a/homeassistant/components/bluetooth/passive_update_coordinator.py +++ b/homeassistant/components/bluetooth/passive_update_coordinator.py @@ -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.""" diff --git a/homeassistant/components/bluetooth/passive_update_processor.py b/homeassistant/components/bluetooth/passive_update_processor.py index 43467701879..1f2047c02cb 100644 --- a/homeassistant/components/bluetooth/passive_update_processor.py +++ b/homeassistant/components/bluetooth/passive_update_processor.py @@ -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.""" diff --git a/homeassistant/components/bluetooth/update_coordinator.py b/homeassistant/components/bluetooth/update_coordinator.py index b1cb2de1453..d0f38ce32c6 100644 --- a/homeassistant/components/bluetooth/update_coordinator.py +++ b/homeassistant/components/bluetooth/update_coordinator.py @@ -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.""" diff --git a/homeassistant/components/bluetooth/usage.py b/homeassistant/components/bluetooth/usage.py index da5d062a36f..b3a6783cf30 100644 --- a/homeassistant/components/bluetooth/usage.py +++ b/homeassistant/components/bluetooth/usage.py @@ -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] diff --git a/homeassistant/components/bluetooth_le_tracker/device_tracker.py b/homeassistant/components/bluetooth_le_tracker/device_tracker.py index fa55c22f994..6ba33e506cc 100644 --- a/homeassistant/components/bluetooth_le_tracker/device_tracker.py +++ b/homeassistant/components/bluetooth_le_tracker/device_tracker.py @@ -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), ] diff --git a/homeassistant/components/govee_ble/__init__.py b/homeassistant/components/govee_ble/__init__.py index 3099d401e9b..7a134e43ace 100644 --- a/homeassistant/components/govee_ble/__init__.py +++ b/homeassistant/components/govee_ble/__init__.py @@ -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( diff --git a/homeassistant/components/govee_ble/config_flow.py b/homeassistant/components/govee_ble/config_flow.py index 9f2efac0ce9..1e3a5566bfd 100644 --- a/homeassistant/components/govee_ble/config_flow.py +++ b/homeassistant/components/govee_ble/config_flow.py @@ -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) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index d8b3fda2d06..31677e37b20 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -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: diff --git a/homeassistant/components/homekit_controller/utils.py b/homeassistant/components/homekit_controller/utils.py index d7780029331..6e272067b54 100644 --- a/homeassistant/components/homekit_controller/utils.py +++ b/homeassistant/components/homekit_controller/utils.py @@ -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 diff --git a/homeassistant/components/inkbird/__init__.py b/homeassistant/components/inkbird/__init__.py index 5553b1c6ded..0272114b83c 100644 --- a/homeassistant/components/inkbird/__init__.py +++ b/homeassistant/components/inkbird/__init__.py @@ -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( diff --git a/homeassistant/components/inkbird/config_flow.py b/homeassistant/components/inkbird/config_flow.py index 679ff43b19e..21ed85e117e 100644 --- a/homeassistant/components/inkbird/config_flow.py +++ b/homeassistant/components/inkbird/config_flow.py @@ -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) diff --git a/homeassistant/components/moat/__init__.py b/homeassistant/components/moat/__init__.py index 259b6b66709..237948a8ff6 100644 --- a/homeassistant/components/moat/__init__.py +++ b/homeassistant/components/moat/__init__.py @@ -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( diff --git a/homeassistant/components/moat/config_flow.py b/homeassistant/components/moat/config_flow.py index a353f4963ad..6f51b62d110 100644 --- a/homeassistant/components/moat/config_flow.py +++ b/homeassistant/components/moat/config_flow.py @@ -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) diff --git a/homeassistant/components/sensorpush/__init__.py b/homeassistant/components/sensorpush/__init__.py index 0a9efcbc752..d4a0872ba3f 100644 --- a/homeassistant/components/sensorpush/__init__.py +++ b/homeassistant/components/sensorpush/__init__.py @@ -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( diff --git a/homeassistant/components/sensorpush/config_flow.py b/homeassistant/components/sensorpush/config_flow.py index 1a8e8b47abe..d10c2f481a6 100644 --- a/homeassistant/components/sensorpush/config_flow.py +++ b/homeassistant/components/sensorpush/config_flow.py @@ -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) diff --git a/homeassistant/components/switchbot/config_flow.py b/homeassistant/components/switchbot/config_flow.py index 3a34a89d9fd..eaad573d370 100644 --- a/homeassistant/components/switchbot/config_flow.py +++ b/homeassistant/components/switchbot/config_flow.py @@ -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") diff --git a/homeassistant/components/switchbot/coordinator.py b/homeassistant/components/switchbot/coordinator.py index f461a3e0f4c..43c576249df 100644 --- a/homeassistant/components/switchbot/coordinator.py +++ b/homeassistant/components/switchbot/coordinator.py @@ -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: diff --git a/homeassistant/components/xiaomi_ble/__init__.py b/homeassistant/components/xiaomi_ble/__init__.py index 4eb20dbd943..791ac1447ad 100644 --- a/homeassistant/components/xiaomi_ble/__init__.py +++ b/homeassistant/components/xiaomi_ble/__init__.py @@ -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( diff --git a/homeassistant/components/xiaomi_ble/config_flow.py b/homeassistant/components/xiaomi_ble/config_flow.py index 91c7e223f1a..aa1ffc24895 100644 --- a/homeassistant/components/xiaomi_ble/config_flow.py +++ b/homeassistant/components/xiaomi_ble/config_flow.py @@ -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) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index b5d5804f100..b0c04323005 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -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() diff --git a/homeassistant/helpers/config_entry_flow.py b/homeassistant/helpers/config_entry_flow.py index bf2f95c12c6..c9d6ffe6065 100644 --- a/homeassistant/helpers/config_entry_flow.py +++ b/homeassistant/helpers/config_entry_flow.py @@ -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(): diff --git a/homeassistant/helpers/service_info/bluetooth.py b/homeassistant/helpers/service_info/bluetooth.py index 968d1dde95f..0db3a39b114 100644 --- a/homeassistant/helpers/service_info/bluetooth.py +++ b/homeassistant/helpers/service_info/bluetooth.py @@ -1,4 +1,5 @@ """The bluetooth integration service info.""" + from home_assistant_bluetooth import BluetoothServiceInfo __all__ = ["BluetoothServiceInfo"] diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 1f86ff4c7c5..c9d424daa33 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -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 diff --git a/requirements_all.txt b/requirements_all.txt index 842376bc696..7fe58a0899b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0d61f2d618a..16919de3c48 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 0664f82dbab..a47916506df 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -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( diff --git a/tests/components/bluetooth/test_passive_update_coordinator.py b/tests/components/bluetooth/test_passive_update_coordinator.py index 4da14fb13d3..31530cd6995 100644 --- a/tests/components/bluetooth/test_passive_update_coordinator.py +++ b/tests/components/bluetooth/test_passive_update_coordinator.py @@ -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 diff --git a/tests/components/bluetooth/test_passive_update_processor.py b/tests/components/bluetooth/test_passive_update_processor.py index ebb69d7c7d0..6a092746a68 100644 --- a/tests/components/bluetooth/test_passive_update_processor.py +++ b/tests/components/bluetooth/test_passive_update_processor.py @@ -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 diff --git a/tests/components/bluetooth_le_tracker/test_device_tracker.py b/tests/components/bluetooth_le_tracker/test_device_tracker.py index dfe47b38b33..f9f0a51fc0f 100644 --- a/tests/components/bluetooth_le_tracker/test_device_tracker.py +++ b/tests/components/bluetooth_le_tracker/test_device_tracker.py @@ -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] diff --git a/tests/components/fjaraskupan/conftest.py b/tests/components/fjaraskupan/conftest.py index d60abcdb9ad..4e06b2ad046 100644 --- a/tests/components/fjaraskupan/conftest.py +++ b/tests/components/fjaraskupan/conftest.py @@ -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: diff --git a/tests/components/govee_ble/test_sensor.py b/tests/components/govee_ble/test_sensor.py index 7a6ecbaed51..75d269ea0ba 100644 --- a/tests/components/govee_ble/test_sensor.py +++ b/tests/components/govee_ble/test_sensor.py @@ -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 diff --git a/tests/components/inkbird/test_sensor.py b/tests/components/inkbird/test_sensor.py index a851cb92ec3..cafc22911c3 100644 --- a/tests/components/inkbird/test_sensor.py +++ b/tests/components/inkbird/test_sensor.py @@ -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 diff --git a/tests/components/moat/test_sensor.py b/tests/components/moat/test_sensor.py index 826bbc72cdb..6424144106b 100644 --- a/tests/components/moat/test_sensor.py +++ b/tests/components/moat/test_sensor.py @@ -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 diff --git a/tests/components/sensorpush/test_sensor.py b/tests/components/sensorpush/test_sensor.py index 31fbdd8d712..34179985d78 100644 --- a/tests/components/sensorpush/test_sensor.py +++ b/tests/components/sensorpush/test_sensor.py @@ -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 diff --git a/tests/components/xiaomi_ble/test_config_flow.py b/tests/components/xiaomi_ble/test_config_flow.py index b424228cc6c..86fda21aaa0 100644 --- a/tests/components/xiaomi_ble/test_config_flow.py +++ b/tests/components/xiaomi_ble/test_config_flow.py @@ -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", diff --git a/tests/components/xiaomi_ble/test_sensor.py b/tests/components/xiaomi_ble/test_sensor.py index dca00e92254..011c6daecae 100644 --- a/tests/components/xiaomi_ble/test_sensor.py +++ b/tests/components/xiaomi_ble/test_sensor.py @@ -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