diff --git a/homeassistant/components/bluetooth/advertisement_tracker.py b/homeassistant/components/bluetooth/advertisement_tracker.py index b6a70e32865..f17bcf938f5 100644 --- a/homeassistant/components/bluetooth/advertisement_tracker.py +++ b/homeassistant/components/bluetooth/advertisement_tracker.py @@ -18,11 +18,12 @@ TRACKER_BUFFERING_WOBBLE_SECONDS = 5 class AdvertisementTracker: """Tracker to determine the interval that a device is advertising.""" - __slots__ = ("intervals", "sources", "_timings") + __slots__ = ("intervals", "fallback_intervals", "sources", "_timings") def __init__(self) -> None: """Initialize the tracker.""" self.intervals: dict[str, float] = {} + self.fallback_intervals: dict[str, float] = {} self.sources: dict[str, str] = {} self._timings: dict[str, list[float]] = {} @@ -31,6 +32,7 @@ class AdvertisementTracker: """Return diagnostics.""" return { "intervals": self.intervals, + "fallback_intervals": self.fallback_intervals, "sources": self.sources, "timings": self._timings, } @@ -67,6 +69,11 @@ class AdvertisementTracker: self.sources.pop(address, None) self._timings.pop(address, None) + @callback + def async_remove_fallback_interval(self, address: str) -> None: + """Remove fallback interval.""" + self.fallback_intervals.pop(address, None) + @callback def async_remove_source(self, source: str) -> None: """Remove the tracker.""" diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index d69558fe7fd..34edccaf4ab 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -109,6 +109,7 @@ class BluetoothManager: "_cancel_logging_listener", "_advertisement_tracker", "_fallback_intervals", + "_intervals", "_unavailable_callbacks", "_connectable_unavailable_callbacks", "_callback_index", @@ -140,7 +141,8 @@ class BluetoothManager: self._cancel_logging_listener: CALLBACK_TYPE | None = None self._advertisement_tracker = AdvertisementTracker() - self._fallback_intervals: dict[str, float] = {} + self._fallback_intervals = self._advertisement_tracker.fallback_intervals + self._intervals = self._advertisement_tracker.intervals self._unavailable_callbacks: dict[ str, list[Callable[[BluetoothServiceInfoBleak], None]] @@ -359,7 +361,7 @@ class BluetoothManager: # The second loop (connectable=False) is responsible for removing # the device from all the interval tracking since it is no longer # available for both connectable and non-connectable - self._fallback_intervals.pop(address, None) + tracker.async_remove_fallback_interval(address) tracker.async_remove_address(address) self._integration_matcher.async_clear_address(address) self._async_dismiss_discoveries(address) @@ -390,7 +392,7 @@ class BluetoothManager: ) -> bool: """Prefer previous advertisement from a different source if it is better.""" if new.time - old.time > ( - stale_seconds := self._advertisement_tracker.intervals.get( + stale_seconds := self._intervals.get( new.address, self._fallback_intervals.get( new.address, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS @@ -791,7 +793,7 @@ class BluetoothManager: @hass_callback def async_get_learned_advertising_interval(self, address: str) -> float | None: """Get the learned advertising interval for a MAC address.""" - return self._advertisement_tracker.intervals.get(address) + return self._intervals.get(address) @hass_callback def async_get_fallback_availability_interval(self, address: str) -> float | None: diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index 765e2a9a612..0e8b2b54f06 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -157,6 +157,7 @@ async def test_diagnostics( }, "advertisement_tracker": { "intervals": {}, + "fallback_intervals": {}, "sources": {}, "timings": {}, }, @@ -328,6 +329,7 @@ async def test_diagnostics_macos( }, "advertisement_tracker": { "intervals": {}, + "fallback_intervals": {}, "sources": {"44:44:33:11:23:45": "local"}, "timings": {"44:44:33:11:23:45": [ANY]}, }, @@ -520,6 +522,7 @@ async def test_diagnostics_remote_adapter( }, "advertisement_tracker": { "intervals": {}, + "fallback_intervals": {}, "sources": {"44:44:33:11:23:45": "esp32"}, "timings": {"44:44:33:11:23:45": [ANY]}, },