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