diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index e4aef087..1e2b7e09 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,23 @@ 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 `_.
+`0.14.3`_ (2022-04-29)
+======================
+
+Changed
+-------
+
+* Suppress macOS 12 scanner bug error message for macOS 12.3 and higher. Fixes #720.
+* Added filters ``Discoverable`` and ``Pattern`` to BlueZ D-Bus scanner. Fixes #790.
+
+Fixed
+-----
+
+* Fixed reading the battery level returns a zero-filled ``bytearray`` on BlueZ >= 5.48. Fixes #750.
+* Fixed unpairing does not work on windows with WinRT. Fixes #699
+* Fixed leak of ``_disconnect_futures`` in ``CentralManagerDelegate``.
+* Fixed callback not removed from ``_disconnect_callbacks`` on disconnect in ``CentralManagerDelegate``.
+
`0.14.2`_ (2022-01-26)
======================
@@ -648,7 +665,8 @@ Fixed
* Bleak created.
-.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.14.2...develop
+.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.14.3...develop
+.. _0.14.3: https://github.com/hbldh/bleak/compare/v0.14.2...v0.14.3
.. _0.14.2: https://github.com/hbldh/bleak/compare/v0.14.1...v0.14.2
.. _0.14.1: https://github.com/hbldh/bleak/compare/v0.14.0...v0.14.1
.. _0.14.0: https://github.com/hbldh/bleak/compare/v0.13.0...v0.14.0
diff --git a/Pipfile b/Pipfile
index 25053cb5..632498ee 100644
--- a/Pipfile
+++ b/Pipfile
@@ -23,7 +23,7 @@ pyyaml = "*"
wheel = "*"
watchdog = "*"
coverage = "*"
-black = "*"
+black = ">=22.1.0"
[pipenv]
allow_prereleases = true
diff --git a/README.rst b/README.rst
index 7a695439..fdad0440 100644
--- a/README.rst
+++ b/README.rst
@@ -14,6 +14,10 @@ bleak
.. image:: https://img.shields.io/pypi/v/bleak.svg
:target: https://pypi.python.org/pypi/bleak
+
+.. image:: https://img.shields.io/pypi/dm/bleak.svg
+ :target: https://pypi.python.org/pypi/bleak
+ :alt: PyPI - Downloads
.. image:: https://readthedocs.org/projects/bleak/badge/?version=latest
:target: https://bleak.readthedocs.io/en/latest/?badge=latest
diff --git a/bleak/__version__.py b/bleak/__version__.py
index 5c9fd149..fcf568e9 100644
--- a/bleak/__version__.py
+++ b/bleak/__version__.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
-__version__ = "0.14.2"
+__version__ = "0.14.3"
diff --git a/bleak/backends/bluezdbus/client.py b/bleak/backends/bluezdbus/client.py
index 2f1c8499..f8053e7d 100644
--- a/bleak/backends/bluezdbus/client.py
+++ b/bleak/backends/bluezdbus/client.py
@@ -640,7 +640,7 @@ async def read_gatt_char(
)
assert_reply(reply)
# Simulate regular characteristics read to be consistent over all platforms.
- value = bytearray(reply.body[0]["Percentage"].value)
+ value = bytearray([reply.body[0]["Percentage"].value])
logger.debug(
"Read Battery Level {0} | {1}: {2}".format(
char_specifier, self._device_path, value
diff --git a/bleak/backends/bluezdbus/scanner.py b/bleak/backends/bluezdbus/scanner.py
index 2d5d72bf..d241c027 100644
--- a/bleak/backends/bluezdbus/scanner.py
+++ b/bleak/backends/bluezdbus/scanner.py
@@ -207,12 +207,16 @@ def set_scanning_filter(self, **kwargs):
self._filters[k] = Variant("as", v)
elif k == "RSSI":
self._filters[k] = Variant("n", v)
- elif k == "DuplicateData":
- self._filters[k] = Variant("b", v)
elif k == "Pathloss":
self._filters[k] = Variant("n", v)
elif k == "Transport":
self._filters[k] = Variant("s", v)
+ elif k == "DuplicateData":
+ self._filters[k] = Variant("b", v)
+ elif k == "Discoverable":
+ self._filters[k] = Variant("b", v)
+ elif k == "Pattern":
+ self._filters[k] = Variant("s", v)
else:
logger.warning("Filter '%s' is not currently supported." % k)
diff --git a/bleak/backends/corebluetooth/CentralManagerDelegate.py b/bleak/backends/corebluetooth/CentralManagerDelegate.py
index 5fc3e6dd..ac83c9f2 100644
--- a/bleak/backends/corebluetooth/CentralManagerDelegate.py
+++ b/bleak/backends/corebluetooth/CentralManagerDelegate.py
@@ -185,7 +185,7 @@ async def disconnect(self, peripheral: CBPeripheral) -> None:
self.central_manager.cancelPeripheralConnection_(peripheral)
await future
finally:
- del self._disconnect_callbacks[peripheral.identifier()]
+ del self._disconnect_futures[peripheral.identifier()]
@objc.python_method
def _changed_is_scanning(self, is_scanning: bool) -> None:
@@ -353,7 +353,8 @@ def did_disconnect_peripheral(
else:
future.set_result(None)
- callback = self._disconnect_callbacks.get(peripheral.identifier())
+ callback = self._disconnect_callbacks.pop(peripheral.identifier(), None)
+
if callback is not None:
callback()
diff --git a/bleak/backends/corebluetooth/characteristic.py b/bleak/backends/corebluetooth/characteristic.py
index c6c76627..477543c9 100644
--- a/bleak/backends/corebluetooth/characteristic.py
+++ b/bleak/backends/corebluetooth/characteristic.py
@@ -63,7 +63,7 @@ def __init__(self, obj: CBCharacteristic):
# self.__props = obj.properties()
self.__props: List[str] = [
_GattCharacteristicsPropertiesEnum[v][0]
- for v in [2 ** n for n in range(10)]
+ for v in [2**n for n in range(10)]
if (self.obj.properties() & v)
]
self._uuid: str = cb_uuid_to_str(self.obj.UUID())
diff --git a/bleak/backends/corebluetooth/scanner.py b/bleak/backends/corebluetooth/scanner.py
index cd28afa7..cf1fc3fe 100644
--- a/bleak/backends/corebluetooth/scanner.py
+++ b/bleak/backends/corebluetooth/scanner.py
@@ -3,7 +3,7 @@
from typing import Any, Dict, List, Optional
import objc
-from Foundation import NSArray, NSUUID
+from Foundation import NSArray, NSUUID, NSBundle
from CoreBluetooth import CBPeripheral
from bleak.backends.corebluetooth.CentralManagerDelegate import CentralManagerDelegate
@@ -35,7 +35,7 @@ class BleakScannerCoreBluetooth(BaseBleakScanner):
**service_uuids (List[str]):
Optional list of service UUIDs to filter on. Only advertisements
containing this advertising data will be received. Required on
- macOS 12 and later.
+ macOS 12 and later (unless you create an app with ``py2app``).
"""
def __init__(self, **kwargs):
@@ -43,10 +43,16 @@ def __init__(self, **kwargs):
self._identifiers: Optional[Dict[NSUUID, Dict[str, Any]]] = None
self._manager = CentralManagerDelegate.alloc().init()
self._timeout: float = kwargs.get("timeout", 5.0)
- if objc.macos_available(12, 0) and not self._service_uuids:
- logger.error(
- "macOS 12 requires non-empty service_uuids kwarg, otherwise no advertisement data will be received"
- )
+ if (
+ objc.macos_available(12, 0)
+ and not objc.macos_available(12, 3)
+ and not self._service_uuids
+ ):
+ # See https://github.com/hbldh/bleak/issues/720
+ if NSBundle.mainBundle().bundleIdentifier() == "org.python.python":
+ logger.error(
+ "macOS 12.0, 12.1 and 12.2 require non-empty service_uuids kwarg, otherwise no advertisement data will be received"
+ )
async def start(self):
self._identifiers = {}
diff --git a/bleak/backends/winrt/characteristic.py b/bleak/backends/winrt/characteristic.py
index 8796112b..7847422b 100644
--- a/bleak/backends/winrt/characteristic.py
+++ b/bleak/backends/winrt/characteristic.py
@@ -67,7 +67,7 @@ def __init__(self, obj: GattCharacteristicProperties):
self.__descriptors = []
self.__props = [
_GattCharacteristicsPropertiesMap[v][0]
- for v in [2 ** n for n in range(10)]
+ for v in [2**n for n in range(10)]
if (self.obj.characteristic_properties & v)
]
diff --git a/bleak/backends/winrt/client.py b/bleak/backends/winrt/client.py
index e2e30aaf..0005e9b6 100644
--- a/bleak/backends/winrt/client.py
+++ b/bleak/backends/winrt/client.py
@@ -31,6 +31,7 @@
GattSession,
)
from bleak_winrt.windows.devices.enumeration import (
+ DeviceInformation,
DevicePairingKinds,
DevicePairingResultStatus,
DeviceUnpairingResultStatus,
@@ -350,15 +351,18 @@ async def pair(self, protection_level: int = None, **kwargs) -> bool:
Boolean regarding success of pairing.
"""
-
+ # New local device information object created since the object from the requester isn't updated
+ device_information = await DeviceInformation.create_from_id_async(
+ self._requester.device_information.id
+ )
if (
- self._requester.device_information.pairing.can_pair
- and not self._requester.device_information.pairing.is_paired
+ device_information.pairing.can_pair
+ and not device_information.pairing.is_paired
):
# Currently only supporting Just Works solutions...
ceremony = DevicePairingKinds.CONFIRM_ONLY
- custom_pairing = self._requester.device_information.pairing.custom
+ custom_pairing = device_information.pairing.custom
def handler(sender, args):
args.accept()
@@ -395,7 +399,7 @@ def handler(sender, args):
)
return True
else:
- return self._requester.device_information.pairing.is_paired
+ return device_information.pairing.is_paired
async def unpair(self) -> bool:
"""Attempts to unpair from the device.
@@ -407,10 +411,12 @@ async def unpair(self) -> bool:
"""
- if self._requester.device_information.pairing.is_paired:
- unpairing_result = (
- await self._requester.device_information.pairing.unpair_async()
- )
+ # New local device information object created since the object from the requester isn't updated
+ device_information = await DeviceInformation.create_from_id_async(
+ self._requester.device_information.id
+ )
+ if device_information.pairing.is_paired:
+ unpairing_result = await device_information.pairing.unpair_async()
if unpairing_result.status not in (
DevicePairingResultStatus.PAIRED,
@@ -426,7 +432,7 @@ async def unpair(self) -> bool:
logger.info("Unpaired with device.")
return True
- return not self._requester.device_information.pairing.is_paired
+ return not device_information.pairing.is_paired
# GATT services methods
diff --git a/docs/conf.py b/docs/conf.py
index a1ebce93..f4bc9f0a 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -73,8 +73,8 @@
master_doc = "index"
# General information about the project.
-project = u"bleak"
-copyright = u"2020, Henrik Blidh"
+project = "bleak"
+copyright = "2020, Henrik Blidh"
# The version info for the project you're documenting, acts as replacement
# for |version| and |release|, also used in various other places throughout
@@ -225,7 +225,7 @@
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
- ("index", "bleak.tex", u"bleak Documentation", u"Henrik Blidh", "manual")
+ ("index", "bleak.tex", "bleak Documentation", "Henrik Blidh", "manual")
]
# The name of an image file (relative to this directory) to place at
@@ -253,7 +253,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [("index", "bleak", u"bleak Documentation", [u"Henrik Blidh"], 1)]
+man_pages = [("index", "bleak", "bleak Documentation", ["Henrik Blidh"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -268,8 +268,8 @@
(
"index",
"bleak",
- u"bleak Documentation",
- u"Henrik Blidh",
+ "bleak Documentation",
+ "Henrik Blidh",
"bleak",
"One line description of project.",
"Miscellaneous",
diff --git a/docs/images/macos-privacy-bluetooth.png b/docs/images/macos-privacy-bluetooth.png
new file mode 100644
index 00000000..7e8744c5
Binary files /dev/null and b/docs/images/macos-privacy-bluetooth.png differ
diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst
index 6c9fa0a5..523420d3 100644
--- a/docs/troubleshooting.rst
+++ b/docs/troubleshooting.rst
@@ -15,6 +15,50 @@ crash with an ``ImportError`` similar to::
To fix the error, change the name of the script to something other than ``bleak.py``.
+----------
+macOS Bugs
+----------
+
+Bleak crashes with SIGABRT on macOS
+===================================
+
+If you see a crash similar to this::
+
+ Crashed Thread: 1 Dispatch queue: com.apple.root.default-qos
+
+ Exception Type: EXC_CRASH (SIGABRT)
+ Exception Codes: 0x0000000000000000, 0x0000000000000000
+ Exception Note: EXC_CORPSE_NOTIFY
+
+ Termination Reason: Namespace TCC, Code 0
+ This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.
+
+It is not a problem with Bleak. It is a problem with your terminal application.
+
+Ideally, the terminal application should be fixed by adding ``NSBluetoothAlwaysUsageDescription``
+to the ``Info.plist`` file (`example `_).
+
+It is also possible to manually add the app to the list of Bluetooth apps in
+the *Privacy* settings in the macOS *System Preferences*.
+
+.. image:: images/macos-privacy-bluetooth.png
+
+
+No devices found when scanning on macOS 12
+==========================================
+
+A bug was introduced in macOS 12.0 that causes scanning to not work unless a
+list of service UUIDs is provided to ``BleakScanner``. This bug was fixed in
+macOS 12.3. On the affected version, users of bleak will see the following
+error logged:
+
+.. code-block:: none
+
+ macOS 12.0, 12.1 and 12.2 require non-empty service_uuids kwarg, otherwise no advertisement data will be received
+
+See `#635 `_ and
+`#720 `_ for more information
+including some partial workarounds if you need to support these macOS versions.
--------------
Enable Logging
@@ -38,6 +82,63 @@ Windows Command Prompt::
Then run your Python script in the same terminal.
+-----------------------------------------------
+Connecting to multiple devices at the same time
+-----------------------------------------------
+
+If you're having difficulty connecting to multiple devices, try to do a scan first and
+pass the returned ``BLEDevice`` objects to ``BleakClient`` calls.
+
+Python::
+
+ import asyncio
+ from typing import Sequence
+
+ from bleak import BleakClient, BleakScanner
+ from bleak.backends.device import BLEDevice
+
+
+ async def find_all_devices_services()
+ scanner = BleakScanner()
+ devices: Sequence[BLEDevice] = scanner.discover(timeout=5.0)
+ for d in devices:
+ async with BleakClient(d) as client:
+ print(await client.get_services())
+
+
+ asyncio.run(find_all_devices_services())
+
+
+-----------------------------------------
+Pass more parameters to a notify callback
+-----------------------------------------
+
+If you need a way to pass more parameters to the notify callback, please use
+``functools.partial`` to pass in more arguments.
+
+Issue #759 might fix this in the future.
+
+Python::
+
+ from functools import partial
+
+ from bleak import BleakClient
+
+
+ def my_notification_callback_with_client_input(
+ client: BleakClient, sender: int, data: bytearray
+ ):
+ """Notification callback with client awareness"""
+ print(
+ f"Notification from device with address {client.address} and characteristic with handle {client.services.get_characteristic(sender)}. Data: {data}"
+ )
+
+ # [...]
+
+ await client.start_notify(
+ char_specifier, partial(my_notification_callback_with_client_input, client)
+ )
+
-------------------------
Capture Bluetooth Traffic
-------------------------
diff --git a/examples/detection_callback.py b/examples/detection_callback.py
index c3363769..a1e8326e 100644
--- a/examples/detection_callback.py
+++ b/examples/detection_callback.py
@@ -10,13 +10,11 @@
import asyncio
import logging
-import platform
import sys
from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
-from bleak.uuids import uuid16_dict, uuid128_dict
logger = logging.getLogger(__name__)
@@ -26,20 +24,6 @@ def simple_callback(device: BLEDevice, advertisement_data: AdvertisementData):
async def main(service_uuids):
- mac_ver = platform.mac_ver()[0].split(".")
- if mac_ver[0] and int(mac_ver[0]) >= 12 and not service_uuids:
- # In macOS 12 Monterey the service_uuids need to be specified. As a
- # workaround for this example program, we scan for all known UUIDs to
- # increse the chance of at least something showing up. However, in a
- # "real" program, only the device-specific advertised UUID should be
- # used. Devices that don't advertize at least one service UUID cannot
- # currently be detected.
- logger.warning(
- "Scanning using all known service UUIDs to work around a macOS 12 bug. Some devices may not be detected. Please report this to Apple using the Feedback Assistant app and reference ."
- )
- for item in uuid16_dict:
- service_uuids.append("{0:04x}".format(item))
- service_uuids.extend(uuid128_dict.keys())
scanner = BleakScanner(service_uuids=service_uuids)
scanner.register_detection_callback(simple_callback)
diff --git a/requirements_dev.txt b/requirements_dev.txt
index dca565dc..b9b62312 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -2,7 +2,7 @@ pip>=18.0
bump2version==1.0.1
wheel>=0.32.2
watchdog>=0.8.3
-black>=20.8b1
+black>=22.1.0
flake8>=3.5.0
tox>=3.1.3
coverage>=6.0b1
diff --git a/setup.cfg b/setup.cfg
index 28ba2640..a575a716 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -11,7 +11,7 @@ replace = __version__ = "{new_version}"
universal = 1
[flake8]
-exclude = docs,.venv,*.pyi,.buildozer
+exclude = docs,.venv,*.pyi,.buildozer,build,dist,.eggs
ignore = E203,E501,W503
[aliases]