From c99e1d027f98ca66554db1b4c1279b92ab1b9a6f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 13 Oct 2022 13:40:06 -0500 Subject: [PATCH] backends/winrt/scanner: wait for scan response for first callback To make WinRT more like other platforms, we wait for a scan response if it is expected before issuing the first advertisement callback. This way, the convenience methods like `find_by_address` will include the full advertising data similar to other platforms. --- bleak/backends/winrt/scanner.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/bleak/backends/winrt/scanner.py b/bleak/backends/winrt/scanner.py index 74434125..ce1bbfa9 100644 --- a/bleak/backends/winrt/scanner.py +++ b/bleak/backends/winrt/scanner.py @@ -1,7 +1,7 @@ import asyncio import logging import sys -from typing import List, NamedTuple, Optional, cast +from typing import Dict, List, NamedTuple, Optional from uuid import UUID from bleak_winrt.windows.devices.bluetooth.advertisement import ( @@ -79,6 +79,7 @@ def __init__( super(BleakScannerWinRT, self).__init__(detection_callback, service_uuids) self.watcher = None + self._advertisement_pairs: Dict[int, _RawAdvData] = {} self._stopped_event = None # case insensitivity is for backwards compatibility on Windows only @@ -113,12 +114,7 @@ def _received_handler( # us (regular advertisement + scan response) so we have to do it manually. # get the previous advertising data/scan response pair or start a new one - _, old_adv_data = self.seen_devices.get(bdaddr, (None, None)) - raw_data = ( - _RawAdvData(event_args, None) - if old_adv_data is None - else cast(_RawAdvData, old_adv_data.platform_data[1]) - ) + raw_data = self._advertisement_pairs.get(bdaddr, _RawAdvData(None, None)) # update the advertising data depending on the advertising data type if event_args.advertisement_type == BluetoothLEAdvertisementType.SCAN_RESPONSE: @@ -126,6 +122,23 @@ def _received_handler( else: raw_data = _RawAdvData(event_args, raw_data.scan) + self._advertisement_pairs[bdaddr] = raw_data + + # if we are expecting scan response and we haven't received both a + # regular advertisement and a scan response, don't do callbacks yet, + # wait until we have both instead so we get a callback with partial data + + if (raw_data.adv is None or raw_data.scan is None) and ( + event_args.advertisement_type + in [ + BluetoothLEAdvertisementType.CONNECTABLE_UNDIRECTED, + BluetoothLEAdvertisementType.SCANNABLE_UNDIRECTED, + BluetoothLEAdvertisementType.SCAN_RESPONSE, + ] + ): + logger.debug("skipping callback, waiting for scan response") + return + uuids = [] mfg_data = {} service_data = {} @@ -212,6 +225,7 @@ def _stopped_handler(self, sender, e): async def start(self): # start with fresh list of discovered devices self.seen_devices = {} + self._advertisement_pairs.clear() self.watcher = BluetoothLEAdvertisementWatcher() self.watcher.scanning_mode = self._scanning_mode