Skip to content

Commit

Permalink
Merge pull request #818 from hbldh/release/0.14.3
Browse files Browse the repository at this point in the history
Release/0.14.3
  • Loading branch information
dlech authored Apr 29, 2022
2 parents d6c208f + 2189eab commit 0361ba4
Show file tree
Hide file tree
Showing 17 changed files with 174 additions and 50 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.

`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)
======================
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pyyaml = "*"
wheel = "*"
watchdog = "*"
coverage = "*"
black = "*"
black = ">=22.1.0"

[pipenv]
allow_prereleases = true
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion bleak/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

__version__ = "0.14.2"
__version__ = "0.14.3"
2 changes: 1 addition & 1 deletion bleak/backends/bluezdbus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions bleak/backends/bluezdbus/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 3 additions & 2 deletions bleak/backends/corebluetooth/CentralManagerDelegate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()

Expand Down
2 changes: 1 addition & 1 deletion bleak/backends/corebluetooth/characteristic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
18 changes: 12 additions & 6 deletions bleak/backends/corebluetooth/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -35,18 +35,24 @@ 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):
super(BleakScannerCoreBluetooth, self).__init__(**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 = {}
Expand Down
2 changes: 1 addition & 1 deletion bleak/backends/winrt/characteristic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
]

Expand Down
26 changes: 16 additions & 10 deletions bleak/backends/winrt/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
GattSession,
)
from bleak_winrt.windows.devices.enumeration import (
DeviceInformation,
DevicePairingKinds,
DevicePairingResultStatus,
DeviceUnpairingResultStatus,
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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

Expand Down
12 changes: 6 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -268,8 +268,8 @@
(
"index",
"bleak",
u"bleak Documentation",
u"Henrik Blidh",
"bleak Documentation",
"Henrik Blidh",
"bleak",
"One line description of project.",
"Miscellaneous",
Expand Down
Binary file added docs/images/macos-privacy-bluetooth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 101 additions & 0 deletions docs/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/gnachman/iTerm2/pull/457/commits/626068e026ffb958242034129a1974ff87b21a32>`_).

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 <https://github.com/hbldh/bleak/issues/635>`_ and
`#720 <https://github.com/hbldh/bleak/issues/720>`_ for more information
including some partial workarounds if you need to support these macOS versions.

--------------
Enable Logging
Expand All @@ -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
-------------------------
Expand Down
Loading

0 comments on commit 0361ba4

Please sign in to comment.