Skip to content
Merged

v2.0.0 #1861

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bb56b1a
Merge pull request #1824 from hbldh/master
dlech Sep 7, 2025
a242cfb
use consistent type annotations for generic types
o-murphy Aug 22, 2025
aa9ab9c
backends/bluezdbus/scanner: add assert to avoid type checker error
dlech Sep 7, 2025
2e7192c
use consistent type annotations for generic types
dlech Sep 8, 2025
bb51273
fix exception documentation
patman15 Sep 28, 2025
a4a8755
typo at backend/winrt/scanner.py
Sakth1 Oct 10, 2025
101a693
backends/corebluetooth: drop support for macOS < v10.15
timrid Oct 25, 2025
67f4326
bleak: new BleakBluetoothNotAvailableError exception
timrid Oct 25, 2025
471ff1d
bleak: drop support for Python 3.9
dlech Oct 25, 2025
cd92010
backends/bluezdbus: implement BleakBluetoothNotAvailableError
dlech Oct 25, 2025
63455af
poetry update
dlech Oct 25, 2025
d33fb59
bleak: add more docs for BleakBluetoothNotAvailableError
dlech Oct 25, 2025
e68cbd8
docs: fix wrong versionadded directive syntax
dlech Oct 25, 2025
95c478d
CHANGELOG: use * instead of -
dlech Oct 26, 2025
d2d6201
vscode: settings: add python-env settings
dlech Oct 26, 2025
72a6c71
backends/winrt: check radio status when starting scan
dlech Oct 26, 2025
6892f7c
exc: add NO_BLE_CENTRAL reason
dlech Oct 26, 2025
2a420b9
backends/corebluetooth: Separate delegates from python in preparation…
timrid Oct 26, 2025
c41a61b
backends/corebluetooth/scanner: fix missing return
dlech Oct 26, 2025
4a030ec
backends/corebluetooth: fix some type checking errors
dlech Oct 26, 2025
67b5371
backends/corebluetooth/CentralManagerDelegate: use weakref.finalize
dlech Oct 26, 2025
cb06487
docs: add BleakClient.name
dlech Nov 3, 2025
5d0593d
backends/corebluetooth: avoid crash in delegate callbacks
dlech Nov 4, 2025
a6fea5a
backends: Centralize backend detection
timrid Nov 5, 2025
ea5fe45
backends: winrt: allow multiple `disconnect()` calls
patman15 Nov 22, 2025
520bed7
backends: bluez: use AcquireNotify when possible
TristanOpbroek Nov 22, 2025
0df017d
backends/bluezdbus/manager: Release resources on bus restart
dlech Sep 12, 2025
f16cbf6
backends/winrt/client: fix potential race condition for session status
dlech Sep 19, 2025
2d18e16
poetry: bump dbus-fast to 3.1.2 in the lock file
dlech Nov 22, 2025
718e5d7
v2.0.0
dlech Nov 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ 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.9+ on the following platforms:
3. The pull request should work for Python 3.10+ on the following platforms:
- Windows 10, version 16299 (Fall Creators Update) and greater
- Linux distributions with BlueZ >= 5.55
- OS X / macOS >= 10.13
- OS X / macOS >= 10.15
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.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
"isort.importStrategy": "fromEnvironment",
"isort.args":["--profile", "black"],
"flake8.importStrategy": "fromEnvironment",
"python.analysis.typeCheckingMode": "strict"
"python.analysis.typeCheckingMode": "strict",
"python-envs.defaultEnvManager": "ms-python.python:poetry",
"python-envs.defaultPackageManager": "ms-python.python:poetry",
"python-envs.pythonProjects": []
}
50 changes: 39 additions & 11 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,67 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0
`Unreleased`_
=============

`2.0.0`_ (2025-11-22)
=====================

Added
-----
* Added ``bleak.backends.get_default_backend()`` and ``BleakBackend`` enum for a centralized backend detection.
* Added ``BleakClient().backend_id`` and ``BleakScanner().backend_id`` properties to identify the backend in use.

Changed
-------
* Use ``"AcquireNotify"`` rather than ``"StartNotify"`` for Linux backend on supported characteristics
* Allow multiple calls to ``disconnect()`` on Windows to align behavior over all backends.
* Raise new ``BleakBluetoothNotAvailableError`` when Bluetooth is not supported, turned off or permission is denied.

Fixed
-----
* Fixed potential race condition causing timeout while connecting in WinRT backend.
* Fixed file handle leak in BlueZ backend when D-Bus connection is lost and re-established.
* Fixed crash in CoreBluetooth backend if an ObjC delegate callback is called after the asyncio run loop stops.
* Fixed possible deadlock when starting scanning on Windows when Bluetooth is turned off.
* Fixed "Bluetooth device is turned off" Exception on macOS, when a Bluetooth permission request popup is shown to the user by the OS.

Removed
-------
* Removed support for Python 3.9.
* Removed support for macOS < 10.15.

`1.1.1`_ (2025-09-07)
=====================

Fixed
-----
- Fixed D-Bus connection leak on connection failure in BlueZ backend.
- Fixed characteristic's max write without response size using wrong characteristic's value. Fixes #1820.
- Fixed ``AttributeError`` in Python4Android backend when accessing ``is_connected`` before connecting. Fixes #1791.
* Fixed D-Bus connection leak on connection failure in BlueZ backend.
* Fixed characteristic's max write without response size using wrong characteristic's value. Fixes #1820.
* Fixed ``AttributeError`` in Python4Android backend when accessing ``is_connected`` before connecting. Fixes #1791.

`1.1.0`_ (2025-08-10)
=====================

Added
-----
- Added support for Pythonista iOS app backend.
- Added ``BleakClient.name`` property for getting the peripheral's name. Fixes #1802.
* Added support for Pythonista iOS app backend.
* Added ``BleakClient.name`` property for getting the peripheral's name. Fixes #1802.

Fixed
-----
- Fixed ``BleakClient.connect()`` on Android when service characteristics have descriptors. Fixes #1803.
- Fixed disconnect callback not called on Windows when Bleak initiates disconnection.
* Fixed ``BleakClient.connect()`` on Android when service characteristics have descriptors. Fixes #1803.
* Fixed disconnect callback not called on Windows when Bleak initiates disconnection.

`1.0.1`_ (2025-06-30)
=====================

Changed
-------
- Added deprecation warnings for importing ``bleak.args.*`` types from ``bleak.backends.*``.
* Added deprecation warnings for importing ``bleak.args.*`` types from ``bleak.backends.*``.

Fixed
-----

- Restored ``**kwargs`` in ``BLEDevice()`` constructor. Fixes #1783.
- Restored importing ``OrPattern`` from ``bleak.backends.bluezdbus.advertisement_monitor``.
* Restored ``**kwargs`` in ``BLEDevice()`` constructor. Fixes #1783.
* Restored importing ``OrPattern`` from ``bleak.backends.bluezdbus.advertisement_monitor``.


`1.0.0`_ (2025-06-28)
Expand Down Expand Up @@ -1120,7 +1147,8 @@ Fixed
* Bleak created.


.. _Unreleased: https://github.com/hbldh/bleak/compare/v1.1.1...develop
.. _Unreleased: https://github.com/hbldh/bleak/compare/v2.0.0...develop
.. _2.0.0: https://github.com/hbldh/bleak/compare/v1.1.1...v2.0.0
.. _1.1.1: https://github.com/hbldh/bleak/compare/v1.1.0...v1.1.1
.. _1.1.0: https://github.com/hbldh/bleak/compare/v1.0.1...v1.1.0
.. _1.0.1: https://github.com/hbldh/bleak/compare/v1.0.0...v1.0.1
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ 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.9+ on the following platforms:
3. The pull request should work for Python 3.10+ on the following platforms:
- Windows 10, version 16299 (Fall Creators Update) and greater
- Linux distributions with BlueZ >= 5.55
- OS X / macOS >= 10.13
- OS X / macOS >= 10.15
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.
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Features

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

Bleak supports reading, writing and getting notifications from
Expand Down
65 changes: 54 additions & 11 deletions bleak/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from bleak.args.bluez import BlueZScannerArgs
from bleak.args.corebluetooth import CBScannerArgs, CBStartNotifyArgs
from bleak.args.winrt import WinRTClientArgs
from bleak.backends import BleakBackend
from bleak.backends.characteristic import BleakGATTCharacteristic
from bleak.backends.client import BaseBleakClient, get_platform_client_backend_type
from bleak.backends.descriptor import BleakGATTDescriptor
Expand Down Expand Up @@ -60,7 +61,7 @@


# prevent tasks from being garbage collected
_background_tasks = set[asyncio.Task[None]]()
_background_tasks: set[asyncio.Task[None]] = set()


class BleakScanner:
Expand Down Expand Up @@ -123,8 +124,10 @@ def __init__(
backend: Optional[type[BaseBleakScanner]] = None,
**kwargs: Any,
) -> None:
PlatformBleakScanner = (
get_platform_scanner_backend_type() if backend is None else backend
PlatformBleakScanner, backend_id = (
get_platform_scanner_backend_type()
if backend is None
else (backend, backend.__name__)
)

self._backend = PlatformBleakScanner(
Expand All @@ -135,6 +138,19 @@ def __init__(
cb=cb,
**kwargs,
) # type: ignore
self._backend_id = backend_id

@property
def backend_id(self) -> BleakBackend | str:
"""
Gets the identifier of the backend in use.

The value is one of the :class:`BleakBackend` enum values in case of
built-in backends, or a string identifying a custom backend.

.. versionadded:: 2.0
"""
return self._backend_id

async def __aenter__(self) -> Self:
await self._backend.start()
Expand All @@ -149,7 +165,17 @@ async def __aexit__(
await self._backend.stop()

async def start(self) -> None:
"""Start scanning for devices"""
"""
Start scanning for devices.

Raises:
BleakBluetoothNotAvailableError:
if Bluetooth is not currently available

.. versionchanged:: 2.0
Now raises :class:`BleakBluetoothNotAvailableError` instead of :class:`BleakError`
when Bluetooth is not currently available.
"""
await self._backend.start()

async def stop(self) -> None:
Expand All @@ -170,7 +196,7 @@ async def advertisement_data(

.. versionadded:: 0.21
"""
devices = asyncio.Queue[tuple[BLEDevice, AdvertisementData]]()
devices: asyncio.Queue[tuple[BLEDevice, AdvertisementData]] = asyncio.Queue()

unregister_callback = self._backend.register_detection_callback(
lambda bd, ad: devices.put_nowait((bd, ad))
Expand Down Expand Up @@ -490,8 +516,10 @@ def __init__(
backend: Optional[type[BaseBleakClient]] = None,
**kwargs: Any,
) -> None:
PlatformBleakClient = (
get_platform_client_backend_type() if backend is None else backend
PlatformBleakClient, backend_id = (
get_platform_client_backend_type()
if backend is None
else (backend, backend.__name__)
)

self._backend = PlatformBleakClient(
Expand All @@ -509,6 +537,19 @@ def __init__(
**kwargs,
)
self._pair_before_connect = pair
self._backend_id = backend_id

@property
def backend_id(self) -> BleakBackend | str:
"""
Gets the identifier of the backend in use.

The value is one of the :class:`BleakBackend` enum values in case of
built-in backends, or a string identifying a custom backend.

.. versionadded:: 2.0
"""
return self._backend_id

# device info

Expand All @@ -523,6 +564,8 @@ def name(self) -> str:
a Bluetooth address separated with dashes (``-``) instead of colons
(``:``) (or a UUID on Apple devices). It may also be possible to override
the device name using the OS's Bluetooth settings.

.. versionadded:: 1.1
"""
return self._backend.name

Expand Down Expand Up @@ -663,7 +706,7 @@ async def read_gatt_char(
The read data.

Raises:
BleakGattCharacteristicNotFoundError: if a characteristic with the
BleakCharacteristicNotFoundError: if a characteristic with the
handle or UUID specified by ``char_specifier`` could not be found.
backend-specific exceptions: if the read operation failed.
"""
Expand Down Expand Up @@ -712,7 +755,7 @@ async def write_gatt_char(
property, which is why an explicit argument is encouraged.

Raises:
BleakGattCharacteristicNotFoundError: if a characteristic with the
BleakCharacteristicNotFoundError: if a characteristic with the
handle or UUID specified by ``char_specifier`` could not be found.
backend-specific exceptions: if the write operation failed.

Expand Down Expand Up @@ -771,7 +814,7 @@ def callback(sender: BleakGATTCharacteristic, data: bytearray):
CoreBluetooth specific arguments.

Raises:
BleakGattCharacteristicNotFoundError: if a characteristic with the
BleakCharacteristicNotFoundError: if a characteristic with the
handle or UUID specified by ``char_specifier`` could not be found.
backend-specific exceptions: if the start notification operation failed.

Expand Down Expand Up @@ -813,7 +856,7 @@ async def stop_notify(
BleakGATTCharacteristic object representing it.

Raises:
BleakGattCharacteristicNotFoundError: if a characteristic with the
BleakCharacteristicNotFoundError: if a characteristic with the
handle or UUID specified by ``char_specifier`` could not be found.
backend-specific exceptions: if the stop notification operation failed.

Expand Down
76 changes: 73 additions & 3 deletions bleak/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,75 @@
# -*- coding: utf-8 -*-
# Created on 2017-11-19 by hbldh <[email protected]>
"""
__init__.py
Communicating with Bluetooth hardware requires calling OS-specific APIs. These
are abstracted as "backends" in Bleak.

The backend will be automatically selected based on the operating system Bleak
is running on. In some cases, this may also depend on a specific runtime, like
Pythonista on iOS.
"""

import enum
import os
import platform
import sys

from bleak.exc import BleakError


class BleakBackend(str, enum.Enum):
"""
Identifiers for available built-in Bleak backends.

.. versionadded:: 2.0
"""

P4ANDROID = "p4android"
"""
Python for Android backend.
"""

BLUEZ_DBUS = "bluez_dbus"
"""
BlueZ D-Bus backend for Linux.
"""

PYTHONISTA_CB = "pythonista_cb"
"""
Pythonista CoreBluetooth backend for iOS and macOS.
"""

CORE_BLUETOOTH = "core_bluetooth"
"""
CoreBluetooth backend for macOS.
"""

WIN_RT = "win_rt"
"""
Windows Runtime backend for Windows.
"""


def get_default_backend() -> BleakBackend:
"""
Returns the preferred backend for the current platform/environment.

.. versionadded:: 2.0
"""
if os.environ.get("P4A_BOOTSTRAP") is not None:
return BleakBackend.P4ANDROID

if platform.system() == "Linux":
return BleakBackend.BLUEZ_DBUS

if sys.platform == "ios" and "Pythonista3.app" in sys.executable:
# Must be resolved before checking for "Darwin" (macOS),
# as both the Pythonista app for iOS and macOS
# return "Darwin" from platform.system()
return BleakBackend.PYTHONISTA_CB

if platform.system() == "Darwin":
return BleakBackend.CORE_BLUETOOTH

if platform.system() == "Windows":
return BleakBackend.WIN_RT

raise BleakError(f"Unsupported platform: {platform.system()}")
Loading
Loading