From 7aa5d77adf2de765942dab515315751e5f3c56d3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 28 Apr 2024 18:00:17 -0500 Subject: [PATCH 1/2] backends/winrt: protect scanner start from re-entrancy It is not safe to start the scanner if it is already running. This adds a check to ensure that the scanner is not already running before starting. It will not raise an exception instead of resulting in undefined behavior. --- bleak/backends/winrt/scanner.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bleak/backends/winrt/scanner.py b/bleak/backends/winrt/scanner.py index bfd46c71..665625bb 100644 --- a/bleak/backends/winrt/scanner.py +++ b/bleak/backends/winrt/scanner.py @@ -22,6 +22,7 @@ ) from ...assigned_numbers import AdvertisementDataType +from ...exc import BleakError from ...uuids import normalize_uuid_str from ..scanner import AdvertisementData, AdvertisementDataCallback, BaseBleakScanner @@ -83,7 +84,7 @@ def __init__( ): super(BleakScannerWinRT, self).__init__(detection_callback, service_uuids) - self.watcher = None + self.watcher: Optional[BluetoothLEAdvertisementWatcher] = None self._advertisement_pairs: Dict[int, _RawAdvData] = {} self._stopped_event = None @@ -220,6 +221,9 @@ def _stopped_handler(self, sender, e): self._stopped_event.set() async def start(self) -> None: + if self.watcher: + raise BleakError("Scanner already started") + # start with fresh list of discovered devices self.seen_devices = {} self._advertisement_pairs.clear() From 9dfd3a3c0651edd652437c1ee9d48af10aa978dc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 28 Apr 2024 18:17:24 -0500 Subject: [PATCH 2/2] backends/winrt: check watcher status after starting If Bluetooth is off or not present, the status will be ABORTED. We can use this to give a helpful error message. Also wait some time for the status to change to STARTED before returning. Fixes: https://github.com/hbldh/bleak/issues/1535 --- CHANGELOG.rst | 1 + bleak/backends/winrt/scanner.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dc8901cc..f838df4a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,6 +27,7 @@ Fixed * Fixed ``KeyError`` in BlueZ ``is_connected()`` and ``get_global_bluez_manager()`` when device is not present. Fixes #1507. * Fixed BlueZ ``_wait_removed`` completion on invalid object path. Fixes #1489. * Fixed rare unhandled exception when scanning on macOS when using ``use_bdaddr``. Fixes #1523. +* Fixed scanning silently failing on Windows when Bluetooth is off. Fixes #1535. `0.21.1`_ (2023-09-08) ====================== diff --git a/bleak/backends/winrt/scanner.py b/bleak/backends/winrt/scanner.py index 665625bb..542f8097 100644 --- a/bleak/backends/winrt/scanner.py +++ b/bleak/backends/winrt/scanner.py @@ -248,6 +248,16 @@ async def start(self) -> None: self.watcher.start() + # no events for status changes, so we have to poll :-( + while self.watcher.status == BluetoothLEAdvertisementWatcherStatus.CREATED: + await asyncio.sleep(0.01) + + if self.watcher.status == BluetoothLEAdvertisementWatcherStatus.ABORTED: + raise BleakError("Failed to start scanner. Is Bluetooth turned on?") + + if self.watcher.status != BluetoothLEAdvertisementWatcherStatus.STARTED: + raise BleakError(f"Unexpected watcher status: {self.watcher.status.name}") + async def stop(self) -> None: self.watcher.stop()