Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump bleak to 0.19.0 #80349

Merged
merged 35 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix diagnostics
  • Loading branch information
bdraco committed Oct 14, 2022
commit e12b5910983732ae6cdafb94e7e1818e674f7732
92 changes: 51 additions & 41 deletions homeassistant/components/bluetooth/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ def __init__(
self._integration_matcher = integration_matcher
self._cancel_unavailable_tracking: CALLBACK_TYPE | None = None

self._advertisement_tracker = AdvertisementTracker()
self._connectable_advertisement_tracker = AdvertisementTracker()
self._non_connectable_advertisement_tracker = AdvertisementTracker()

self._unavailable_callbacks: dict[
str, list[Callable[[BluetoothServiceInfoBleak], None]]
Expand All @@ -121,9 +122,13 @@ def __init__(
self._bleak_callbacks: list[
tuple[AdvertisementDataCallback, dict[str, set[str]]]
] = []
self._history: dict[str, BluetoothServiceInfoBleak] = {}

self._non_connectable_history: dict[str, BluetoothServiceInfoBleak] = {}
self._connectable_history: dict[str, BluetoothServiceInfoBleak] = {}
self._all_history: dict[str, BluetoothServiceInfoBleak] = {}

self._non_connectable_scanners: list[BaseHaScanner] = []

self._connectable_scanners: list[BaseHaScanner] = []
self._adapters: dict[str, AdapterDetails] = {}

Expand Down Expand Up @@ -155,10 +160,12 @@ async def async_diagnostics(self) -> dict[str, Any]:
service_info.as_dict()
for service_info in self._connectable_history.values()
],
"history": [
service_info.as_dict() for service_info in self._history.values()
"non_connectable_history": [
service_info.as_dict()
for service_info in self._non_connectable_history.values()
],
"advertisement_tracker": self._advertisement_tracker.async_diagnostics(),
"connectable_advertisement_tracker": self._connectable_advertisement_tracker.async_diagnostics(),
"non_connectable_advertisement_tracker": self._non_connectable_advertisement_tracker.async_diagnostics(),
}

def _find_adapter_by_address(self, address: str) -> str | None:
Expand Down Expand Up @@ -189,8 +196,8 @@ async def async_setup(self) -> None:
# Everything is connectable so it fall into both
# buckets since the host system can only provide
# connectable devices
self._history = history.copy()
self._connectable_history = history.copy()
self._all_history = history.copy()
self.async_setup_unavailable_tracking()

@hass_callback
Expand Down Expand Up @@ -252,15 +259,14 @@ def _async_check_unavailable(self, now: datetime) -> None:
"""Watch for unavailable devices and cleanup state history."""
monotonic_now = MONOTONIC_TIME()
connectable_history = self._connectable_history
all_history = self._history
removed_addresses: set[str] = set()
all_history = self._all_history

for connectable in (True, False):
unavailable_callbacks = self._get_unavailable_callbacks_by_type(connectable)
intervals = self._advertisement_tracker.intervals
tracker = self._get_advertisement_tracker_by_type(connectable)
intervals = tracker.intervals
history = connectable_history if connectable else all_history
history_set = set(history)
disappeared = history_set.difference(
disappeared = set(history).difference(
self._async_all_discovered_addresses(connectable)
)
for address in disappeared:
Expand All @@ -276,7 +282,7 @@ def _async_check_unavailable(self, now: datetime) -> None:
continue

service_info = history.pop(address)
removed_addresses.add(address)
tracker.async_remove_address(address)

if not (callbacks := unavailable_callbacks.get(address)):
continue
Expand All @@ -287,18 +293,15 @@ def _async_check_unavailable(self, now: datetime) -> None:
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Error in unavailable callback")

# If we removed the device from both the connectable history
# and all history then we can remove it from the advertisement tracker
for address in removed_addresses:
if address not in connectable_history and address not in all_history:
self._advertisement_tracker.async_remove_address(address)

def _prefer_previous_adv_from_different_source(
self, old: BluetoothServiceInfoBleak, new: BluetoothServiceInfoBleak
self,
old: BluetoothServiceInfoBleak,
new: BluetoothServiceInfoBleak,
tracker: AdvertisementTracker,
) -> 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 := tracker.intervals.get(
new.address, FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
)
):
Expand Down Expand Up @@ -356,26 +359,27 @@ def scanner_adv_received(self, service_info: BluetoothServiceInfoBleak) -> None:
device = service_info.device
connectable = service_info.connectable
address = device.address
all_history = self._connectable_history if connectable else self._history

if connectable:
history_by_connectable = self._connectable_history
tracker = self._connectable_advertisement_tracker
else:
history_by_connectable = self._non_connectable_history
tracker = self._non_connectable_advertisement_tracker
source = service_info.source
if (
(old_service_info := all_history.get(address))
(old_service_info := history_by_connectable.get(address))
and source != old_service_info.source
and self._prefer_previous_adv_from_different_source(
old_service_info, service_info
old_service_info, service_info, tracker
)
):
return

self._history[address] = service_info

if connectable:
self._connectable_history[address] = service_info
# Bleak callbacks must get a connectable device
self._all_history[address] = history_by_connectable[address] = service_info

# Track advertisement intervals to determine when we need to
# switch adapters or mark a device as unavailable
tracker = self._advertisement_tracker
if (last_source := tracker.sources.get(address)) and last_source != source:
# Source changed, remove the old address from the tracker
tracker.async_remove_address(address)
Expand Down Expand Up @@ -517,27 +521,31 @@ def async_rediscover_address(self, address: str) -> None:

def _get_scanners_by_type(self, connectable: bool) -> list[BaseHaScanner]:
"""Return the scanners by type."""
return (
self._connectable_scanners
if connectable
else self._non_connectable_scanners
)
if connectable:
return self._connectable_scanners
return self._non_connectable_scanners

def _get_unavailable_callbacks_by_type(
self, connectable: bool
) -> dict[str, list[Callable[[BluetoothServiceInfoBleak], None]]]:
"""Return the unavailable callbacks by type."""
return (
self._connectable_unavailable_callbacks
if connectable
else self._unavailable_callbacks
)
if connectable:
return self._connectable_unavailable_callbacks
return self._unavailable_callbacks

def _get_history_by_type(
self, connectable: bool
) -> dict[str, BluetoothServiceInfoBleak]:
"""Return the history by type."""
return self._connectable_history if connectable else self._history
return self._connectable_history if connectable else self._all_history

def _get_advertisement_tracker_by_type(
self, connectable: bool
) -> AdvertisementTracker:
"""Return the advertisement tracker by type."""
if connectable:
return self._connectable_advertisement_tracker
return self._non_connectable_advertisement_tracker

def async_register_scanner(
self, scanner: BaseHaScanner, connectable: bool
Expand All @@ -546,7 +554,9 @@ def async_register_scanner(
scanners = self._get_scanners_by_type(connectable)

def _unregister_scanner() -> None:
self._advertisement_tracker.async_remove_source(scanner.source)
self._get_advertisement_tracker_by_type(connectable).async_remove_source(
scanner.source
)
scanners.remove(scanner)

scanners.append(scanner)
Expand Down
63 changes: 35 additions & 28 deletions tests/components/bluetooth/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,6 @@ async def test_diagnostics(
}
},
"manager": {
"advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"adapters": {
"hci0": {
"address": "00:00:00:00:00:01",
Expand All @@ -115,8 +110,18 @@ async def test_diagnostics(
"sw_version": "BlueZ 4.63",
},
},
"connectable_advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"connectable_history": [],
"history": [],
"non_connectable_advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"non_connectable_history": [],
"scanners": [
{
"adapter": "hci0",
Expand Down Expand Up @@ -203,38 +208,34 @@ async def test_diagnostics_macos(
}
},
"manager": {
"advertisement_tracker": {
"intervals": {},
"sources": {"44:44:33:11:23:45": "local"},
"timings": {"44:44:33:11:23:45": [ANY]},
},
"adapters": {
"Core Bluetooth": {
"address": "00:00:00:00:00:00",
"passive_scan": False,
"sw_version": ANY,
"sw_version": "21.6.0",
}
},
"connectable_advertisement_tracker": {
"intervals": {},
"sources": {"44:44:33:11:23:45": "local"},
"timings": {"44:44:33:11:23:45": [ANY]},
},
"connectable_history": [
{
"address": "44:44:33:11:23:45",
"advertisement": ANY,
"connectable": True,
"manufacturer_data": ANY,
"name": "wohand",
"rssi": -127,
"service_data": {},
"service_uuids": [],
"source": "local",
"time": ANY,
}
],
"history": [
{
"address": "44:44:33:11:23:45",
"advertisement": ANY,
"advertisement": [
"wohand",
{"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}},
{},
[],
-127,
-127,
[[]],
],
"connectable": True,
"manufacturer_data": ANY,
"manufacturer_data": {
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
},
"name": "wohand",
"rssi": -127,
"service_data": {},
Expand All @@ -243,6 +244,12 @@ async def test_diagnostics_macos(
"time": ANY,
}
],
"non_connectable_advertisement_tracker": {
"intervals": {},
"sources": {},
"timings": {},
},
"non_connectable_history": [],
"scanners": [
{
"adapter": "Core Bluetooth",
Expand Down