Pure-Python Bluetooth SIG Mesh stack (rides on an existing mesh network)
Project description
Bluetooth Mesh for Home Assistant
A pure-Python Bluetooth SIG Mesh stack and a Home Assistant integration that lets HA control Bluetooth Mesh lighting — Häfele Connect Mesh (Loox), other ThingOS-based luminaires, and standard SIG-Mesh lights — with no extra hardware beyond what most installs already have: an ESPHome Bluetooth proxy or a local Bluetooth adapter.
Home Assistant has no native Bluetooth Mesh support, which orphans entire
product families of "app-only" mesh lights. Existing workarounds need either a
discontinued vendor gateway or an experimental BlueZ bluetooth-meshd setup
that cannot run on Home Assistant OS. This project removes both requirements.
Status: working, validated on real hardware. A Häfele Connect Mesh tunable-white lamp is controlled end-to-end from Home Assistant through an ESPHome Bluetooth proxy — on/off, brightness, and colour temperature — with a kept-alive proxy connection that makes commands feel instant. See What it controls for the current capability surface.
What it controls
Each provisioned lighting node becomes one Home Assistant light entity, with
capabilities read from its mesh composition:
| Node model | HA capability |
|---|---|
Generic OnOff (0x1000) |
on / off |
Light Lightness (0x1300) |
+ brightness |
Light CTL (0x1303 / 0x1306) |
+ colour temperature (tunable white) |
Not yet supported: RGB / full-colour lamps (Light HSL / xyL). The author's hardware is tunable-white only, so colour is left unimplemented rather than shipped untested — contributions with colour hardware to validate against are welcome.
Two deliverables
This repository ships two things, mirroring the pymadoka-ng / daikin_madoka
split:
btmesh(src/btmesh/) — a Home-Assistant-independent Python library implementing the mesh stack: crypto (k1–k4 derivations, AES-CMAC, AES-CCM, network obfuscation), network/transport/access layers, proxy-PDU segmentation/reassembly, a provisioner, and a GATT bearer that runs overbleak/habluetooth(so it works through ESPHome Bluetooth proxies).bluetooth_mesh(custom_components/bluetooth_mesh/) — a HACS custom integration: config flow, storage, a connection coordinator, and Home Assistant entities (starting withlight).
How it works
Home Assistant (pure-Python mesh stack: btmesh)
│ bleak / habluetooth
▼
ESPHome BLE proxy (existing fleet) ← or any local BT adapter
│ GATT: Mesh Proxy Service (proxy protocol tunnel)
▼
A mesh node with GATT Proxy enabled (any powered lamp)
│ advertising bearer (mesh relay)
▼
The rest of the mesh network
One GATT connection serves the whole network: the stack maintains a single tunnel to one proxy node, and the mesh relays messages to every other node. No BlueZ meshd, no dedicated ESP32 firmware — it works on Home Assistant OS in a VM with no local radio.
Coexistence with the vendor app (shared keys)
Rather than replicating a vendor's proprietary activation step, this project
rides on the mesh network the vendor app already created. The app exports
its network as a .connect JSON (ThingOS format) containing the NetKey, the
AppKey, and each node's unicast address. The integration imports that file,
connects its proxy to the same network, and sends standard, app-keyed mesh
messages (Generic OnOff Set, Light Lightness Set, …) to each node's unicast
address.
Because both sides share the same keys, the vendor app and Home Assistant can control the same lamps — HA drives the same lights the app provisioned.
A mesh node has a single GATT-proxy connection slot, so Home Assistant and
the app cannot both hold it at once. The integration keeps its proxy connection
open by default (so commands are instant), which means the vendor app cannot
connect while HA is loaded. If you still want to use the app, set a keep-alive
timeout (Settings → Devices & Services → Bluetooth Mesh → Configure):
after that many idle seconds HA releases the lamp so the app can take over, at
the cost of a few-second reconnect on HA's next command. 0 = always
connected.
Installation
- Add the repository to HACS as a custom repository (HACS → Integrations → ⋯ → Custom repositories), category Integration, then install Bluetooth Mesh and restart Home Assistant.
- Export your network from the vendor app as a
.connectfile. - Add the integration (Settings → Devices & Services → Add Integration →
Bluetooth Mesh) and paste the contents of the
.connectfile when prompted. Keep this file private — it contains your mesh network keys and should never be committed to a repository. - (Optional) Configure the keep-alive timeout if you want to keep using the vendor app — see Coexistence.
A Bluetooth transport is required: an ESPHome Bluetooth proxy on your network, or a local Bluetooth adapter usable by Home Assistant. At least one mesh lamp must be powered and in range of that proxy for Home Assistant to reach the network.
Development
# Library only (fast; no Home Assistant):
uv sync
uv run pytest tests # btmesh library test suite
# Full suite (library + HA integration) in a HA-equipped environment:
pip install -e .
pip install -r requirements-test.txt
pytest tests # library + tests/ha/ (Home Assistant) together
The tests/ha/ tree is guarded with pytest.importorskip so it stays skipped
when Home Assistant is not installed, and runs for real when it is. CI installs
Home Assistant and runs the combined suite on Linux; see
.github/workflows/tests.yml (pytest) and .github/workflows/validate.yml
(hassfest + HACS).
Vendored library
src/btmesh/ is the canonical library (the source of truth, published to PyPI).
Because HACS installs custom_components/bluetooth_mesh/ as-is, the integration
ships a vendored copy of the library at
custom_components/bluetooth_mesh/btmesh/, so it installs with no external
btmesh dependency (only cryptography, which Home Assistant already provides).
After changing anything under src/btmesh/, re-sync the vendored copy:
python scripts/sync_vendored_btmesh.py
Documentation
Design and feasibility write-ups live in docs/plans/:
2026-07-18-btmesh-design.md— design document (problem, architecture, key decisions).2026-07-18-phase0-feasibility.md— Phase 0 feasibility plan.2026-07-19-phase0-report.md— Phase 0 report (the hardware-validated breakthrough).2026-07-19-phase1-library-and-ha-integration.md— Phase 1 plan (library + HA integration).
Credit and honesty
This project grew out of reverse-engineering a Häfele Connect Mesh setup after its vendor gateway was discontinued. The mesh crypto and codec layers are implemented from the public Bluetooth SIG Mesh specification and validated against the specification's official sample vectors; the coexistence approach was discovered empirically against real hardware. It is an independent, unofficial project and is not affiliated with or endorsed by Häfele, ThingOS, or the Bluetooth SIG.
License
MIT © 2026 David Simon.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file btmesh-0.1.0.tar.gz.
File metadata
- Download URL: btmesh-0.1.0.tar.gz
- Upload date:
- Size: 40.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1852b6c0f8421defd175f0c5b806dba95417ae5cd2692c4771659bceba499e83
|
|
| MD5 |
48f731b858e9cd8f4c44af1bf3a07010
|
|
| BLAKE2b-256 |
95ab196132919c39a504b0a7b77b75f5de124d3309c28374adab05c268fe0adc
|
File details
Details for the file btmesh-0.1.0-py3-none-any.whl.
File metadata
- Download URL: btmesh-0.1.0-py3-none-any.whl
- Upload date:
- Size: 45.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38535ee5fb481ed8cc5fdc27868ec14446605db532e0196a40fe368410a6b3c2
|
|
| MD5 |
35e56f63160a8e1ec2b46a070a732717
|
|
| BLAKE2b-256 |
2a48c26b0031e637f256d0fc4699ae38fb2b70beb7b743b2d0bf86714c964ed5
|