Reduce overhead to update passive bluetooth devices (#78545)
This commit is contained in:
parent
b093c2840b
commit
085abc74ee
25 changed files with 364 additions and 709 deletions
|
@ -103,6 +103,16 @@ def async_discovered_service_info(
|
||||||
return _get_manager(hass).async_discovered_service_info(connectable)
|
return _get_manager(hass).async_discovered_service_info(connectable)
|
||||||
|
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def async_last_service_info(
|
||||||
|
hass: HomeAssistant, address: str, connectable: bool = True
|
||||||
|
) -> BluetoothServiceInfoBleak | None:
|
||||||
|
"""Return the last service info for an address."""
|
||||||
|
if DATA_MANAGER not in hass.data:
|
||||||
|
return None
|
||||||
|
return _get_manager(hass).async_last_service_info(address, connectable)
|
||||||
|
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def async_ble_device_from_address(
|
def async_ble_device_from_address(
|
||||||
hass: HomeAssistant, address: str, connectable: bool = True
|
hass: HomeAssistant, address: str, connectable: bool = True
|
||||||
|
@ -173,7 +183,7 @@ async def async_process_advertisements(
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def async_track_unavailable(
|
def async_track_unavailable(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
callback: Callable[[str], None],
|
callback: Callable[[BluetoothServiceInfoBleak], None],
|
||||||
address: str,
|
address: str,
|
||||||
connectable: bool = True,
|
connectable: bool = True,
|
||||||
) -> Callable[[], None]:
|
) -> Callable[[], None]:
|
||||||
|
|
|
@ -143,9 +143,11 @@ class BluetoothManager:
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._integration_matcher = integration_matcher
|
self._integration_matcher = integration_matcher
|
||||||
self._cancel_unavailable_tracking: list[CALLBACK_TYPE] = []
|
self._cancel_unavailable_tracking: list[CALLBACK_TYPE] = []
|
||||||
self._unavailable_callbacks: dict[str, list[Callable[[str], None]]] = {}
|
self._unavailable_callbacks: dict[
|
||||||
|
str, list[Callable[[BluetoothServiceInfoBleak], None]]
|
||||||
|
] = {}
|
||||||
self._connectable_unavailable_callbacks: dict[
|
self._connectable_unavailable_callbacks: dict[
|
||||||
str, list[Callable[[str], None]]
|
str, list[Callable[[BluetoothServiceInfoBleak], None]]
|
||||||
] = {}
|
] = {}
|
||||||
self._callback_index = BluetoothCallbackMatcherIndex()
|
self._callback_index = BluetoothCallbackMatcherIndex()
|
||||||
self._bleak_callbacks: list[
|
self._bleak_callbacks: list[
|
||||||
|
@ -269,12 +271,12 @@ class BluetoothManager:
|
||||||
}
|
}
|
||||||
disappeared = history_set.difference(active_addresses)
|
disappeared = history_set.difference(active_addresses)
|
||||||
for address in disappeared:
|
for address in disappeared:
|
||||||
del history[address]
|
service_info = history.pop(address)
|
||||||
if not (callbacks := unavailable_callbacks.get(address)):
|
if not (callbacks := unavailable_callbacks.get(address)):
|
||||||
continue
|
continue
|
||||||
for callback in callbacks:
|
for callback in callbacks:
|
||||||
try:
|
try:
|
||||||
callback(address)
|
callback(service_info)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception("Error in unavailable callback")
|
_LOGGER.exception("Error in unavailable callback")
|
||||||
|
|
||||||
|
@ -358,7 +360,10 @@ class BluetoothManager:
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def async_track_unavailable(
|
def async_track_unavailable(
|
||||||
self, callback: Callable[[str], None], address: str, connectable: bool
|
self,
|
||||||
|
callback: Callable[[BluetoothServiceInfoBleak], None],
|
||||||
|
address: str,
|
||||||
|
connectable: bool,
|
||||||
) -> Callable[[], None]:
|
) -> Callable[[], None]:
|
||||||
"""Register a callback."""
|
"""Register a callback."""
|
||||||
unavailable_callbacks = self._get_unavailable_callbacks_by_type(connectable)
|
unavailable_callbacks = self._get_unavailable_callbacks_by_type(connectable)
|
||||||
|
@ -430,9 +435,16 @@ class BluetoothManager:
|
||||||
def async_discovered_service_info(
|
def async_discovered_service_info(
|
||||||
self, connectable: bool
|
self, connectable: bool
|
||||||
) -> Iterable[BluetoothServiceInfoBleak]:
|
) -> Iterable[BluetoothServiceInfoBleak]:
|
||||||
"""Return if the address is present."""
|
"""Return all the discovered services info."""
|
||||||
return self._get_history_by_type(connectable).values()
|
return self._get_history_by_type(connectable).values()
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def async_last_service_info(
|
||||||
|
self, address: str, connectable: bool
|
||||||
|
) -> BluetoothServiceInfoBleak | None:
|
||||||
|
"""Return the last service info for an address."""
|
||||||
|
return self._get_history_by_type(connectable).get(address)
|
||||||
|
|
||||||
@hass_callback
|
@hass_callback
|
||||||
def async_rediscover_address(self, address: str) -> None:
|
def async_rediscover_address(self, address: str) -> None:
|
||||||
"""Trigger discovery of devices which have already been seen."""
|
"""Trigger discovery of devices which have already been seen."""
|
||||||
|
@ -448,7 +460,7 @@ class BluetoothManager:
|
||||||
|
|
||||||
def _get_unavailable_callbacks_by_type(
|
def _get_unavailable_callbacks_by_type(
|
||||||
self, connectable: bool
|
self, connectable: bool
|
||||||
) -> dict[str, list[Callable[[str], None]]]:
|
) -> dict[str, list[Callable[[BluetoothServiceInfoBleak], None]]]:
|
||||||
"""Return the unavailable callbacks by type."""
|
"""Return the unavailable callbacks by type."""
|
||||||
return (
|
return (
|
||||||
self._connectable_unavailable_callbacks
|
self._connectable_unavailable_callbacks
|
||||||
|
|
|
@ -41,9 +41,11 @@ class PassiveBluetoothDataUpdateCoordinator(BasePassiveBluetoothCoordinator):
|
||||||
update_callback()
|
update_callback()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_handle_unavailable(self, address: str) -> None:
|
def _async_handle_unavailable(
|
||||||
|
self, service_info: BluetoothServiceInfoBleak
|
||||||
|
) -> None:
|
||||||
"""Handle the device going unavailable."""
|
"""Handle the device going unavailable."""
|
||||||
super()._async_handle_unavailable(address)
|
super()._async_handle_unavailable(service_info)
|
||||||
self.async_update_listeners()
|
self.async_update_listeners()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -73,7 +75,6 @@ class PassiveBluetoothDataUpdateCoordinator(BasePassiveBluetoothCoordinator):
|
||||||
change: BluetoothChange,
|
change: BluetoothChange,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle a Bluetooth event."""
|
"""Handle a Bluetooth event."""
|
||||||
super()._async_handle_bluetooth_event(service_info, change)
|
|
||||||
self.async_update_listeners()
|
self.async_update_listeners()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -101,9 +101,11 @@ class PassiveBluetoothProcessorCoordinator(
|
||||||
return remove_processor
|
return remove_processor
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_handle_unavailable(self, address: str) -> None:
|
def _async_handle_unavailable(
|
||||||
|
self, service_info: BluetoothServiceInfoBleak
|
||||||
|
) -> None:
|
||||||
"""Handle the device going unavailable."""
|
"""Handle the device going unavailable."""
|
||||||
super()._async_handle_unavailable(address)
|
super()._async_handle_unavailable(service_info)
|
||||||
for processor in self._processors:
|
for processor in self._processors:
|
||||||
processor.async_handle_unavailable()
|
processor.async_handle_unavailable()
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
"""Update coordinator for the Bluetooth integration."""
|
"""Update coordinator for the Bluetooth integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
|
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ from . import (
|
||||||
BluetoothChange,
|
BluetoothChange,
|
||||||
BluetoothScanningMode,
|
BluetoothScanningMode,
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
|
async_address_present,
|
||||||
|
async_last_service_info,
|
||||||
async_register_callback,
|
async_register_callback,
|
||||||
async_track_unavailable,
|
async_track_unavailable,
|
||||||
)
|
)
|
||||||
|
@ -33,14 +35,13 @@ class BasePassiveBluetoothCoordinator:
|
||||||
"""Initialize the coordinator."""
|
"""Initialize the coordinator."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.name: str | None = None
|
|
||||||
self.address = address
|
self.address = address
|
||||||
self.connectable = connectable
|
self.connectable = connectable
|
||||||
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.mode = mode
|
self.mode = mode
|
||||||
self.last_seen = 0.0
|
self._last_unavailable_time = 0.0
|
||||||
|
self._last_name = address
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_start(self) -> CALLBACK_TYPE:
|
def async_start(self) -> CALLBACK_TYPE:
|
||||||
|
@ -53,10 +54,41 @@ class BasePassiveBluetoothCoordinator:
|
||||||
|
|
||||||
return _async_cancel
|
return _async_cancel
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@abstractmethod
|
||||||
|
def _async_handle_bluetooth_event(
|
||||||
|
self,
|
||||||
|
service_info: BluetoothServiceInfoBleak,
|
||||||
|
change: BluetoothChange,
|
||||||
|
) -> None:
|
||||||
|
"""Handle a bluetooth event."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return last known name of the device."""
|
||||||
|
if service_info := async_last_service_info(
|
||||||
|
self.hass, self.address, self.connectable
|
||||||
|
):
|
||||||
|
return service_info.name
|
||||||
|
return self._last_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_seen(self) -> float:
|
||||||
|
"""Return the last time the device was seen."""
|
||||||
|
# If the device is unavailable it will not have a service
|
||||||
|
# info and fall through below.
|
||||||
|
if service_info := async_last_service_info(
|
||||||
|
self.hass, self.address, self.connectable
|
||||||
|
):
|
||||||
|
return service_info.time
|
||||||
|
# This is the time from the last advertisement that
|
||||||
|
# was set when the unavailable callback was called.
|
||||||
|
return self._last_unavailable_time
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if the device is available."""
|
"""Return if the device is available."""
|
||||||
return self._present
|
return async_address_present(self.hass, self.address, self.connectable)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_start(self) -> None:
|
def _async_start(self) -> None:
|
||||||
|
@ -84,17 +116,9 @@ class BasePassiveBluetoothCoordinator:
|
||||||
self._cancel_track_unavailable = None
|
self._cancel_track_unavailable = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_handle_unavailable(self, address: str) -> None:
|
def _async_handle_unavailable(
|
||||||
"""Handle the device going unavailable."""
|
self, service_info: BluetoothServiceInfoBleak
|
||||||
self._present = False
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_handle_bluetooth_event(
|
|
||||||
self,
|
|
||||||
service_info: BluetoothServiceInfoBleak,
|
|
||||||
change: BluetoothChange,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle a Bluetooth event."""
|
"""Handle the device going unavailable."""
|
||||||
self.last_seen = time.monotonic()
|
self._last_unavailable_time = service_info.time
|
||||||
self.name = service_info.name
|
self._last_name = service_info.name
|
||||||
self._present = True
|
|
||||||
|
|
|
@ -56,7 +56,9 @@ class YALEXSBLEEntity(Entity):
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_device_unavailable(self, _address: str) -> None:
|
def _async_device_unavailable(
|
||||||
|
self, _service_info: bluetooth.BluetoothServiceInfoBleak
|
||||||
|
) -> None:
|
||||||
"""Handle device not longer being seen by the bluetooth stack."""
|
"""Handle device not longer being seen by the bluetooth stack."""
|
||||||
self._attr_available = False
|
self._attr_available = False
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
"""Test the BlueMaestro sensors."""
|
"""Test the BlueMaestro sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluemaestro.const import DOMAIN
|
from homeassistant.components.bluemaestro.const import DOMAIN
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
|
||||||
from . import BLUEMAESTRO_SERVICE_INFO
|
from . import BLUEMAESTRO_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all("sensor")) == 0
|
assert len(hass.states.async_all("sensor")) == 0
|
||||||
saved_callback(BLUEMAESTRO_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, BLUEMAESTRO_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all("sensor")) == 4
|
assert len(hass.states.async_all("sensor")) == 4
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,16 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"inject_advertisement",
|
||||||
|
"inject_advertisement_with_source",
|
||||||
|
"inject_advertisement_with_time_and_source",
|
||||||
|
"inject_advertisement_with_time_and_source_connectable",
|
||||||
|
"inject_bluetooth_service_info",
|
||||||
|
"patch_all_discovered_devices",
|
||||||
|
"patch_discovered_devices",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _get_manager() -> BluetoothManager:
|
def _get_manager() -> BluetoothManager:
|
||||||
"""Return the bluetooth manager."""
|
"""Return the bluetooth manager."""
|
||||||
|
@ -80,6 +90,51 @@ def inject_advertisement_with_time_and_source_connectable(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_bluetooth_service_info_bleak(
|
||||||
|
hass: HomeAssistant, info: models.BluetoothServiceInfoBleak
|
||||||
|
) -> None:
|
||||||
|
"""Inject an advertisement into the manager with connectable status."""
|
||||||
|
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call]
|
||||||
|
local_name=None if info.name == "" else info.name,
|
||||||
|
manufacturer_data=info.manufacturer_data,
|
||||||
|
service_data=info.service_data,
|
||||||
|
service_uuids=info.service_uuids,
|
||||||
|
)
|
||||||
|
device = BLEDevice( # type: ignore[no-untyped-call]
|
||||||
|
address=info.address,
|
||||||
|
name=info.name,
|
||||||
|
details={},
|
||||||
|
rssi=info.rssi,
|
||||||
|
)
|
||||||
|
inject_advertisement_with_time_and_source_connectable(
|
||||||
|
hass,
|
||||||
|
device,
|
||||||
|
advertisement_data,
|
||||||
|
info.time,
|
||||||
|
SOURCE_LOCAL,
|
||||||
|
connectable=info.connectable,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_bluetooth_service_info(
|
||||||
|
hass: HomeAssistant, info: models.BluetoothServiceInfo
|
||||||
|
) -> None:
|
||||||
|
"""Inject a BluetoothServiceInfo into the manager."""
|
||||||
|
advertisement_data = AdvertisementData( # type: ignore[no-untyped-call]
|
||||||
|
local_name=None if info.name == "" else info.name,
|
||||||
|
manufacturer_data=info.manufacturer_data,
|
||||||
|
service_data=info.service_data,
|
||||||
|
service_uuids=info.service_uuids,
|
||||||
|
)
|
||||||
|
device = BLEDevice( # type: ignore[no-untyped-call]
|
||||||
|
address=info.address,
|
||||||
|
name=info.name,
|
||||||
|
details={},
|
||||||
|
rssi=info.rssi,
|
||||||
|
)
|
||||||
|
inject_advertisement(hass, device, advertisement_data)
|
||||||
|
|
||||||
|
|
||||||
def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
||||||
"""Mock all the discovered devices from all the scanners."""
|
"""Mock all the discovered devices from all the scanners."""
|
||||||
return patch.object(
|
return patch.object(
|
||||||
|
@ -87,18 +142,6 @@ def patch_all_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def patch_history(mock_history: dict[str, models.BluetoothServiceInfoBleak]) -> None:
|
|
||||||
"""Patch the history."""
|
|
||||||
return patch.dict(_get_manager()._history, mock_history)
|
|
||||||
|
|
||||||
|
|
||||||
def patch_connectable_history(
|
|
||||||
mock_history: dict[str, models.BluetoothServiceInfoBleak]
|
|
||||||
) -> None:
|
|
||||||
"""Patch the connectable history."""
|
|
||||||
return patch.dict(_get_manager()._connectable_history, mock_history)
|
|
||||||
|
|
||||||
|
|
||||||
def patch_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
def patch_discovered_devices(mock_discovered: list[BLEDevice]) -> None:
|
||||||
"""Mock the combined best path to discovered devices from all the scanners."""
|
"""Mock the combined best path to discovered devices from all the scanners."""
|
||||||
return patch.object(
|
return patch.object(
|
||||||
|
|
|
@ -3,13 +3,12 @@ from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from unittest.mock import MagicMock, call, patch
|
from unittest.mock import MagicMock, call
|
||||||
|
|
||||||
from bleak import BleakError
|
from bleak import BleakError
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import (
|
from homeassistant.components.bluetooth import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
BluetoothChange,
|
|
||||||
BluetoothScanningMode,
|
BluetoothScanningMode,
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
)
|
)
|
||||||
|
@ -21,6 +20,8 @@ from homeassistant.helpers.debounce import Debouncer
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,26 +61,14 @@ async def test_basic_usage(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||||
poll_method=_poll,
|
poll_method=_poll,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
|
@ -126,38 +115,26 @@ async def test_poll_can_be_skipped(hass: HomeAssistant, mock_bleak_scanner_start
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": True})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": True})
|
||||||
|
|
||||||
flag = False
|
flag = False
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
||||||
|
|
||||||
flag = True
|
flag = True
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": True})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": True})
|
||||||
|
|
||||||
|
@ -200,27 +177,15 @@ async def test_bleak_error_and_recover(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
# First poll fails
|
# First poll fails
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
||||||
|
|
||||||
|
@ -231,7 +196,7 @@ async def test_bleak_error_and_recover(
|
||||||
|
|
||||||
# Second poll works
|
# Second poll works
|
||||||
flag = False
|
flag = False
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": False})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": False})
|
||||||
|
|
||||||
|
@ -272,33 +237,21 @@ async def test_poll_failure_and_recover(hass: HomeAssistant, mock_bleak_scanner_
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
# First poll fails
|
# First poll fails
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": None})
|
||||||
|
|
||||||
# Second poll works
|
# Second poll works
|
||||||
flag = False
|
flag = False
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": False})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": False})
|
||||||
|
|
||||||
|
@ -334,29 +287,17 @@ async def test_second_poll_needed(hass: HomeAssistant, mock_bleak_scanner_start)
|
||||||
poll_method=_poll,
|
poll_method=_poll,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
# First poll gets queued
|
# First poll gets queued
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Second poll gets stuck behind first poll
|
# Second poll gets stuck behind first poll
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
||||||
|
@ -392,31 +333,19 @@ async def test_rate_limit(hass: HomeAssistant, mock_bleak_scanner_start):
|
||||||
poll_method=_poll,
|
poll_method=_poll,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
processor = MagicMock()
|
processor = MagicMock()
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
async_handle_update = processor.async_handle_update
|
async_handle_update = processor.async_handle_update
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
cancel = coordinator.async_start()
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
|
||||||
|
|
||||||
# First poll gets queued
|
# First poll gets queued
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Second poll gets stuck behind first poll
|
# Second poll gets stuck behind first poll
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Third poll gets stuck behind first poll doesn't get queued
|
# Third poll gets stuck behind first poll doesn't get queued
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
assert async_handle_update.mock_calls[-1] == call({"testdata": 1})
|
||||||
|
|
|
@ -20,9 +20,10 @@ from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import patch_all_discovered_devices, patch_history
|
from . import patch_all_discovered_devices
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import async_fire_time_changed
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -65,32 +66,20 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
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
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
mock_listener = MagicMock()
|
mock_listener = MagicMock()
|
||||||
unregister_listener = coordinator.async_add_listener(mock_listener)
|
unregister_listener = coordinator.async_add_listener(mock_listener)
|
||||||
|
|
||||||
with patch(
|
cancel = coordinator.async_start()
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
cancel = coordinator.async_start()
|
|
||||||
|
|
||||||
assert saved_callback is not None
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
|
||||||
|
|
||||||
assert len(mock_listener.mock_calls) == 1
|
assert len(mock_listener.mock_calls) == 1
|
||||||
assert coordinator.data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
|
assert coordinator.data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
|
|
||||||
unregister_listener()
|
unregister_listener()
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
assert len(mock_listener.mock_calls) == 1
|
assert len(mock_listener.mock_calls) == 1
|
||||||
assert coordinator.data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
|
assert coordinator.data == {"rssi": GENERIC_BLUETOOTH_SERVICE_INFO.rssi}
|
||||||
|
@ -107,21 +96,11 @@ async def test_context_compatiblity_with_data_update_coordinator(
|
||||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.ACTIVE
|
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
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
mock_listener = MagicMock()
|
mock_listener = MagicMock()
|
||||||
coordinator.async_add_listener(mock_listener)
|
coordinator.async_add_listener(mock_listener)
|
||||||
|
|
||||||
with patch(
|
coordinator.async_start()
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
coordinator.async_start()
|
|
||||||
|
|
||||||
assert not set(coordinator.async_contexts())
|
assert not set(coordinator.async_contexts())
|
||||||
|
|
||||||
|
@ -158,41 +137,27 @@ async def test_unavailable_callbacks_mark_the_coordinator_unavailable(
|
||||||
hass, _LOGGER, "aa:bb:cc:dd:ee:ff", BluetoothScanningMode.PASSIVE
|
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
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
mock_listener = MagicMock()
|
mock_listener = MagicMock()
|
||||||
coordinator.async_add_listener(mock_listener)
|
coordinator.async_add_listener(mock_listener)
|
||||||
|
|
||||||
with patch(
|
coordinator.async_start()
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
coordinator.async_start()
|
|
||||||
|
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
|
|
||||||
with patch_all_discovered_devices(
|
with patch_all_discovered_devices([MagicMock(address="44:44:33:11:23:45")]):
|
||||||
[MagicMock(address="44:44:33:11:23:45")]
|
|
||||||
), patch_history({"aa:bb:cc:dd:ee:ff": MagicMock()}):
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
|
|
||||||
with patch_all_discovered_devices(
|
with patch_all_discovered_devices([MagicMock(address="44:44:33:11:23:45")]):
|
||||||
[MagicMock(address="44:44:33:11:23:45")]
|
|
||||||
), patch_history({"aa:bb:cc:dd:ee:ff": MagicMock()}):
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
||||||
)
|
)
|
||||||
|
@ -209,21 +174,10 @@ async def test_passive_bluetooth_coordinator_entity(hass, mock_bleak_scanner_sta
|
||||||
entity = PassiveBluetoothCoordinatorEntity(coordinator)
|
entity = PassiveBluetoothCoordinatorEntity(coordinator)
|
||||||
assert entity.available is False
|
assert entity.available is False
|
||||||
|
|
||||||
saved_callback = None
|
coordinator.async_start()
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
coordinator.async_start()
|
|
||||||
|
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
entity.hass = hass
|
entity.hass = hass
|
||||||
await entity.async_update()
|
await entity.async_update()
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from home_assistant_bluetooth import BluetoothServiceInfo
|
from home_assistant_bluetooth import BluetoothServiceInfo
|
||||||
|
@ -16,6 +17,7 @@ from homeassistant.components.bluetooth import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
BluetoothChange,
|
BluetoothChange,
|
||||||
BluetoothScanningMode,
|
BluetoothScanningMode,
|
||||||
|
BluetoothServiceInfoBleak,
|
||||||
)
|
)
|
||||||
from homeassistant.components.bluetooth.const import UNAVAILABLE_TRACK_SECONDS
|
from homeassistant.components.bluetooth.const import UNAVAILABLE_TRACK_SECONDS
|
||||||
from homeassistant.components.bluetooth.passive_update_processor import (
|
from homeassistant.components.bluetooth.passive_update_processor import (
|
||||||
|
@ -32,9 +34,13 @@ from homeassistant.helpers.entity import DeviceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from . import patch_all_discovered_devices, patch_connectable_history, patch_history
|
from . import patch_all_discovered_devices
|
||||||
|
|
||||||
from tests.common import MockEntityPlatform, async_fire_time_changed
|
from tests.common import MockEntityPlatform, async_fire_time_changed
|
||||||
|
from tests.components.bluetooth import (
|
||||||
|
inject_bluetooth_service_info,
|
||||||
|
inject_bluetooth_service_info_bleak,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -50,6 +56,7 @@ GENERIC_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo(
|
||||||
service_uuids=[],
|
service_uuids=[],
|
||||||
source="local",
|
source="local",
|
||||||
)
|
)
|
||||||
|
|
||||||
GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
||||||
devices={
|
devices={
|
||||||
None: DeviceInfo(
|
None: DeviceInfo(
|
||||||
|
@ -105,21 +112,11 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
|
|
||||||
with patch(
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
cancel_coordinator = coordinator.async_start()
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
entity_key = PassiveBluetoothEntityKey("temperature", None)
|
entity_key = PassiveBluetoothEntityKey("temperature", None)
|
||||||
entity_key_events = []
|
entity_key_events = []
|
||||||
|
@ -149,7 +146,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||||
mock_add_entities,
|
mock_add_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
# Each listener should receive the same data
|
# Each listener should receive the same data
|
||||||
# since both match
|
# since both match
|
||||||
|
@ -159,7 +156,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||||
# There should be 4 calls to create entities
|
# There should be 4 calls to create entities
|
||||||
assert len(mock_entity.mock_calls) == 2
|
assert len(mock_entity.mock_calls) == 2
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
# Each listener should receive the same data
|
# Each listener should receive the same data
|
||||||
# since both match
|
# since both match
|
||||||
|
@ -174,7 +171,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
||||||
cancel_listener()
|
cancel_listener()
|
||||||
cancel_async_add_entities_listener()
|
cancel_async_add_entities_listener()
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
|
|
||||||
# Each listener should not trigger any more now
|
# Each listener should not trigger any more now
|
||||||
# that they were cancelled
|
# that they were cancelled
|
||||||
|
@ -217,20 +214,11 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
mock_entity = MagicMock()
|
mock_entity = MagicMock()
|
||||||
mock_add_entities = MagicMock()
|
mock_add_entities = MagicMock()
|
||||||
|
@ -242,38 +230,49 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
assert processor.available is False
|
assert processor.available is False
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
now = time.monotonic()
|
||||||
|
service_info_at_time = BluetoothServiceInfoBleak(
|
||||||
|
name="Generic",
|
||||||
|
address="aa:bb:cc:dd:ee:ff",
|
||||||
|
rssi=-95,
|
||||||
|
manufacturer_data={
|
||||||
|
1: b"\x01\x01\x01\x01\x01\x01\x01\x01",
|
||||||
|
},
|
||||||
|
service_data={},
|
||||||
|
service_uuids=[],
|
||||||
|
source="local",
|
||||||
|
time=now,
|
||||||
|
device=MagicMock(),
|
||||||
|
advertisement=MagicMock(),
|
||||||
|
connectable=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
inject_bluetooth_service_info_bleak(hass, service_info_at_time)
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
with patch_all_discovered_devices(
|
with patch_all_discovered_devices([MagicMock(address="44:44:33:11:23:45")]):
|
||||||
[MagicMock(address="44:44:33:11:23:45")]
|
|
||||||
), patch_history({"aa:bb:cc:dd:ee:ff": MagicMock()}), patch_connectable_history(
|
|
||||||
{"aa:bb:cc:dd:ee:ff": MagicMock()},
|
|
||||||
):
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
assert processor.available is False
|
assert processor.available is False
|
||||||
|
assert coordinator.last_seen == service_info_at_time.time
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info_bleak(hass, service_info_at_time)
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
|
|
||||||
with patch_all_discovered_devices(
|
with patch_all_discovered_devices([MagicMock(address="44:44:33:11:23:45")]):
|
||||||
[MagicMock(address="44:44:33:11:23:45")]
|
|
||||||
), patch_history({"aa:bb:cc:dd:ee:ff": MagicMock()}), patch_connectable_history(
|
|
||||||
{"aa:bb:cc:dd:ee:ff": MagicMock()},
|
|
||||||
):
|
|
||||||
async_fire_time_changed(
|
async_fire_time_changed(
|
||||||
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
hass, dt_util.utcnow() + timedelta(seconds=UNAVAILABLE_TRACK_SECONDS)
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert coordinator.available is False
|
assert coordinator.available is False
|
||||||
assert processor.available is False
|
assert processor.available is False
|
||||||
|
assert coordinator.last_seen == service_info_at_time.time
|
||||||
|
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
cancel_coordinator()
|
cancel_coordinator()
|
||||||
|
@ -304,21 +303,11 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
|
|
||||||
with patch(
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
cancel_coordinator = coordinator.async_start()
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
all_events = []
|
all_events = []
|
||||||
|
|
||||||
|
@ -330,13 +319,13 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
||||||
_all_listener,
|
_all_listener,
|
||||||
)
|
)
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert len(all_events) == 1
|
assert len(all_events) == 1
|
||||||
|
|
||||||
hass.state = CoreState.stopping
|
hass.state = CoreState.stopping
|
||||||
|
|
||||||
# We should stop processing events once hass is stopping
|
# We should stop processing events once hass is stopping
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert len(all_events) == 1
|
assert len(all_events) == 1
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
cancel_coordinator()
|
cancel_coordinator()
|
||||||
|
@ -361,7 +350,7 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
||||||
"""Generate mock data."""
|
"""Generate mock data."""
|
||||||
nonlocal run_count
|
nonlocal run_count
|
||||||
run_count += 1
|
run_count += 1
|
||||||
if run_count == 2:
|
if run_count == 1:
|
||||||
raise Exception("Test exception")
|
raise Exception("Test exception")
|
||||||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||||
|
|
||||||
|
@ -390,7 +379,7 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
|
|
||||||
# We should go unavailable once we get an exception
|
# We should go unavailable once we get an exception
|
||||||
|
@ -424,7 +413,7 @@ async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
||||||
"""Generate mock data."""
|
"""Generate mock data."""
|
||||||
nonlocal run_count
|
nonlocal run_count
|
||||||
run_count += 1
|
run_count += 1
|
||||||
if run_count == 2:
|
if run_count == 1:
|
||||||
return "bad_data"
|
return "bad_data"
|
||||||
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
return GENERIC_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||||
|
|
||||||
|
@ -453,7 +442,7 @@ async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
|
|
||||||
# We should go unavailable once we get bad data
|
# We should go unavailable once we get bad data
|
||||||
|
@ -788,20 +777,11 @@ async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
coordinator.async_register_processor(processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
|
@ -812,19 +792,19 @@ async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
||||||
mock_add_entities,
|
mock_add_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# First call with just the remote sensor entities results in them being added
|
# First call with just the remote sensor entities results in them being added
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Second call with just the remote sensor entities does not add them again
|
# Second call with just the remote sensor entities does not add them again
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Third call with primary and remote sensor entities adds the primary sensor entities
|
# Third call with primary and remote sensor entities adds the primary sensor entities
|
||||||
assert len(mock_add_entities.mock_calls) == 2
|
assert len(mock_add_entities.mock_calls) == 2
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# Forth call with both primary and remote sensor entities does not add them again
|
# Forth call with both primary and remote sensor entities does not add them again
|
||||||
assert len(mock_add_entities.mock_calls) == 2
|
assert len(mock_add_entities.mock_calls) == 2
|
||||||
|
|
||||||
|
@ -908,20 +888,11 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
coordinator.async_register_processor(processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
mock_add_entities = MagicMock()
|
mock_add_entities = MagicMock()
|
||||||
|
|
||||||
|
@ -930,11 +901,11 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
||||||
mock_add_entities,
|
mock_add_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
saved_callback(NO_DEVICES_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, NO_DEVICES_BLUETOOTH_SERVICE_INFO)
|
||||||
# First call with just the remote sensor entities results in them being added
|
# First call with just the remote sensor entities results in them being added
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
|
|
||||||
saved_callback(NO_DEVICES_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, NO_DEVICES_BLUETOOTH_SERVICE_INFO)
|
||||||
# Second call with just the remote sensor entities does not add them again
|
# Second call with just the remote sensor entities does not add them again
|
||||||
assert len(mock_add_entities.mock_calls) == 1
|
assert len(mock_add_entities.mock_calls) == 1
|
||||||
|
|
||||||
|
@ -982,20 +953,11 @@ async def test_passive_bluetooth_entity_with_entity_platform(
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
coordinator.async_register_processor(processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
PassiveBluetoothProcessorEntity,
|
PassiveBluetoothProcessorEntity,
|
||||||
|
@ -1003,9 +965,9 @@ async def test_passive_bluetooth_entity_with_entity_platform(
|
||||||
entity_platform.async_add_entities(entities)
|
entity_platform.async_add_entities(entities)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
saved_callback(NO_DEVICES_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, NO_DEVICES_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
saved_callback(NO_DEVICES_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, NO_DEVICES_BLUETOOTH_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
hass.states.get("test_domain.test_platform_aa_bb_cc_dd_ee_ff_temperature")
|
hass.states.get("test_domain.test_platform_aa_bb_cc_dd_ee_ff_temperature")
|
||||||
|
@ -1079,12 +1041,6 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
binary_sensor_processor = PassiveBluetoothDataProcessor(
|
binary_sensor_processor = PassiveBluetoothDataProcessor(
|
||||||
lambda service_info: BINARY_SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE
|
lambda service_info: BINARY_SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||||
|
@ -1093,13 +1049,9 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
||||||
lambda service_info: SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE
|
lambda service_info: SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
coordinator.async_register_processor(binary_sensor_processor)
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
coordinator.async_register_processor(sesnor_processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
coordinator.async_register_processor(binary_sensor_processor)
|
|
||||||
coordinator.async_register_processor(sesnor_processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
binary_sensor_processor.async_add_listener(MagicMock())
|
binary_sensor_processor.async_add_listener(MagicMock())
|
||||||
sesnor_processor.async_add_listener(MagicMock())
|
sesnor_processor.async_add_listener(MagicMock())
|
||||||
|
@ -1116,7 +1068,7 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
||||||
mock_add_binary_sensor_entities,
|
mock_add_binary_sensor_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
# First call with just the remote sensor entities results in them being added
|
# First call with just the remote sensor entities results in them being added
|
||||||
assert len(mock_add_binary_sensor_entities.mock_calls) == 1
|
assert len(mock_add_binary_sensor_entities.mock_calls) == 1
|
||||||
assert len(mock_add_sensor_entities.mock_calls) == 1
|
assert len(mock_add_sensor_entities.mock_calls) == 1
|
||||||
|
@ -1193,33 +1145,24 @@ async def test_exception_from_coordinator_update_method(
|
||||||
_mock_update_method,
|
_mock_update_method,
|
||||||
)
|
)
|
||||||
assert coordinator.available is False # no data yet
|
assert coordinator.available is False # no data yet
|
||||||
saved_callback = None
|
|
||||||
|
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
processor = PassiveBluetoothDataProcessor(_async_generate_mock_data)
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
_async_register_callback,
|
cancel_coordinator = coordinator.async_start()
|
||||||
):
|
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
|
||||||
cancel_coordinator = coordinator.async_start()
|
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
|
|
||||||
# We should go unavailable once we get an exception
|
# We should go unavailable once we get an exception
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert "Test exception" in caplog.text
|
assert "Test exception" in caplog.text
|
||||||
assert processor.available is False
|
assert processor.available is False
|
||||||
|
|
||||||
# We should go available again once we get data again
|
# We should go available again once we get data again
|
||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GENERIC_BLUETOOTH_SERVICE_INFO)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
cancel_coordinator()
|
cancel_coordinator()
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
"""Test BTHome binary sensors."""
|
"""Test BTHome binary sensors."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.bthome.const import DOMAIN
|
from homeassistant.components.bthome.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF, STATE_ON
|
from homeassistant.const import ATTR_FRIENDLY_NAME, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
from . import make_advertisement
|
from . import make_advertisement
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -81,25 +80,14 @@ async def test_binary_sensors(
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
saved_callback(
|
inject_bluetooth_service_info(
|
||||||
|
hass,
|
||||||
advertisement,
|
advertisement,
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
"""Test the BTHome sensors."""
|
"""Test the BTHome sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.bthome.const import DOMAIN
|
from homeassistant.components.bthome.const import DOMAIN
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -12,6 +10,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import make_advertisement, make_encrypted_advertisement
|
from . import make_advertisement, make_encrypted_advertisement
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -340,25 +339,14 @@ async def test_sensors(
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
saved_callback(
|
inject_bluetooth_service_info(
|
||||||
|
hass,
|
||||||
advertisement,
|
advertisement,
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == len(result)
|
assert len(hass.states.async_all()) == len(result)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the Govee BLE sensors."""
|
"""Test the Govee BLE sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.govee_ble.const import DOMAIN
|
from homeassistant.components.govee_ble.const import DOMAIN
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import GVH5075_SERVICE_INFO
|
from . import GVH5075_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(GVH5075_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, GVH5075_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 3
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the INKBIRD config flow."""
|
"""Test the INKBIRD config flow."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.inkbird.const import DOMAIN
|
from homeassistant.components.inkbird.const import DOMAIN
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import SPS_SERVICE_INFO
|
from . import SPS_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(SPS_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, SPS_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 3
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the Moat sensors."""
|
"""Test the Moat sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.moat.const import DOMAIN
|
from homeassistant.components.moat.const import DOMAIN
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,32 +8,22 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import MOAT_S2_SERVICE_INFO
|
from . import MOAT_S2_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
"""Test setting up creates the sensors."""
|
"""Test setting up creates the sensors."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="61DE521B-F0BF-9F44-64D4-75BBE1738105",
|
unique_id="aa:bb:cc:dd:ee:ff",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(MOAT_S2_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, MOAT_S2_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 4
|
assert len(hass.states.async_all()) == 4
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
"""Test the Qingping binary sensors."""
|
"""Test the Qingping binary sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.qingping.const import DOMAIN
|
from homeassistant.components.qingping.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||||
|
|
||||||
from . import LIGHT_AND_SIGNAL_SERVICE_INFO
|
from . import LIGHT_AND_SIGNAL_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensors(hass):
|
async def test_binary_sensors(hass):
|
||||||
|
@ -19,22 +18,11 @@ async def test_binary_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all("binary_sensor")) == 0
|
assert len(hass.states.async_all("binary_sensor")) == 0
|
||||||
saved_callback(LIGHT_AND_SIGNAL_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, LIGHT_AND_SIGNAL_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all("binary_sensor")) == 1
|
assert len(hass.states.async_all("binary_sensor")) == 1
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the Qingping sensors."""
|
"""Test the Qingping sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.qingping.const import DOMAIN
|
from homeassistant.components.qingping.const import DOMAIN
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import LIGHT_AND_SIGNAL_SERVICE_INFO
|
from . import LIGHT_AND_SIGNAL_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all("sensor")) == 0
|
assert len(hass.states.async_all("sensor")) == 0
|
||||||
saved_callback(LIGHT_AND_SIGNAL_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, LIGHT_AND_SIGNAL_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all("sensor")) == 1
|
assert len(hass.states.async_all("sensor")) == 1
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the SensorPro sensors."""
|
"""Test the SensorPro sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.sensorpro.const import DOMAIN
|
from homeassistant.components.sensorpro.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import SENSORPRO_SERVICE_INFO
|
from . import SENSORPRO_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all("sensor")) == 0
|
assert len(hass.states.async_all("sensor")) == 0
|
||||||
saved_callback(SENSORPRO_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, SENSORPRO_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all("sensor")) == 4
|
assert len(hass.states.async_all("sensor")) == 4
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
"""Test the SensorPush config flow."""
|
"""Test the SensorPush sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.sensorpush.const import DOMAIN
|
from homeassistant.components.sensorpush.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,32 +7,22 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import HTPWX_SERVICE_INFO
|
from . import HTPWX_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
"""Test setting up creates the sensors."""
|
"""Test setting up creates the sensors."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="61DE521B-F0BF-9F44-64D4-75BBE1738105",
|
unique_id="4125DDBA-2774-4851-9889-6AADDD4CAC3D",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(HTPWX_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, HTPWX_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 3
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
"""Test the ThermoBeacon sensors."""
|
"""Test the ThermoBeacon sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.thermobeacon.const import DOMAIN
|
from homeassistant.components.thermobeacon.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import THERMOBEACON_SERVICE_INFO
|
from . import THERMOBEACON_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -20,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all("sensor")) == 0
|
assert len(hass.states.async_all("sensor")) == 0
|
||||||
saved_callback(THERMOBEACON_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, THERMOBEACON_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all("sensor")) == 4
|
assert len(hass.states.async_all("sensor")) == 4
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
"""Test the ThermoPro config flow."""
|
"""Test the ThermoPro config flow."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.thermopro.const import DOMAIN
|
from homeassistant.components.thermopro.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -10,32 +7,22 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import TP357_SERVICE_INFO
|
from . import TP357_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
"""Test setting up creates the sensors."""
|
"""Test setting up creates the sensors."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="61DE521B-F0BF-9F44-64D4-75BBE1738105",
|
unique_id="4125DDBA-2774-4851-9889-6AADDD4CAC3D",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(TP357_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info(hass, TP357_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 2
|
assert len(hass.states.async_all()) == 2
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothCallback, BluetoothChange
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.tilt_ble.const import DOMAIN
|
from homeassistant.components.tilt_ble.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -13,33 +10,22 @@ from homeassistant.core import HomeAssistant
|
||||||
from . import TILT_GREEN_SERVICE_INFO
|
from . import TILT_GREEN_SERVICE_INFO
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass: HomeAssistant):
|
async def test_sensors(hass: HomeAssistant):
|
||||||
"""Test setting up creates the sensors."""
|
"""Test setting up creates the sensors."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="61DE521B-F0BF-9F44-64D4-75BBE1738105",
|
unique_id="F6:0F:28:F2:1F:CB",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback: BluetoothCallback | None = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
assert saved_callback is not None
|
inject_bluetooth_service_info(hass, TILT_GREEN_SERVICE_INFO)
|
||||||
saved_callback(TILT_GREEN_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 2
|
assert len(hass.states.async_all()) == 2
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
"""Test Xiaomi binary sensors."""
|
"""Test Xiaomi binary sensors."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import BluetoothChange
|
|
||||||
from homeassistant.components.xiaomi_ble.const import DOMAIN
|
from homeassistant.components.xiaomi_ble.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||||
|
|
||||||
from . import make_advertisement
|
from . import make_advertisement
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
|
||||||
|
|
||||||
|
|
||||||
async def test_smoke_sensor(hass):
|
async def test_smoke_sensor(hass):
|
||||||
|
@ -20,27 +18,16 @@ async def test_smoke_sensor(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"54:EF:44:E3:9C:BC",
|
"54:EF:44:E3:9C:BC",
|
||||||
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90",
|
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90",
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 1
|
||||||
|
@ -62,29 +49,18 @@ async def test_moisture(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
# WARNING: This test data is synthetic, rather than captured from a real device
|
# WARNING: This test data is synthetic, rather than captured from a real device
|
||||||
# obj type is 0x1014, payload len is 0x2 and payload is 0xf400
|
# obj type is 0x1014, payload len is 0x2 and payload is 0xf400
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x14\x10\x02\xf4\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x14\x10\x02\xf4\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
"""Test the Xiaomi config flow."""
|
"""Test the Xiaomi config flow."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from homeassistant.components.bluetooth import (
|
|
||||||
BluetoothChange,
|
|
||||||
async_get_advertisement_callback,
|
|
||||||
)
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
from homeassistant.components.xiaomi_ble.const import DOMAIN
|
from homeassistant.components.xiaomi_ble.const import DOMAIN
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
|
@ -13,6 +8,7 @@ from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
from . import MMC_T201_1_SERVICE_INFO, make_advertisement
|
from . import MMC_T201_1_SERVICE_INFO, make_advertisement
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
|
||||||
|
|
||||||
|
|
||||||
async def test_sensors(hass):
|
async def test_sensors(hass):
|
||||||
|
@ -23,22 +19,11 @@ async def test_sensors(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(MMC_T201_1_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
inject_bluetooth_service_info_bleak(hass, MMC_T201_1_SERVICE_INFO)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 2
|
assert len(hass.states.async_all()) == 2
|
||||||
|
|
||||||
|
@ -63,29 +48,18 @@ async def test_xiaomi_formaldeyhde(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
# WARNING: This test data is synthetic, rather than captured from a real device
|
# WARNING: This test data is synthetic, rather than captured from a real device
|
||||||
# obj type is 0x1010, payload len is 0x2 and payload is 0xf400
|
# obj type is 0x1010, payload len is 0x2 and payload is 0xf400
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x10\x10\x02\xf4\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x10\x10\x02\xf4\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -110,29 +84,18 @@ async def test_xiaomi_consumable(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
# WARNING: This test data is synthetic, rather than captured from a real device
|
# WARNING: This test data is synthetic, rather than captured from a real device
|
||||||
# obj type is 0x1310, payload len is 0x2 and payload is 0x6000
|
# obj type is 0x1310, payload len is 0x2 and payload is 0x6000
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x13\x10\x02\x60\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x13\x10\x02\x60\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -157,29 +120,16 @@ async def test_xiaomi_battery_voltage(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
|
||||||
|
|
||||||
# WARNING: This test data is synthetic, rather than captured from a real device
|
# WARNING: This test data is synthetic, rather than captured from a real device
|
||||||
# obj type is 0x0a10, payload len is 0x2 and payload is 0x6400
|
# obj type is 0x0a10, payload len is 0x2 and payload is 0x6400
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x0a\x10\x02\x64\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x0a\x10\x02\x64\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -211,44 +161,33 @@ async def test_xiaomi_HHCCJCY01(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02"
|
"C4:7C:8D:6A:3E:7A", b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@"
|
"C4:7C:8D:6A:3E:7A", b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A", b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00"
|
"C4:7C:8D:6A:3E:7A", b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00"
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 5
|
assert len(hass.states.async_all()) == 5
|
||||||
|
@ -296,56 +235,45 @@ async def test_xiaomi_HHCCJCY01_not_connectable(hass):
|
||||||
"""This device has multiple advertisements before all sensors are visible but not connectable."""
|
"""This device has multiple advertisements before all sensors are visible but not connectable."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="C4:7C:8D:6A:3E:7B",
|
unique_id="C4:7C:8D:6A:3E:7A",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00",
|
b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02",
|
b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@",
|
b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00",
|
b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 4
|
assert len(hass.states.async_all()) == 4
|
||||||
|
@ -392,34 +320,36 @@ async def test_xiaomi_HHCCJCY01_only_some_sources_connectable(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = async_get_advertisement_callback(hass)
|
|
||||||
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00",
|
b"q \x98\x00fz>j\x8d|\xc4\r\x07\x10\x03\x00\x00\x00",
|
||||||
connectable=True,
|
connectable=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02",
|
b"q \x98\x00hz>j\x8d|\xc4\r\t\x10\x02W\x02",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@",
|
b"q \x98\x00Gz>j\x8d|\xc4\r\x08\x10\x01@",
|
||||||
connectable=False,
|
connectable=False,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"C4:7C:8D:6A:3E:7A",
|
"C4:7C:8D:6A:3E:7A",
|
||||||
b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00",
|
b"q \x98\x00iz>j\x8d|\xc4\r\x04\x10\x02\xf4\x00",
|
||||||
|
@ -477,27 +407,16 @@ async def test_xiaomi_CGDK2(hass):
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
saved_callback = None
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
def _async_register_callback(_hass, _callback, _matcher, _mode):
|
|
||||||
nonlocal saved_callback
|
|
||||||
saved_callback = _callback
|
|
||||||
return lambda: None
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
|
|
||||||
_async_register_callback,
|
|
||||||
):
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
saved_callback(
|
inject_bluetooth_service_info_bleak(
|
||||||
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"58:2D:34:12:20:89",
|
"58:2D:34:12:20:89",
|
||||||
b"XXo\x06\x07\x89 \x124-X_\x17m\xd5O\x02\x00\x00/\xa4S\xfa",
|
b"XXo\x06\x07\x89 \x124-X_\x17m\xd5O\x02\x00\x00/\xa4S\xfa",
|
||||||
),
|
),
|
||||||
BluetoothChange.ADVERTISEMENT,
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue