Skip to content

Commit b6ab166

Browse files
authored
Merge pull request #1257 from hbldh/release/0.20.0
Release/v0.20.0
2 parents b8f22bc + 1b93859 commit b6ab166

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1556
-1056
lines changed

.github/workflows/build_and_test.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ jobs:
1111
name: "Build and test"
1212
runs-on: ${{ matrix.os }}
1313
strategy:
14+
fail-fast: false
1415
matrix:
1516
os: [ubuntu-latest, windows-latest, macos-latest]
1617
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
1718
steps:
18-
- uses: actions/checkout@v2
19+
- uses: actions/checkout@v3
1920
- name: Set up Python ${{ matrix.python-version }}
2021
uses: actions/setup-python@v4
2122
id: py
@@ -24,6 +25,9 @@ jobs:
2425
- name: Create virtual environment
2526
run: pipx run poetry env use '${{ steps.py.outputs.python-path }}'
2627
- name: Install dependencies
28+
# work around https://github.com/python-poetry/poetry/issues/7161
29+
env:
30+
SYSTEM_VERSION_COMPAT: 0
2731
run: pipx run poetry install --only main,test
2832
- name: Test with pytest
2933
run: |

.github/workflows/build_android.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ on: workflow_dispatch
55
jobs:
66
build_android:
77
name: "Build Android"
8-
runs-on: ubuntu-20.04
8+
runs-on: ubuntu-22.04
99
steps:
10-
- uses: actions/checkout@v2
10+
- uses: actions/checkout@v3
1111
- name: Install dependencies
1212
run: pip install buildozer cython
1313
- name: Cache buildozer files
14-
uses: actions/cache@v2
14+
uses: actions/cache@v3
1515
id: buildozer-cache
1616
with:
1717
path: |

.github/workflows/format_and_lint.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ jobs:
1111
name: "Format and lint"
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v3
1515
- name: Set up Python
16-
uses: actions/setup-python@v2
16+
uses: actions/setup-python@v4
1717
- name: Install development dependencies
1818
run: pipx run poetry install --only docs,lint
1919
- name: Check code formatting with black

.github/workflows/pypi-publish.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414

1515
steps:
16-
- uses: actions/checkout@v2
16+
- uses: actions/checkout@v3
1717
- name: Build
1818
run: pipx run poetry build
1919
- name: Publish

CHANGELOG.rst

+43-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,47 @@ All notable changes to this project will be documented in this file.
77
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
88
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
99

10+
`Unreleased`_
11+
=============
12+
13+
`0.20.0`_ (2023-03-17)
14+
======================
15+
16+
Added
17+
-----
18+
* Added ``BLEAK_DBUS_AUTH_UID`` environment variable for hardcoding D-Bus UID. Merged #1182.
19+
* Added return type ``None`` to some scanner methods.
20+
* Added optional hack to use Bluetooth address instead of UUID on macOS. Merged #1073.
21+
* Added ``BleakScanner.find_device_by_name()`` class method.
22+
* Added optional command line argument to use debug log level to all applicable examples.
23+
* Added ``bleak.uuids.normalize_uuid_str()`` function.
24+
* Added optional ``services`` argument to ``BleakClient()`` to filter services. Merged #654.
25+
* Added automatic retry on ``le-connection-abort-by-local`` in BlueZ backend. Fixes #1220.
26+
27+
Changed
28+
-------
29+
* Dropped ``async-timeout`` dependency on Python >= 3.11.
30+
* Deprecated ``BLEDevice.rssi`` and ``BLEDevice.metadata``. Fixes #1025.
31+
* ``BLEDevice`` now uses ``__slots__`` to reduce memory usage. Merged #1117.
32+
* ``BaseBleakClient.services`` is now ``None`` instead of empty service collection
33+
until services are discovered.
34+
* Include thread name in ``BLEAK_LOGGING`` output. Merged #1144.
35+
* Updated PyObjC dependency on macOS to v9.x.
36+
37+
Fixed
38+
-----
39+
* Fixed invalid UTF-8 in ``uuids.uuid16_dict``.
40+
* Fixed ``AttributeError`` in ``_ensure_success`` in WinRT backend.
41+
* Fixed ``BleakScanner.stop()`` can raise ``BleakDBusError`` with ``org.bluez.Error.NotReady`` in BlueZ backend.
42+
* Fixed ``BleakScanner.stop()`` hanging in WinRT backend when Bluetooth is disabled.
43+
* Fixed leaking services when ``get_services()`` is cancelled in WinRT backend.
44+
* Fixed disconnect monitor task not always cancelled on the BlueZ client. Merged #1159.
45+
* Fixed WinRT scanner never calling ``detection_callback`` when a device does
46+
not send a scan response. Fixes #1211.
47+
* Fixed ``BLEDevice`` name sometimes incorrectly ``None``.
48+
* Fixed unhandled exception in ``CentralManagerDelegate`` destructor on macOS. Fixes #1219.
49+
* Fixed object passed to ``disconnected_callback`` is not ``BleakClient``. Fixes #1200.
50+
1051
`0.19.5`_ (2022-11-19)
1152
======================
1253

@@ -880,7 +921,8 @@ Fixed
880921
* Bleak created.
881922

882923

883-
.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.19.5...develop
924+
.. _Unreleased: https://github.com/hbldh/bleak/compare/v0.20.0...develop
925+
.. _0.20.0: https://github.com/hbldh/bleak/compare/v0.19.5...v0.20.0
884926
.. _0.19.5: https://github.com/hbldh/bleak/compare/v0.19.4...v0.19.5
885927
.. _0.19.4: https://github.com/hbldh/bleak/compare/v0.19.3...v0.19.4
886928
.. _0.19.3: https://github.com/hbldh/bleak/compare/v0.19.2...v0.19.3

bleak/__init__.py

+74-13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Awaitable,
2020
Callable,
2121
Dict,
22+
Iterable,
2223
List,
2324
Optional,
2425
Tuple,
@@ -28,7 +29,10 @@
2829
)
2930
from warnings import warn
3031

31-
import async_timeout
32+
if sys.version_info < (3, 11):
33+
from async_timeout import timeout as async_timeout
34+
else:
35+
from asyncio import timeout as async_timeout
3236

3337
if sys.version_info[:2] < (3, 8):
3438
from typing_extensions import Literal
@@ -47,16 +51,18 @@
4751
)
4852
from .backends.service import BleakGATTServiceCollection
4953
from .exc import BleakError
54+
from .uuids import normalize_uuid_str
5055

5156
if TYPE_CHECKING:
5257
from .backends.bluezdbus.scanner import BlueZScannerArgs
58+
from .backends.corebluetooth.scanner import CBScannerArgs
5359
from .backends.winrt.client import WinRTClientArgs
5460

5561

5662
_logger = logging.getLogger(__name__)
5763
_logger.addHandler(logging.NullHandler())
5864
if bool(os.environ.get("BLEAK_LOGGING", False)):
59-
FORMAT = "%(asctime)-15s %(name)-8s %(levelname)s: %(message)s"
65+
FORMAT = "%(asctime)-15s %(name)-8s %(threadName)s %(levelname)s: %(message)s"
6066
handler = logging.StreamHandler(sys.stdout)
6167
handler.setLevel(logging.DEBUG)
6268
handler.setFormatter(logging.Formatter(fmt=FORMAT))
@@ -89,12 +95,21 @@ class BleakScanner:
8995
:class:`BleakError` if set to ``"passive"`` on macOS.
9096
bluez:
9197
Dictionary of arguments specific to the BlueZ backend.
98+
cb:
99+
Dictionary of arguments specific to the CoreBluetooth backend.
92100
backend:
93101
Used to override the automatically selected backend (i.e. for a
94102
custom backend).
95103
**kwargs:
96104
Additional args for backwards compatibility.
97105
106+
.. tip:: The first received advertisement in ``detection_callback`` may or
107+
may not include scan response data if the remote device supports it.
108+
Be sure to take this into account when handing the callback. For example,
109+
the scan response often contains the local name of the device so if you
110+
are matching a device based on other data but want to display the local
111+
name to the user, be sure to wait for ``adv_data.local_name is not None``.
112+
98113
.. versionchanged:: 0.15.0
99114
``detection_callback``, ``service_uuids`` and ``scanning_mode`` are no longer keyword-only.
100115
Added ``bluez`` parameter.
@@ -111,6 +126,7 @@ def __init__(
111126
scanning_mode: Literal["active", "passive"] = "active",
112127
*,
113128
bluez: BlueZScannerArgs = {},
129+
cb: CBScannerArgs = {},
114130
backend: Optional[Type[BaseBleakScanner]] = None,
115131
**kwargs,
116132
):
@@ -119,7 +135,12 @@ def __init__(
119135
)
120136

121137
self._backend = PlatformBleakScanner(
122-
detection_callback, service_uuids, scanning_mode, bluez=bluez, **kwargs
138+
detection_callback,
139+
service_uuids,
140+
scanning_mode,
141+
bluez=bluez,
142+
cb=cb,
143+
**kwargs,
123144
)
124145

125146
async def __aenter__(self):
@@ -188,7 +209,7 @@ async def discover(
188209
@overload
189210
@classmethod
190211
async def discover(
191-
cls, timeout: float = 5.0, *, return_adv: Literal[True], **kwargs
212+
cls, timeout: float = 5.0, *, return_adv: Literal[True] = True, **kwargs
192213
) -> Dict[str, Tuple[BLEDevice, AdvertisementData]]:
193214
...
194215

@@ -272,11 +293,9 @@ async def find_device_by_address(
272293
"""Obtain a ``BLEDevice`` for a BLE server specified by Bluetooth address or (macOS) UUID address.
273294
274295
Args:
275-
device_identifier (str): The Bluetooth/UUID address of the Bluetooth peripheral sought.
276-
timeout (float): Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
277-
278-
Keyword Args:
279-
adapter (str): Bluetooth adapter to use for discovery.
296+
device_identifier: The Bluetooth/UUID address of the Bluetooth peripheral sought.
297+
timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
298+
**kwargs: additional args passed to the :class:`BleakScanner` constructor.
280299
281300
Returns:
282301
The ``BLEDevice`` sought or ``None`` if not detected.
@@ -289,6 +308,28 @@ async def find_device_by_address(
289308
**kwargs,
290309
)
291310

311+
@classmethod
312+
async def find_device_by_name(
313+
cls, name: str, timeout: float = 10.0, **kwargs
314+
) -> Optional[BLEDevice]:
315+
"""Obtain a ``BLEDevice`` for a BLE server specified by the local name in the advertising data.
316+
317+
Args:
318+
name: The name sought.
319+
timeout: Optional timeout to wait for detection of specified peripheral before giving up. Defaults to 10.0 seconds.
320+
**kwargs: additional args passed to the :class:`BleakScanner` constructor.
321+
322+
Returns:
323+
The ``BLEDevice`` sought or ``None`` if not detected.
324+
325+
.. versionadded:: 0.20.0
326+
"""
327+
return await cls.find_device_by_filter(
328+
lambda d, ad: ad.local_name == name,
329+
timeout=timeout,
330+
**kwargs,
331+
)
332+
292333
@classmethod
293334
async def find_device_by_filter(
294335
cls, filterfunc: AdvertisementDataFilter, timeout: float = 10.0, **kwargs
@@ -322,7 +363,7 @@ def apply_filter(d: BLEDevice, ad: AdvertisementData):
322363

323364
async with cls(detection_callback=apply_filter, **kwargs):
324365
try:
325-
async with async_timeout.timeout(timeout):
366+
async with async_timeout(timeout):
326367
return await found_device_queue.get()
327368
except asyncio.TimeoutError:
328369
return None
@@ -345,6 +386,12 @@ class BleakClient:
345386
Callback that will be scheduled in the event loop when the client is
346387
disconnected. The callable must take one argument, which will be
347388
this client object.
389+
services:
390+
Optional list of services to filter. If provided, only these services
391+
will be resolved. This may or may not reduce the time needed to
392+
enumerate the services depending on if the OS supports such filtering
393+
in the Bluetooth stack or not (should affect Windows and Mac).
394+
These can be 16-bit or 128-bit UUIDs.
348395
timeout:
349396
Timeout in seconds passed to the implicit ``discover`` call when
350397
``address_or_ble_device`` is not a :class:`BLEDevice`. Defaults to 10.0.
@@ -381,6 +428,7 @@ def __init__(
381428
self,
382429
address_or_ble_device: Union[BLEDevice, str],
383430
disconnected_callback: Optional[Callable[[BleakClient], None]] = None,
431+
services: Optional[Iterable[str]] = None,
384432
*,
385433
timeout: float = 10.0,
386434
winrt: WinRTClientArgs = {},
@@ -393,7 +441,12 @@ def __init__(
393441

394442
self._backend = PlatformBleakClient(
395443
address_or_ble_device,
396-
disconnected_callback=disconnected_callback,
444+
disconnected_callback=None
445+
if disconnected_callback is None
446+
else functools.partial(disconnected_callback, self),
447+
services=None
448+
if services is None
449+
else set(map(normalize_uuid_str, services)),
397450
timeout=timeout,
398451
winrt=winrt,
399452
**kwargs,
@@ -456,7 +509,9 @@ def set_disconnected_callback(
456509
FutureWarning,
457510
stacklevel=2,
458511
)
459-
self._backend.set_disconnected_callback(callback, **kwargs)
512+
self._backend.set_disconnected_callback(
513+
None if callback is None else functools.partial(callback, self), **kwargs
514+
)
460515

461516
async def connect(self, **kwargs) -> bool:
462517
"""Connect to the specified GATT server.
@@ -545,7 +600,13 @@ def services(self) -> BleakGATTServiceCollection:
545600
Gets the collection of GATT services available on the device.
546601
547602
The returned value is only valid as long as the device is connected.
603+
604+
Raises:
605+
BleakError: if service discovery has not been performed yet during this connection.
548606
"""
607+
if not self._backend.services:
608+
raise BleakError("Service Discovery has not been performed yet")
609+
549610
return self._backend.services
550611

551612
# I/O methods
@@ -608,7 +669,7 @@ async def start_notify(
608669
609670
.. code-block:: python
610671
611-
def callback(sender: int, data: bytearray):
672+
def callback(sender: BleakGATTCharacteristic, data: bytearray):
612673
print(f"{sender}: {data}")
613674
614675
client.start_notify(char_uuid, callback)

0 commit comments

Comments
 (0)