bluek
August 10, 2026 · View on GitHub
A bleak-compatible BLE central API for
Linux that talks to the in-kernel BlueZ stack directly over sockets — no
D-Bus, and (unlike bumble-bleak) no
exclusive control of the controller. The kernel keeps managing the adapter, so
bluek coexists with bluetoothd / Home Assistant.
app → bluek → bluez (kernel) → hw
- Scanning: Bluetooth management socket (
HCI_CHANNEL_CONTROL) — the same APIbluetoothduses, so discovery coexists. - GATT: an L2CAP socket on the ATT channel (CID
0x0004); the ATT/GATT client protocol is implemented in Python (thegatttool/btgatt-clientmodel). - Pairing: delegated to
bluetoothctl(the kernel keeps the keys); no SMP in bluek.
BleakClient.connect() runs a short (≤2 s) mgmt-level discovery first so the
kernel's L2CAP-LE connect path has a recent advert observation for the peer —
without this, connect() silently hangs (when bluetoothd is also scanning)
or returns EHOSTUNREACH for the bare-MAC case. The pre-scan bails as soon
as the peer's advert arrives, so the cost is one advertising interval
(typically <500 ms) when a BleakScanner was already running.
It's the Linux sibling of micropython-bleak
(wraps aioble) and bumble-bleak (wraps
Bumble).
Usage
import bluek as bleak
from bluek import BleakClient, BleakScanner
or transparently shadow the real bleak:
import bluek.shadow # noqa: F401 — makes `import bleak` resolve to bluek
The adapter= argument accepts an hciN name, a controller MAC
("2C:CF:67:5F:4A:6D", re-resolved to its current index on each connect so it
survives USB re-enumeration), or None/"default" for hci0.
Requirements
Linux with a BlueZ kernel stack. The management socket (HCI_CHANNEL_CONTROL)
and L2CAP LE sockets require CAP_NET_ADMIN and CAP_NET_RAW. In practice
this means either:
- run as root, or
- grant both capabilities to an unprivileged user.
For a systemd service, prefer running as root (simplest on a bench box):
[Service]
# no User= line → runs as root
Or with a non-root user, add ambient capabilities:
[Service]
User=myuser
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
Note: on some kernel/HCI-bridge combinations the mgmt
START_SERVICE_DISCOVERYcommand (opcode0x003A) is refused even with both capabilities present, falling back to the olderSTART_DISCOVERYopcode. If that also fails, run as root — the kernel mgmt permission check is stricter for non-root even with ambient caps on some platforms (observed with an ESP32-S3 USB HCI bridge on Raspberry Pi OS, kernel 6.12).
bluetoothd may stay running — bluek coexists with it and does not require
exclusive control of the controller.
Status
Early. Implements the GATT-client subset that batmon-ha uses. See
tests/ for the pure codec tests and examples/spike.py for a hardware probe.