From f04d32f6628ff0bb08faaf2a12c32d038f463fe6 Mon Sep 17 00:00:00 2001 From: Adam Incera Date: Tue, 20 Oct 2020 20:42:27 -0400 Subject: [PATCH 1/9] Fixed issue causing advertising data to be lost on successive calls to scanner callback --- bleak/backends/corebluetooth/scanner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bleak/backends/corebluetooth/scanner.py b/bleak/backends/corebluetooth/scanner.py index 1a93ded7..a8c8bf24 100644 --- a/bleak/backends/corebluetooth/scanner.py +++ b/bleak/backends/corebluetooth/scanner.py @@ -47,7 +47,8 @@ async def start(self): self._identifiers = {} def callback(p, a, r): - self._identifiers[p.identifier()] = a + # update identifiers for scanned device + self._identifiers.setdefault(p.identifier(), {}).update(a) if self._callback: self._callback(p, a, r) From b980252cf4b1378d587e0d918c5de8e153dd2a5e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 20 Oct 2020 22:31:31 -0500 Subject: [PATCH 2/9] corebluetooth: use cb_uuid_to_str() to convert kCBAdvDataServiceUUIDs This was missed in 6a0d8c99f5c82b6b1c000316fb1c0dff49a92138. Also, since this was just using `str()` directly on the `CBUUID` instance, it had an additional bug for well known services since `str()` would return the name of the service instead of a UUID. Fixes #342. --- CHANGELOG.rst | 11 +++++++++++ bleak/backends/corebluetooth/device.py | 5 ++--- bleak/backends/corebluetooth/scanner.py | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9898f140..f944eb93 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,17 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_, and this project adheres to `Semantic Versioning `_. + +`Unreleased`_ +-------------- + +Fixed +~~~~~ + +* Fix well-known services not converted to UUIDs in ``BLEDevice.metadata`` in + CoreBluetooth backend. Fixes #342. + + `0.9.0`_ (2020-10-20) --------------------- diff --git a/bleak/backends/corebluetooth/device.py b/bleak/backends/corebluetooth/device.py index 2e0e022e..228a49e0 100644 --- a/bleak/backends/corebluetooth/device.py +++ b/bleak/backends/corebluetooth/device.py @@ -3,7 +3,7 @@ from Foundation import NSDictionary - +from bleak.backends.corebluetooth.utils import cb_uuid_to_str from bleak.backends.device import BLEDevice @@ -46,8 +46,7 @@ def _update_uuids(self, advertisementData: NSDictionary): cbuuids = advertisementData.get("kCBAdvDataServiceUUIDs", []) if not cbuuids: return - # converting to lower case to match other platforms - chuuids = [str(u).lower() for u in cbuuids] + chuuids = [cb_uuid_to_str(u) for u in cbuuids] if "uuids" in self.metadata: for uuid in chuuids: if not uuid in self.metadata["uuids"]: diff --git a/bleak/backends/corebluetooth/scanner.py b/bleak/backends/corebluetooth/scanner.py index 1a93ded7..6a90fee3 100644 --- a/bleak/backends/corebluetooth/scanner.py +++ b/bleak/backends/corebluetooth/scanner.py @@ -5,6 +5,7 @@ from typing import Callable, Any, Union, List from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate +from bleak.backends.corebluetooth.utils import cb_uuid_to_str from bleak.backends.device import BLEDevice from bleak.exc import BleakError from bleak.backends.scanner import BaseBleakScanner @@ -101,8 +102,7 @@ async def get_discovered_devices(self) -> List[BLEDevice]: manufacturer_data = {manufacturer_id: manufacturer_value} uuids = [ - # converting to lower case to match other platforms - str(u).lower() + cb_uuid_to_str(u) for u in advertisementData.get("kCBAdvDataServiceUUIDs", []) ] From 5f2caca53e61b70bbee6cfc8942d52c33d7f97d1 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 21 Oct 2020 17:26:30 -0500 Subject: [PATCH 3/9] changelog: add entry for #343 --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f944eb93..c56f24e6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,8 @@ Fixed * Fix well-known services not converted to UUIDs in ``BLEDevice.metadata`` in CoreBluetooth backend. Fixes #342. +* Fix advertising data replaced instead of merged in scanner in CoreBluetooth + backend. Merged #343. `0.9.0`_ (2020-10-20) From 5cdb3980bad263fb850a5355af1b90a1f7fd0aef Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 21 Oct 2020 21:53:48 -0500 Subject: [PATCH 4/9] corebluetooth: use threading.Event to wait for centralManagerDidUpdateState_ Previously, we were using an `asyncio.Event` to wait for the initial centralManagerDidUpdateState_ callback. However we were not always waiting for this before using the `CBCentralManager` in all code paths. By using `threading.Event` instead, we can do a (short) blocking wait in the initializer to ensure that `CBCentralManager` is ready before we do anything else. --- CHANGELOG.rst | 2 + .../corebluetooth/CentralManagerDelegate.py | 39 +++++++------------ bleak/backends/corebluetooth/discovery.py | 7 ---- bleak/backends/corebluetooth/scanner.py | 6 --- 4 files changed, 17 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c56f24e6..eca7b7f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,8 @@ Fixed CoreBluetooth backend. Fixes #342. * Fix advertising data replaced instead of merged in scanner in CoreBluetooth backend. Merged #343. +* Fix CBCentralManager not properly waited for during initialization in some + cases. `0.9.0`_ (2020-10-20) diff --git a/bleak/backends/corebluetooth/CentralManagerDelegate.py b/bleak/backends/corebluetooth/CentralManagerDelegate.py index 84b60b78..682a6385 100644 --- a/bleak/backends/corebluetooth/CentralManagerDelegate.py +++ b/bleak/backends/corebluetooth/CentralManagerDelegate.py @@ -9,6 +9,7 @@ import asyncio import logging import platform +import threading from enum import Enum from typing import List @@ -35,6 +36,7 @@ from bleak.backends.corebluetooth.PeripheralDelegate import PeripheralDelegate from bleak.backends.corebluetooth.device import BLEDeviceCoreBluetooth +from bleak.exc import BleakError logger = logging.getLogger(__name__) CBCentralManagerDelegate = objc.protocolNamed("CBCentralManagerDelegate") @@ -70,17 +72,26 @@ def init(self): self.connected_peripheral = None self._connection_state = CMDConnectionState.DISCONNECTED - self.powered_on_event = asyncio.Event() self.devices = {} self.callbacks = {} self.disconnected_callback = None self._connection_state_changed = asyncio.Event() + self._did_update_state_event = threading.Event() self.central_manager = CBCentralManager.alloc().initWithDelegate_queue_( self, dispatch_queue_create(b"bleak.corebluetooth", DISPATCH_QUEUE_SERIAL) ) + # according to CoreBluetooth docs, it is not valid to call CBCentral + # methods until the centralManagerDidUpdateState_() delegate method + # is called and the current state is CBManagerStatePoweredOn. + # It doesn't take long for the callback to occur, so we should be able + # to do a blocking wait here without anyone complaining. + self._did_update_state_event.wait(1) + if self.central_manager.state() != CBManagerStatePoweredOn: + raise BleakError("Bluetooth device is turned off") + return self # User defined functions @@ -89,16 +100,6 @@ def init(self): def isConnected(self) -> bool: return self._connection_state == CMDConnectionState.CONNECTED - @objc.python_method - async def wait_for_powered_on(self, timeout: float): - """ - Waits for state to be CBManagerStatePoweredOn. This must be done before - attempting to do anything else. - - Throws asyncio.TimeoutError if power on is not detected before timeout. - """ - await asyncio.wait_for(self.powered_on_event.wait(), timeout) - @objc.python_method def start_scan(self, scan_options): # remove old @@ -174,8 +175,8 @@ async def disconnect(self) -> bool: # Protocol Functions - @objc.python_method - def did_update_state(self, centralManager): + def centralManagerDidUpdateState_(self, centralManager): + logger.debug("centralManagerDidUpdateState_") if centralManager.state() == CBManagerStateUnknown: logger.debug("Cannot detect bluetooth device") elif centralManager.state() == CBManagerStateResetting: @@ -189,17 +190,7 @@ def did_update_state(self, centralManager): elif centralManager.state() == CBManagerStatePoweredOn: logger.debug("Bluetooth powered on") - if centralManager.state() == CBManagerStatePoweredOn: - self.powered_on_event.set() - else: - self.powered_on_event.clear() - - def centralManagerDidUpdateState_(self, centralManager): - logger.debug("centralManagerDidUpdateState_") - self.event_loop.call_soon_threadsafe( - self.did_update_state, - centralManager, - ) + self._did_update_state_event.set() @objc.python_method def did_discover_peripheral( diff --git a/bleak/backends/corebluetooth/discovery.py b/bleak/backends/corebluetooth/discovery.py index f52496ad..f9197d28 100644 --- a/bleak/backends/corebluetooth/discovery.py +++ b/bleak/backends/corebluetooth/discovery.py @@ -7,12 +7,10 @@ """ -import asyncio from typing import List from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate from bleak.backends.device import BLEDevice -from bleak.exc import BleakError async def discover(timeout: float = 5.0, **kwargs) -> List[BLEDevice]: @@ -23,11 +21,6 @@ async def discover(timeout: float = 5.0, **kwargs) -> List[BLEDevice]: """ manager = CentralManagerDelegate.alloc().init() - try: - await manager.wait_for_powered_on(0.1) - except asyncio.TimeoutError: - raise BleakError("Bluetooth device is turned off") - scan_options = {"timeout": timeout} await manager.scanForPeripherals_(scan_options) diff --git a/bleak/backends/corebluetooth/scanner.py b/bleak/backends/corebluetooth/scanner.py index e12952f9..f7b75d55 100644 --- a/bleak/backends/corebluetooth/scanner.py +++ b/bleak/backends/corebluetooth/scanner.py @@ -7,7 +7,6 @@ from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate from bleak.backends.corebluetooth.utils import cb_uuid_to_str from bleak.backends.device import BLEDevice -from bleak.exc import BleakError from bleak.backends.scanner import BaseBleakScanner @@ -40,11 +39,6 @@ def __init__(self, **kwargs): self._timeout = kwargs.get("timeout", 5.0) async def start(self): - try: - await self._manager.wait_for_powered_on(0.1) - except asyncio.TimeoutError: - raise BleakError("Bluetooth device is turned off") - self._identifiers = {} def callback(p, a, r): From b27c7e18f184c40ddbb417ae869258a159552d51 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 21 Oct 2020 22:37:17 -0500 Subject: [PATCH 5/9] corebluetooth: fix AttributeError when trying to use BLEDeviceCoreBluetooth In 583c08e4bf1b we missed passing the CentralManagerDelegate instance to the BLEDeviceCoreBluetooth constructor. This also uses the index operator instead of the get() method since that just deferred the error until later which made it harder to troubleshoot. --- CHANGELOG.rst | 1 + bleak/backends/corebluetooth/CentralManagerDelegate.py | 2 +- bleak/backends/corebluetooth/client.py | 6 ++---- bleak/backends/corebluetooth/device.py | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eca7b7f8..f8d0e61d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,7 @@ Fixed backend. Merged #343. * Fix CBCentralManager not properly waited for during initialization in some cases. +* Fix AttributeError in CoreBluetooth when using BLEDeviceCoreBluetooth object. `0.9.0`_ (2020-10-20) diff --git a/bleak/backends/corebluetooth/CentralManagerDelegate.py b/bleak/backends/corebluetooth/CentralManagerDelegate.py index 682a6385..0d1423ea 100644 --- a/bleak/backends/corebluetooth/CentralManagerDelegate.py +++ b/bleak/backends/corebluetooth/CentralManagerDelegate.py @@ -225,7 +225,7 @@ def did_discover_peripheral( address = uuid_string name = peripheral.name() or None details = peripheral - device = BLEDeviceCoreBluetooth(address, name, details) + device = BLEDeviceCoreBluetooth(address, name, details, delegate=self) self.devices[uuid_string] = device device._rssi = float(RSSI) diff --git a/bleak/backends/corebluetooth/client.py b/bleak/backends/corebluetooth/client.py index 6e829eb0..fc8f0b92 100644 --- a/bleak/backends/corebluetooth/client.py +++ b/bleak/backends/corebluetooth/client.py @@ -48,9 +48,7 @@ def __init__(self, address_or_ble_device: Union[BLEDevice, str], **kwargs): if isinstance(address_or_ble_device, BLEDevice): self._device_info = address_or_ble_device.details - self._central_manager_delegate = address_or_ble_device.metadata.get( - "delegate" - ) + self._central_manager_delegate = address_or_ble_device.metadata["delegate"] else: self._device_info = None self._central_manager_delegate = None @@ -79,7 +77,7 @@ async def connect(self, **kwargs) -> bool: if device: self._device_info = device.details - self._central_manager_delegate = device.metadata.get("delegate") + self._central_manager_delegate = device.metadata["delegate"] else: raise BleakError( "Device with address {} was not found".format(self.address) diff --git a/bleak/backends/corebluetooth/device.py b/bleak/backends/corebluetooth/device.py index 228a49e0..835fb8ac 100644 --- a/bleak/backends/corebluetooth/device.py +++ b/bleak/backends/corebluetooth/device.py @@ -14,7 +14,7 @@ class BLEDeviceCoreBluetooth(BLEDevice): - The `details` attribute will be a CBPeripheral object. - - The `metadata` keys are more or less part of the crossplattform interface. + - The `metadata` keys are more or less part of the cross-platform interface. - Note: Take care not to rely on any reference to `advertisementData` and it's data as lower layers of the corebluetooth stack can change it. i.e. @@ -35,7 +35,6 @@ class BLEDeviceCoreBluetooth(BLEDevice): def __init__(self, *args, **kwargs): super(BLEDeviceCoreBluetooth, self).__init__(*args, **kwargs) - self.metadata = {} self._rssi = kwargs.get("rssi") def _update(self, advertisementData: NSDictionary): From a1d0f53c59e167ca42ccd6596162cdedaf85362b Mon Sep 17 00:00:00 2001 From: Carglglz Date: Thu, 22 Oct 2020 08:33:48 +0100 Subject: [PATCH 6/9] Add device info bluezdbus client.py (#346) * Added device info to bluezdbus client --- bleak/backends/bluezdbus/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bleak/backends/bluezdbus/client.py b/bleak/backends/bluezdbus/client.py index 48a87abf..c554be47 100644 --- a/bleak/backends/bluezdbus/client.py +++ b/bleak/backends/bluezdbus/client.py @@ -54,8 +54,10 @@ def __init__(self, address_or_ble_device: Union[BLEDevice, str], **kwargs): # Backend specific, TXDBus objects and data if isinstance(address_or_ble_device, BLEDevice): self._device_path = address_or_ble_device.details["path"] + self._device_info = address_or_ble_device.details.get("props") else: self._device_path = None + self._device_info = None self._bus = None self._reactor = None self._rules = {} @@ -92,6 +94,7 @@ async def connect(self, **kwargs) -> bool: ) if device: + self._device_info = device.details.get("props") self._device_path = device.details["path"] else: raise BleakError( From e6c7f869d6ee6843347d7cf563d8fe8f3c45e866 Mon Sep 17 00:00:00 2001 From: Henrik Blidh Date: Thu, 22 Oct 2020 10:08:17 +0200 Subject: [PATCH 7/9] CHANGELOG.rst updates related to #347 --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f8d0e61d..cf69fbbe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,11 @@ and this project adheres to `Semantic Versioning Date: Thu, 22 Oct 2020 10:31:35 +0200 Subject: [PATCH 8/9] Contribution details updated. - Updated documentation regarding contribution. - Added PR template --- .github/ISSUE_TEMPLATE.md | 10 ++++++++-- .github/pull_request_template.md | 16 ++++++++++++++++ AUTHORS.rst | 7 ++++++- CHANGELOG.rst | 7 +++++++ CONTRIBUTING.rst | 13 +++++++------ 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index bbe8dc2f..34d2c0d7 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ * bleak version: * Python version: * Operating System: -* BlueZ version (`bluetoothctl -v`) in case of Linux: +* BlueZ version (`bluetoothctl -v`) in case of Linux: ### Description @@ -10,7 +10,13 @@ Tell us what happened, what went wrong, and what you expected to happen. ### What I Did +It is preferable if an issue contains a [Miminal Workable Example](https://stackoverflow.com/help/minimal-reproducible-example). +This will otherwise be one of the first questions you will get as a response. + +It is also preferable if that example is not dependent on a specific peripheral device, but can be run and +reproduced with other BLE peripherals as well. + ``` Paste the command(s) you ran and the output. -If there was a crash, please include the traceback here. +If there was a crash, please include the traceback here as well. ``` diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..32d734bb --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,16 @@ +Pull Request Guidelines for Bleak +--------------------------------- + +Before you submit a pull request, check that it meets these guidelines: + +1. If the pull request adds functionality, the docs should be updated. +2. Modify the `CHANGELOG.rst`, describing your changes as is specified by the + guidelines in that document. +3. The pull request should work for Python 3.6+ on the following platforms: + - Windows 10, version 16299 (Fall Creators Update) and greater + - Linux distributions with BlueZ >= 5.43 + - OS X / macOS >= 10.11 +4. Squash all your commits on your PR branch, if the commits are not solving + different problems and you are committing them in the same PR. In that case, + consider making several PRs instead. +5. Feel free to add your name as a contributor to the `AUTHORS.rst` file! diff --git a/AUTHORS.rst b/AUTHORS.rst index 2325f5e3..2a411f63 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,7 +7,12 @@ Development Lead * Henrik Blidh +Development Team / Collaborators +-------------------------------- + +* David Lechner + Contributors ------------ -* David Lechner + diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cf69fbbe..e5b28b4c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,13 @@ Added ~~~~~ * Added new attribute ``_device_info`` on ``BleakClientBlueZDBus``. Merges #347 +* Added Pull Request Template. + +Changed +~~~~~~~ + +* Updated instructions on how to contribute, file issues and make PRs. +* Updated ``AUTHORS.rst`` file with development team. Fixed ~~~~~ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 82e558b0..bdb18af4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -98,13 +98,14 @@ Pull Request Guidelines Before you submit a pull request, check that it meets these guidelines: -1. The pull request should include tests. -2. If the pull request adds functionality, the docs should be updated. Put - your new functionality into a function with a docstring, and add the - feature to the list in README.rst. +1. If the pull request adds functionality, the docs should be updated. +2. Modify the ``CHANGELOG.rst``, describing your changes as is specified by the + guidelines in that document. 3. The pull request should work for Python 3.6+ on the following platforms: - Windows 10, version 16299 (Fall Creators Update) and greater - Linux distributions with BlueZ >= 5.43 - OS X / macOS >= 10.11 -4. Feel free to add your name to the ``AUTHORS.rst`` in the root of the project! - +4. Squash all your commits on your PR branch, if the commits are not solving + different problems and you are committing them in the same PR. In that case, + consider making several PRs instead. +5. Feel free to add your name as a contributor to the ``AUTHORS.rst`` file! From 65896e8e475eee04fba94a95d8387929fc1ca2c6 Mon Sep 17 00:00:00 2001 From: Henrik Blidh Date: Thu, 22 Oct 2020 10:44:27 +0200 Subject: [PATCH 9/9] Version bump and CHANGELOG update. --- CHANGELOG.rst | 9 +++++---- bleak/__version__.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e5b28b4c..28a3cd42 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,13 +8,13 @@ The format is based on `Keep a Changelog ` and this project adheres to `Semantic Versioning `_. -`Unreleased`_ --------------- +`0.9.1`_ (2020-10-22) +--------------------- Added ~~~~~ -* Added new attribute ``_device_info`` on ``BleakClientBlueZDBus``. Merges #347 +* Added new attribute ``_device_info`` on ``BleakClientBlueZDBus``. Merges #347. * Added Pull Request Template. Changed @@ -375,7 +375,8 @@ Fixed * Bleak created. -.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.9.0...develop +.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.9.1...develop +.. _0.9.1: https://github.com/hbldh/bleak/compare/v0.9.1...v0.9.0 .. _0.9.0: https://github.com/hbldh/bleak/compare/v0.9.0...v0.8.0 .. _0.8.0: https://github.com/hbldh/bleak/compare/v0.8.0...v0.7.1 .. _0.7.1: https://github.com/hbldh/bleak/compare/v0.7.1...v0.7.0 diff --git a/bleak/__version__.py b/bleak/__version__.py index a0ab76bc..0f4dc532 100644 --- a/bleak/__version__.py +++ b/bleak/__version__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -__version__ = "0.9.0" +__version__ = "0.9.1"