Skip to content

Commit 75f7469

Browse files
authored
Merge pull request #1861 from hbldh/release/v2.0.0
v2.0.0
2 parents 290746f + 718e5d7 commit 75f7469

37 files changed

+1864
-999
lines changed

.github/pull_request_template.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ Before you submit a pull request, check that it meets these guidelines:
66
1. If the pull request adds functionality, the docs should be updated.
77
2. Modify the `CHANGELOG.rst`, describing your changes as is specified by the
88
guidelines in that document.
9-
3. The pull request should work for Python 3.9+ on the following platforms:
9+
3. The pull request should work for Python 3.10+ on the following platforms:
1010
- Windows 10, version 16299 (Fall Creators Update) and greater
1111
- Linux distributions with BlueZ >= 5.55
12-
- OS X / macOS >= 10.13
12+
- OS X / macOS >= 10.15
1313
4. Squash all your commits on your PR branch, if the commits are not solving
1414
different problems and you are committing them in the same PR. In that case,
1515
consider making several PRs instead.

.github/workflows/build_and_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
os: [ubuntu-latest, windows-latest, macos-latest]
19-
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
19+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
2020
steps:
2121
- uses: actions/checkout@v4
2222
- name: Set up Python ${{ matrix.python-version }}

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@
1010
"isort.importStrategy": "fromEnvironment",
1111
"isort.args":["--profile", "black"],
1212
"flake8.importStrategy": "fromEnvironment",
13-
"python.analysis.typeCheckingMode": "strict"
13+
"python.analysis.typeCheckingMode": "strict",
14+
"python-envs.defaultEnvManager": "ms-python.python:poetry",
15+
"python-envs.defaultPackageManager": "ms-python.python:poetry",
16+
"python-envs.pythonProjects": []
1417
}

CHANGELOG.rst

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,67 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
1010
`Unreleased`_
1111
=============
1212

13+
`2.0.0`_ (2025-11-22)
14+
=====================
15+
16+
Added
17+
-----
18+
* Added ``bleak.backends.get_default_backend()`` and ``BleakBackend`` enum for a centralized backend detection.
19+
* Added ``BleakClient().backend_id`` and ``BleakScanner().backend_id`` properties to identify the backend in use.
20+
21+
Changed
22+
-------
23+
* Use ``"AcquireNotify"`` rather than ``"StartNotify"`` for Linux backend on supported characteristics
24+
* Allow multiple calls to ``disconnect()`` on Windows to align behavior over all backends.
25+
* Raise new ``BleakBluetoothNotAvailableError`` when Bluetooth is not supported, turned off or permission is denied.
26+
27+
Fixed
28+
-----
29+
* Fixed potential race condition causing timeout while connecting in WinRT backend.
30+
* Fixed file handle leak in BlueZ backend when D-Bus connection is lost and re-established.
31+
* Fixed crash in CoreBluetooth backend if an ObjC delegate callback is called after the asyncio run loop stops.
32+
* Fixed possible deadlock when starting scanning on Windows when Bluetooth is turned off.
33+
* Fixed "Bluetooth device is turned off" Exception on macOS, when a Bluetooth permission request popup is shown to the user by the OS.
34+
35+
Removed
36+
-------
37+
* Removed support for Python 3.9.
38+
* Removed support for macOS < 10.15.
39+
1340
`1.1.1`_ (2025-09-07)
1441
=====================
1542

1643
Fixed
1744
-----
18-
- Fixed D-Bus connection leak on connection failure in BlueZ backend.
19-
- Fixed characteristic's max write without response size using wrong characteristic's value. Fixes #1820.
20-
- Fixed ``AttributeError`` in Python4Android backend when accessing ``is_connected`` before connecting. Fixes #1791.
45+
* Fixed D-Bus connection leak on connection failure in BlueZ backend.
46+
* Fixed characteristic's max write without response size using wrong characteristic's value. Fixes #1820.
47+
* Fixed ``AttributeError`` in Python4Android backend when accessing ``is_connected`` before connecting. Fixes #1791.
2148

2249
`1.1.0`_ (2025-08-10)
2350
=====================
2451

2552
Added
2653
-----
27-
- Added support for Pythonista iOS app backend.
28-
- Added ``BleakClient.name`` property for getting the peripheral's name. Fixes #1802.
54+
* Added support for Pythonista iOS app backend.
55+
* Added ``BleakClient.name`` property for getting the peripheral's name. Fixes #1802.
2956

3057
Fixed
3158
-----
32-
- Fixed ``BleakClient.connect()`` on Android when service characteristics have descriptors. Fixes #1803.
33-
- Fixed disconnect callback not called on Windows when Bleak initiates disconnection.
59+
* Fixed ``BleakClient.connect()`` on Android when service characteristics have descriptors. Fixes #1803.
60+
* Fixed disconnect callback not called on Windows when Bleak initiates disconnection.
3461

3562
`1.0.1`_ (2025-06-30)
3663
=====================
3764

3865
Changed
3966
-------
40-
- Added deprecation warnings for importing ``bleak.args.*`` types from ``bleak.backends.*``.
67+
* Added deprecation warnings for importing ``bleak.args.*`` types from ``bleak.backends.*``.
4168

4269
Fixed
4370
-----
4471

45-
- Restored ``**kwargs`` in ``BLEDevice()`` constructor. Fixes #1783.
46-
- Restored importing ``OrPattern`` from ``bleak.backends.bluezdbus.advertisement_monitor``.
72+
* Restored ``**kwargs`` in ``BLEDevice()`` constructor. Fixes #1783.
73+
* Restored importing ``OrPattern`` from ``bleak.backends.bluezdbus.advertisement_monitor``.
4774

4875

4976
`1.0.0`_ (2025-06-28)
@@ -1120,7 +1147,8 @@ Fixed
11201147
* Bleak created.
11211148

11221149

1123-
.. _Unreleased: https://github.com/hbldh/bleak/compare/v1.1.1...develop
1150+
.. _Unreleased: https://github.com/hbldh/bleak/compare/v2.0.0...develop
1151+
.. _2.0.0: https://github.com/hbldh/bleak/compare/v1.1.1...v2.0.0
11241152
.. _1.1.1: https://github.com/hbldh/bleak/compare/v1.1.0...v1.1.1
11251153
.. _1.1.0: https://github.com/hbldh/bleak/compare/v1.0.1...v1.1.0
11261154
.. _1.0.1: https://github.com/hbldh/bleak/compare/v1.0.0...v1.0.1

CONTRIBUTING.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ Before you submit a pull request, check that it meets these guidelines:
102102
1. If the pull request adds functionality, the docs should be updated.
103103
2. Modify the ``CHANGELOG.rst``, describing your changes as is specified by the
104104
guidelines in that document.
105-
3. The pull request should work for Python 3.9+ on the following platforms:
105+
3. The pull request should work for Python 3.10+ on the following platforms:
106106
- Windows 10, version 16299 (Fall Creators Update) and greater
107107
- Linux distributions with BlueZ >= 5.55
108-
- OS X / macOS >= 10.13
108+
- OS X / macOS >= 10.15
109109
4. Squash all your commits on your PR branch, if the commits are not solving
110110
different problems and you are committing them in the same PR. In that case,
111111
consider making several PRs instead.

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Features
4747

4848
* Supports Windows 10, version 16299 (Fall Creators Update) or greater
4949
* Supports Linux distributions with BlueZ >= 5.55
50-
* OS X/macOS support via Core Bluetooth API, from at least OS X version 10.13
50+
* OS X/macOS support via Core Bluetooth API, from at least OS X version 10.15
5151
* Android backend compatible with python-for-android
5252

5353
Bleak supports reading, writing and getting notifications from

bleak/__init__.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from bleak.args.bluez import BlueZScannerArgs
3434
from bleak.args.corebluetooth import CBScannerArgs, CBStartNotifyArgs
3535
from bleak.args.winrt import WinRTClientArgs
36+
from bleak.backends import BleakBackend
3637
from bleak.backends.characteristic import BleakGATTCharacteristic
3738
from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type
3839
from bleak.backends.descriptor import BleakGATTDescriptor
@@ -60,7 +61,7 @@
6061

6162

6263
# prevent tasks from being garbage collected
63-
_background_tasks = set[asyncio.Task[None]]()
64+
_background_tasks: set[asyncio.Task[None]] = set()
6465

6566

6667
class BleakScanner:
@@ -123,8 +124,10 @@ def __init__(
123124
backend: Optional[type[BaseBleakScanner]] = None,
124125
**kwargs: Any,
125126
) -> None:
126-
PlatformBleakScanner = (
127-
get_platform_scanner_backend_type() if backend is None else backend
127+
PlatformBleakScanner, backend_id = (
128+
get_platform_scanner_backend_type()
129+
if backend is None
130+
else (backend, backend.__name__)
128131
)
129132

130133
self._backend = PlatformBleakScanner(
@@ -135,6 +138,19 @@ def __init__(
135138
cb=cb,
136139
**kwargs,
137140
) # type: ignore
141+
self._backend_id = backend_id
142+
143+
@property
144+
def backend_id(self) -> BleakBackend | str:
145+
"""
146+
Gets the identifier of the backend in use.
147+
148+
The value is one of the :class:`BleakBackend` enum values in case of
149+
built-in backends, or a string identifying a custom backend.
150+
151+
.. versionadded:: 2.0
152+
"""
153+
return self._backend_id
138154

139155
async def __aenter__(self) -> Self:
140156
await self._backend.start()
@@ -149,7 +165,17 @@ async def __aexit__(
149165
await self._backend.stop()
150166

151167
async def start(self) -> None:
152-
"""Start scanning for devices"""
168+
"""
169+
Start scanning for devices.
170+
171+
Raises:
172+
BleakBluetoothNotAvailableError:
173+
if Bluetooth is not currently available
174+
175+
.. versionchanged:: 2.0
176+
Now raises :class:`BleakBluetoothNotAvailableError` instead of :class:`BleakError`
177+
when Bluetooth is not currently available.
178+
"""
153179
await self._backend.start()
154180

155181
async def stop(self) -> None:
@@ -170,7 +196,7 @@ async def advertisement_data(
170196
171197
.. versionadded:: 0.21
172198
"""
173-
devices = asyncio.Queue[tuple[BLEDevice, AdvertisementData]]()
199+
devices: asyncio.Queue[tuple[BLEDevice, AdvertisementData]] = asyncio.Queue()
174200

175201
unregister_callback = self._backend.register_detection_callback(
176202
lambda bd, ad: devices.put_nowait((bd, ad))
@@ -490,8 +516,10 @@ def __init__(
490516
backend: Optional[type[BaseBleakClient]] = None,
491517
**kwargs: Any,
492518
) -> None:
493-
PlatformBleakClient = (
494-
get_platform_client_backend_type() if backend is None else backend
519+
PlatformBleakClient, backend_id = (
520+
get_platform_client_backend_type()
521+
if backend is None
522+
else (backend, backend.__name__)
495523
)
496524

497525
self._backend = PlatformBleakClient(
@@ -509,6 +537,19 @@ def __init__(
509537
**kwargs,
510538
)
511539
self._pair_before_connect = pair
540+
self._backend_id = backend_id
541+
542+
@property
543+
def backend_id(self) -> BleakBackend | str:
544+
"""
545+
Gets the identifier of the backend in use.
546+
547+
The value is one of the :class:`BleakBackend` enum values in case of
548+
built-in backends, or a string identifying a custom backend.
549+
550+
.. versionadded:: 2.0
551+
"""
552+
return self._backend_id
512553

513554
# device info
514555

@@ -523,6 +564,8 @@ def name(self) -> str:
523564
a Bluetooth address separated with dashes (``-``) instead of colons
524565
(``:``) (or a UUID on Apple devices). It may also be possible to override
525566
the device name using the OS's Bluetooth settings.
567+
568+
.. versionadded:: 1.1
526569
"""
527570
return self._backend.name
528571

@@ -663,7 +706,7 @@ async def read_gatt_char(
663706
The read data.
664707
665708
Raises:
666-
BleakGattCharacteristicNotFoundError: if a characteristic with the
709+
BleakCharacteristicNotFoundError: if a characteristic with the
667710
handle or UUID specified by ``char_specifier`` could not be found.
668711
backend-specific exceptions: if the read operation failed.
669712
"""
@@ -712,7 +755,7 @@ async def write_gatt_char(
712755
property, which is why an explicit argument is encouraged.
713756
714757
Raises:
715-
BleakGattCharacteristicNotFoundError: if a characteristic with the
758+
BleakCharacteristicNotFoundError: if a characteristic with the
716759
handle or UUID specified by ``char_specifier`` could not be found.
717760
backend-specific exceptions: if the write operation failed.
718761
@@ -771,7 +814,7 @@ def callback(sender: BleakGATTCharacteristic, data: bytearray):
771814
CoreBluetooth specific arguments.
772815
773816
Raises:
774-
BleakGattCharacteristicNotFoundError: if a characteristic with the
817+
BleakCharacteristicNotFoundError: if a characteristic with the
775818
handle or UUID specified by ``char_specifier`` could not be found.
776819
backend-specific exceptions: if the start notification operation failed.
777820
@@ -813,7 +856,7 @@ async def stop_notify(
813856
BleakGATTCharacteristic object representing it.
814857
815858
Raises:
816-
BleakGattCharacteristicNotFoundError: if a characteristic with the
859+
BleakCharacteristicNotFoundError: if a characteristic with the
817860
handle or UUID specified by ``char_specifier`` could not be found.
818861
backend-specific exceptions: if the stop notification operation failed.
819862

bleak/backends/__init__.py

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,75 @@
1-
# -*- coding: utf-8 -*-
2-
# Created on 2017-11-19 by hbldh <[email protected]>
31
"""
4-
__init__.py
2+
Communicating with Bluetooth hardware requires calling OS-specific APIs. These
3+
are abstracted as "backends" in Bleak.
4+
5+
The backend will be automatically selected based on the operating system Bleak
6+
is running on. In some cases, this may also depend on a specific runtime, like
7+
Pythonista on iOS.
58
"""
9+
10+
import enum
11+
import os
12+
import platform
13+
import sys
14+
15+
from bleak.exc import BleakError
16+
17+
18+
class BleakBackend(str, enum.Enum):
19+
"""
20+
Identifiers for available built-in Bleak backends.
21+
22+
.. versionadded:: 2.0
23+
"""
24+
25+
P4ANDROID = "p4android"
26+
"""
27+
Python for Android backend.
28+
"""
29+
30+
BLUEZ_DBUS = "bluez_dbus"
31+
"""
32+
BlueZ D-Bus backend for Linux.
33+
"""
34+
35+
PYTHONISTA_CB = "pythonista_cb"
36+
"""
37+
Pythonista CoreBluetooth backend for iOS and macOS.
38+
"""
39+
40+
CORE_BLUETOOTH = "core_bluetooth"
41+
"""
42+
CoreBluetooth backend for macOS.
43+
"""
44+
45+
WIN_RT = "win_rt"
46+
"""
47+
Windows Runtime backend for Windows.
48+
"""
49+
50+
51+
def get_default_backend() -> BleakBackend:
52+
"""
53+
Returns the preferred backend for the current platform/environment.
54+
55+
.. versionadded:: 2.0
56+
"""
57+
if os.environ.get("P4A_BOOTSTRAP") is not None:
58+
return BleakBackend.P4ANDROID
59+
60+
if platform.system() == "Linux":
61+
return BleakBackend.BLUEZ_DBUS
62+
63+
if sys.platform == "ios" and "Pythonista3.app" in sys.executable:
64+
# Must be resolved before checking for "Darwin" (macOS),
65+
# as both the Pythonista app for iOS and macOS
66+
# return "Darwin" from platform.system()
67+
return BleakBackend.PYTHONISTA_CB
68+
69+
if platform.system() == "Darwin":
70+
return BleakBackend.CORE_BLUETOOTH
71+
72+
if platform.system() == "Windows":
73+
return BleakBackend.WIN_RT
74+
75+
raise BleakError(f"Unsupported platform: {platform.system()}")

0 commit comments

Comments
 (0)