# hbldh/bleak: a cross-platform asyncio GATT client for Python

> bleak is an MIT-licensed Python library that talks to Bluetooth Low Energy devices as a GATT client, with backends for Windows, Linux, macOS and Android. It is for engineers who need BLE reads, writes and notifications inside an asyncio program without writing platform-specific code.

**hbldh/bleak** — A cross platform Bluetooth Low Energy Client for Python using asyncio

- Repository: https://github.com/hbldh/bleak
- Stars: 2,535 · Forks: 371
- Language: Python
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/hbldh-bleak

## What bleak solves for Python developers working with BLE

Bluetooth Low Energy work in Python has traditionally meant choosing between platform glue and a wrapper built for one operating system. bleak is the acronym "Bluetooth Low Energy platform Agnostic Klient" and it is exactly that: a GATT client that connects to BLE devices acting as GATT servers. The README describes the goal as an asynchronous, cross-platform Python API to connect and communicate with devices such as sensors. The audience is narrow and specific. You are writing a Python program that needs to read characteristics, write them, or subscribe to notifications from a peripheral, and you want the same code to run on a laptop and a Linux box without a second implementation. If you are building firmware, or you need a GATT server rather than a client, this is the wrong library.

## How the platform backends are wired together

The mechanism is visible in pyproject.toml rather than in the README. bleak ships one API and selects a backend by platform, with the platform-specific dependencies declared as environment markers. On Darwin it pulls pyobjc-core, pyobjc-framework-CoreBluetooth and pyobjc-framework-libdispatch, so the macOS path goes through the Core Bluetooth API. On Windows it pulls a set of winrt-* packages covering Bluetooth, Bluetooth.Advertisement, Bluetooth.GenericAttributeProfile, Devices.Enumeration, Devices.Radios, Foundation, Foundation.Collections and Storage.Streams. On Linux it pulls dbus-fast, which speaks to BlueZ over D-Bus. Android is handled separately, with the README noting a backend compatible with python-for-android and the repository carrying mypy-android.ini and pyrightconfig-android.json, which suggests the Android path is type-checked under its own configuration rather than the default one. The consequence for you is that installing bleak on Linux does not install WinRT or pyobjc, so the dependency footprint follows the machine rather than the library.

## Installing bleak and running a first scan

Installation is a single pip command, and the README gives it directly. There is no build step, no system package to compile and no service to start.

```bash
pip install bleak
```

After that, the smallest useful program is a device scan. The README's discovery example uses BleakScanner.discover() and prints each device. Run it and you should see a list of nearby BLE devices with their addresses and names.

```python
import asyncio
from bleak import BleakScanner

async def main():
    devices = await BleakScanner.discover()
    for d in devices:
        print(d)

asyncio.run(main())
```

The second README example connects to a device and reads the model number characteristic, using the UUID 2A24 and an address such as 24:71:89:cc:09:05. This is the pattern most applications will follow: open an async context manager on BleakClient, then await a read.

```python
import asyncio
from bleak import BleakClient

address = "24:71:89:cc:09:05"
MODEL_NBR_UUID = "2A24"

async def main(address):
    async with BleakClient(address) as client:
        model_number = await client.read_gatt_char(MODEL_NBR_UUID)
        print(f"Model Number: {model_number.decode()}")

asyncio.run(main(address))
```

The README adds one warning that is easy to ignore and expensive to debug: do not name your script bleak.py, because it causes a circular import error. The examples folder carries the rest of the surface, including enable_notifications.py, disconnect_callback.py, mtu_size.py, service_explorer.py, uart_service.py and a kivy subfolder for the Android case.

## Where bleak stops being the right tool

The README states the supported platforms plainly, and the floors are higher than many teams expect. Windows support starts at Windows 11, version 22000 and greater. Linux requires BlueZ >= 5.55. macOS support comes via Core Bluetooth from at least OS X version 10.15. If your deployment target is Windows 10, bleak is not the library for you, and that is a hardware and OS constraint rather than a bug that will be patched. The second boundary is role: bleak is a GATT client only. It connects to devices acting as GATT servers. If your Python process needs to expose a service that a phone connects to, this library does not do that. The third is the Android backend. The README mentions compatibility with python-for-android in a single line, and the repository carries separate mypy-android.ini and pyrightconfig-android.json files, but the README does not document an Android install procedure. Treat that path as the least specified part of the project and budget time accordingly.

## bleak against direct platform bindings

The realistic alternative is not another Python library so much as binding directly to the platform API: Core Bluetooth through pyobjc on macOS, the WinRT Bluetooth namespaces on Windows, or BlueZ over D-Bus on Linux. That approach gives you access to platform features bleak may not surface and removes one layer from the stack. The difference in approach is that you write and maintain three implementations, each with its own async model, its own error types and its own threading rules, and you test all three. bleak's value proposition is that you write one asyncio program and the dependency markers in pyproject.toml decide which binding is loaded. If you only ever ship on one platform and you need something the abstraction does not expose, the direct binding is the honest choice. If you ship on more than one, the abstraction is doing real work.

## Maintenance, releases and the MIT licence

The repository is not archived and the last push was on 2026-09-28. The recent releases are v3.0.0 on 2026-03-22, v3.0.1 on 2026-03-25 and v3.0.2 on 2026-05-02, and pyproject.toml declares version 3.0.2 with requires-python >= 3.10. That version floor matters for upgrade planning: if you are still on Python 3.9, you cannot move to the 3.0.x line. The dependency list also carries conditional pins for older interpreters, with async-timeout for Python below 3.11 and typing-extensions for Python below 3.12, so the library still supports a range of interpreters above its floor. The licence is MIT, declared both in the README and as license = "MIT" with license-files = ["LICENSE"] in pyproject.toml. MIT is permissive and places few obligations on how you redistribute, but the platform dependencies are separate packages under their own licences, and pyobjc and the winrt-* packages are the ones worth checking if your product ships them. That is a question for your own legal review, not something the README answers.

## Conclusion

Adopt bleak when you need a GATT client inside an asyncio Python program and want one API across Windows, Linux, macOS and Android; the platform dependencies in pyproject.toml make that portability real rather than aspirational. Do not adopt it when you need a GATT server, a synchronous API, or support for Windows 10 or older, since the README states Windows 11 version 22000 and greater and BlueZ >= 5.55 as the floors. Before committing, verify your target OS version, confirm your BlueZ version on Linux, and decide whether the Android backend via python-for-android is part of your plan, because that path is the least documented in the README. The first thing to check is whether you can name your script something other than bleak.py.

## FAQ

### How do I install bleak?

The README gives a single command, pip install bleak. There is no build step or system service to configure, though the platform dependencies are installed automatically based on your operating system.

### How do I install bleak for Python specifically?

bleak is a Python package, so installation is the same pip install bleak command. pyproject.toml declares requires-python >= 3.10, so the interpreter must be 3.10 or newer.

### How do I use bleak in Python?

The README shows two starting points: BleakScanner.discover() to list nearby devices, and BleakClient used as an async context manager to read a GATT characteristic such as the model number UUID 2A24. Both run inside asyncio.run().

### How do I install bleak on a Raspberry Pi?

The README does not give a Raspberry Pi procedure. It states that Linux support requires BlueZ >= 5.55, so the version of BlueZ on the distribution is the thing to check, and installation itself is the same pip install bleak command.

## Sources

- [hbldh/bleak on GitHub](https://github.com/hbldh/bleak)
- [Issues](https://github.com/hbldh/bleak/issues)
- [License: MIT](https://github.com/hbldh/bleak/blob/develop/LICENSE)
- [README](https://github.com/hbldh/bleak/blob/develop/README.md)
- [Releases](https://github.com/hbldh/bleak/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/hbldh-bleak
