Fix timestamps for bluetooth scanners that bundle advertisements (#94511)
#94138 added support for raw/bundled advertisements. We should use the same monotonic time for all advertisements in the bundle if not time is passed, or calculate the timestamp and pass it if its known
This commit is contained in:
parent
22dfa8797f
commit
2a5ffa9a5b
10 changed files with 42 additions and 8 deletions
|
@ -76,7 +76,7 @@ from .models import (
|
|||
BluetoothScanningMode,
|
||||
HaBluetoothConnector,
|
||||
)
|
||||
from .scanner import HaScanner, ScannerStartError
|
||||
from .scanner import MONOTONIC_TIME, HaScanner, ScannerStartError
|
||||
from .storage import BluetoothStorage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -108,6 +108,7 @@ __all__ = [
|
|||
"HaBluetoothConnector",
|
||||
"SOURCE_LOCAL",
|
||||
"FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS",
|
||||
"MONOTONIC_TIME",
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
|
|
@ -299,10 +299,10 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||
manufacturer_data: dict[int, bytes],
|
||||
tx_power: int | None,
|
||||
details: dict[Any, Any],
|
||||
advertisement_monotonic_time: float,
|
||||
) -> None:
|
||||
"""Call the registered callback."""
|
||||
now = MONOTONIC_TIME()
|
||||
self._last_detection = now
|
||||
self._last_detection = advertisement_monotonic_time
|
||||
if prev_discovery := self._discovered_device_advertisement_datas.get(address):
|
||||
# Merge the new data with the old data
|
||||
# to function the same as BlueZ which
|
||||
|
@ -365,7 +365,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||
device,
|
||||
advertisement_data,
|
||||
)
|
||||
self._discovered_device_timestamps[address] = now
|
||||
self._discovered_device_timestamps[address] = advertisement_monotonic_time
|
||||
self._new_info_callback(
|
||||
BluetoothServiceInfoBleak(
|
||||
name=local_name or address,
|
||||
|
@ -378,7 +378,7 @@ class BaseHaRemoteScanner(BaseHaScanner):
|
|||
device=device,
|
||||
advertisement=advertisement_data,
|
||||
connectable=self.connectable,
|
||||
time=now,
|
||||
time=advertisement_monotonic_time,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
from aioesphomeapi import BluetoothLEAdvertisement, BluetoothLERawAdvertisement
|
||||
from bluetooth_data_tools import int_to_bluetooth_address, parse_advertisement_data
|
||||
|
||||
from homeassistant.components.bluetooth import BaseHaRemoteScanner
|
||||
from homeassistant.components.bluetooth import MONOTONIC_TIME, BaseHaRemoteScanner
|
||||
from homeassistant.core import callback
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@ class ESPHomeScanner(BaseHaRemoteScanner):
|
|||
adv.manufacturer_data,
|
||||
None,
|
||||
{"address_type": adv.address_type},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
@callback
|
||||
|
@ -31,6 +32,7 @@ class ESPHomeScanner(BaseHaRemoteScanner):
|
|||
self, advertisements: list[BluetoothLERawAdvertisement]
|
||||
) -> None:
|
||||
"""Call the registered callback."""
|
||||
now = MONOTONIC_TIME()
|
||||
for adv in advertisements:
|
||||
parsed = parse_advertisement_data((adv.data,))
|
||||
self._async_on_advertisement(
|
||||
|
@ -42,4 +44,5 @@ class ESPHomeScanner(BaseHaRemoteScanner):
|
|||
parsed.manufacturer_data,
|
||||
None,
|
||||
{"address_type": adv.address_type},
|
||||
now,
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@ from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
|||
|
||||
from homeassistant.components.bluetooth import (
|
||||
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
async_get_advertisement_callback,
|
||||
async_register_scanner,
|
||||
|
@ -47,6 +48,7 @@ class RuuviGatewayScanner(BaseHaRemoteScanner):
|
|||
@callback
|
||||
def _async_handle_new_data(self) -> None:
|
||||
now = time.time()
|
||||
monotonic_now = MONOTONIC_TIME()
|
||||
for tag_data in self.coordinator.data:
|
||||
data_age_seconds = now - tag_data.timestamp # Both are Unix time
|
||||
if data_age_seconds > FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS:
|
||||
|
@ -62,6 +64,7 @@ class RuuviGatewayScanner(BaseHaRemoteScanner):
|
|||
manufacturer_data=anno.manufacturer_data,
|
||||
tx_power=anno.tx_power,
|
||||
details={},
|
||||
advertisement_monotonic_time=monotonic_now - data_age_seconds,
|
||||
)
|
||||
|
||||
@callback
|
||||
|
|
|
@ -6,7 +6,7 @@ from typing import Any
|
|||
from aioshelly.ble import parse_ble_scan_result_event
|
||||
from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT, BLE_SCAN_RESULT_VERSION
|
||||
|
||||
from homeassistant.components.bluetooth import BaseHaRemoteScanner
|
||||
from homeassistant.components.bluetooth import MONOTONIC_TIME, BaseHaRemoteScanner
|
||||
from homeassistant.core import callback
|
||||
|
||||
from ..const import LOGGER
|
||||
|
@ -44,4 +44,5 @@ class ShellyBLEScanner(BaseHaRemoteScanner):
|
|||
parsed.manufacturer_data,
|
||||
parsed.tx_power,
|
||||
{},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
"""Tests for the Bluetooth integration API."""
|
||||
import time
|
||||
|
||||
from bleak.backends.scanner import AdvertisementData, BLEDevice
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth import (
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
BaseHaScanner,
|
||||
HaBluetoothConnector,
|
||||
|
@ -31,6 +35,11 @@ async def test_scanner_by_source(hass: HomeAssistant, enable_bluetooth: None) ->
|
|||
assert async_scanner_by_source(hass, "hci2") is None
|
||||
|
||||
|
||||
async def test_monotonic_time() -> None:
|
||||
"""Test monotonic time."""
|
||||
assert MONOTONIC_TIME() == pytest.approx(time.monotonic(), abs=0.1)
|
||||
|
||||
|
||||
async def test_async_scanner_devices_by_address_connectable(
|
||||
hass: HomeAssistant, enable_bluetooth: None
|
||||
) -> None:
|
||||
|
@ -51,6 +60,7 @@ async def test_async_scanner_devices_by_address_connectable(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
|
|
@ -12,6 +12,7 @@ import pytest
|
|||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth import (
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
HaBluetoothConnector,
|
||||
storage,
|
||||
|
@ -84,6 +85,7 @@ async def test_remote_scanner(hass: HomeAssistant, enable_bluetooth: None) -> No
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
@ -158,6 +160,7 @@ async def test_remote_scanner_expires_connectable(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
@ -232,6 +235,7 @@ async def test_remote_scanner_expires_non_connectable(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
@ -329,6 +333,7 @@ async def test_base_scanner_connecting_behavior(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
@ -452,6 +457,7 @@ async def test_device_with_ten_minute_advertising_interval(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = manager.scanner_adv_received
|
||||
|
|
|
@ -5,7 +5,11 @@ from bleak.backends.scanner import AdvertisementData, BLEDevice
|
|||
from bluetooth_adapters import DEFAULT_ADDRESS
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth import BaseHaRemoteScanner, HaBluetoothConnector
|
||||
from homeassistant.components.bluetooth import (
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
HaBluetoothConnector,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import (
|
||||
|
@ -450,6 +454,7 @@ async def test_diagnostics_remote_adapter(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
with patch(
|
||||
|
|
|
@ -11,6 +11,7 @@ import pytest
|
|||
|
||||
from homeassistant.components import bluetooth
|
||||
from homeassistant.components.bluetooth import (
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
BluetoothChange,
|
||||
BluetoothScanningMode,
|
||||
|
@ -711,6 +712,7 @@ async def test_goes_unavailable_connectable_only_and_recovers(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
new_info_callback = async_get_advertisement_callback(hass)
|
||||
|
@ -883,6 +885,7 @@ async def test_goes_unavailable_dismisses_discovery_and_makes_discoverable(
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
{"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
def clear_all_devices(self) -> None:
|
||||
|
|
|
@ -10,6 +10,7 @@ from bleak.backends.scanner import AdvertisementData
|
|||
import pytest
|
||||
|
||||
from homeassistant.components.bluetooth import (
|
||||
MONOTONIC_TIME,
|
||||
BaseHaRemoteScanner,
|
||||
BluetoothServiceInfoBleak,
|
||||
HaBluetoothConnector,
|
||||
|
@ -59,6 +60,7 @@ class FakeScanner(BaseHaRemoteScanner):
|
|||
advertisement_data.manufacturer_data,
|
||||
advertisement_data.tx_power,
|
||||
device.details | {"scanner_specific_data": "test"},
|
||||
MONOTONIC_TIME(),
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue