btmesh 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- btmesh-0.1.0/.gitignore +12 -0
- btmesh-0.1.0/CHANGELOG.md +40 -0
- btmesh-0.1.0/LICENSE +21 -0
- btmesh-0.1.0/PKG-INFO +197 -0
- btmesh-0.1.0/README.md +167 -0
- btmesh-0.1.0/pyproject.toml +59 -0
- btmesh-0.1.0/src/btmesh/__init__.py +0 -0
- btmesh-0.1.0/src/btmesh/access.py +726 -0
- btmesh-0.1.0/src/btmesh/bearer.py +535 -0
- btmesh-0.1.0/src/btmesh/controller.py +228 -0
- btmesh-0.1.0/src/btmesh/crypto.py +112 -0
- btmesh-0.1.0/src/btmesh/errors.py +12 -0
- btmesh-0.1.0/src/btmesh/network.py +169 -0
- btmesh-0.1.0/src/btmesh/network_model.py +277 -0
- btmesh-0.1.0/src/btmesh/node.py +327 -0
- btmesh-0.1.0/src/btmesh/prov_pdu.py +326 -0
- btmesh-0.1.0/src/btmesh/provisioner.py +381 -0
- btmesh-0.1.0/src/btmesh/proxy_pdu.py +125 -0
- btmesh-0.1.0/src/btmesh/pump.py +65 -0
- btmesh-0.1.0/src/btmesh/transport.py +289 -0
btmesh-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
|
|
7
|
+
# Secrets and vendor artifacts — NEVER commit these.
|
|
8
|
+
# A ThingOS/Häfele ".connect" export holds the mesh NetKey/AppKey/DeviceKeys.
|
|
9
|
+
# (The sanitized tests/fixtures/sample.connect.json is a *.json fixture and is
|
|
10
|
+
# intentionally NOT matched by this pattern.)
|
|
11
|
+
*.connect
|
|
12
|
+
*.apk
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims
|
|
5
|
+
to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — 2026-07-20
|
|
8
|
+
|
|
9
|
+
First public release. A pure-Python Bluetooth SIG Mesh stack (`btmesh`) and a
|
|
10
|
+
Home Assistant custom integration (`bluetooth_mesh`), validated end-to-end on
|
|
11
|
+
real hardware against a Häfele Connect Mesh tunable-white lamp through an
|
|
12
|
+
ESPHome Bluetooth proxy.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Mesh stack (`btmesh`)**: k1–k4 derivations, AES-CMAC/AES-CCM, network
|
|
17
|
+
obfuscation; network/transport/access layers; proxy-PDU segmentation and
|
|
18
|
+
reassembly; a provisioner; and a GATT bearer over `bleak` / `habluetooth`
|
|
19
|
+
(works through ESPHome Bluetooth proxies). Validated against the SIG spec
|
|
20
|
+
sample vectors.
|
|
21
|
+
- **Home Assistant integration (`bluetooth_mesh`)**: config flow importing a
|
|
22
|
+
ThingOS/Häfele `.connect` network export; a connection coordinator; and a
|
|
23
|
+
`light` platform exposing on/off, brightness, and colour temperature (Light
|
|
24
|
+
CTL) per node composition.
|
|
25
|
+
- **Coexistence model**: rides on the network the vendor app already
|
|
26
|
+
provisioned (shared NetKey/AppKey), sending standard app-keyed SIG messages.
|
|
27
|
+
- **Kept-alive proxy connection** for instant commands, with a configurable
|
|
28
|
+
keep-alive timeout (options flow) to hand the lamp's single proxy slot back
|
|
29
|
+
to the vendor app when idle. `0` = always connected.
|
|
30
|
+
- **Local brand icon** (`brand/`) for Home Assistant ≥ 2026.3.
|
|
31
|
+
|
|
32
|
+
### Known limitations
|
|
33
|
+
|
|
34
|
+
- RGB / full-colour lamps (Light HSL / xyL) are not implemented — the reference
|
|
35
|
+
hardware is tunable-white only.
|
|
36
|
+
- Optimistic state: brightness/temperature reflect the last command; changes
|
|
37
|
+
made from the vendor app in parallel are not read back until HA's next
|
|
38
|
+
command.
|
|
39
|
+
|
|
40
|
+
[0.1.0]: https://github.com/dasimon135/ha-bluetooth-mesh/releases/tag/v0.1.0
|
btmesh-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Simon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
btmesh-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: btmesh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure-Python Bluetooth SIG Mesh stack (rides on an existing mesh network)
|
|
5
|
+
Project-URL: Homepage, https://github.com/dasimon135/ha-bluetooth-mesh
|
|
6
|
+
Project-URL: Repository, https://github.com/dasimon135/ha-bluetooth-mesh
|
|
7
|
+
Project-URL: Changelog, https://github.com/dasimon135/ha-bluetooth-mesh/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/dasimon135/ha-bluetooth-mesh/issues
|
|
9
|
+
Author: David Simon
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ble,bluetooth,bluetooth-mesh,home-assistant,mesh,thingos
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Home Automation
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: cryptography>=42
|
|
20
|
+
Provides-Extra: ble
|
|
21
|
+
Requires-Dist: bleak-retry-connector>=3; extra == 'ble'
|
|
22
|
+
Requires-Dist: bleak>=0.22; extra == 'ble'
|
|
23
|
+
Provides-Extra: harness
|
|
24
|
+
Requires-Dist: aioesphomeapi>=24; extra == 'harness'
|
|
25
|
+
Requires-Dist: bleak-esphome>=2.0; extra == 'harness'
|
|
26
|
+
Requires-Dist: bleak-retry-connector>=3; extra == 'harness'
|
|
27
|
+
Requires-Dist: bleak>=0.22; extra == 'harness'
|
|
28
|
+
Requires-Dist: habluetooth>=3; extra == 'harness'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Bluetooth Mesh for Home Assistant
|
|
32
|
+
|
|
33
|
+
A pure-Python **Bluetooth SIG Mesh** stack and a **Home Assistant** integration
|
|
34
|
+
that lets HA control Bluetooth Mesh lighting — Häfele Connect Mesh (Loox),
|
|
35
|
+
other ThingOS-based luminaires, and standard SIG-Mesh lights — **with no extra
|
|
36
|
+
hardware** beyond what most installs already have: an ESPHome Bluetooth proxy or
|
|
37
|
+
a local Bluetooth adapter.
|
|
38
|
+
|
|
39
|
+
Home Assistant has no native Bluetooth Mesh support, which orphans entire
|
|
40
|
+
product families of "app-only" mesh lights. Existing workarounds need either a
|
|
41
|
+
discontinued vendor gateway or an experimental BlueZ `bluetooth-meshd` setup
|
|
42
|
+
that cannot run on Home Assistant OS. This project removes both requirements.
|
|
43
|
+
|
|
44
|
+
> **Status:** working, validated on real hardware. A Häfele Connect Mesh
|
|
45
|
+
> tunable-white lamp is controlled end-to-end from Home Assistant through an
|
|
46
|
+
> ESPHome Bluetooth proxy — on/off, brightness, and colour temperature — with a
|
|
47
|
+
> kept-alive proxy connection that makes commands feel instant. See
|
|
48
|
+
> [What it controls](#what-it-controls) for the current capability surface.
|
|
49
|
+
|
|
50
|
+
## What it controls
|
|
51
|
+
|
|
52
|
+
Each provisioned lighting node becomes one Home Assistant `light` entity, with
|
|
53
|
+
capabilities read from its mesh composition:
|
|
54
|
+
|
|
55
|
+
| Node model | HA capability |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| Generic OnOff (`0x1000`) | on / off |
|
|
58
|
+
| Light Lightness (`0x1300`) | + brightness |
|
|
59
|
+
| Light CTL (`0x1303` / `0x1306`) | + colour temperature (tunable white) |
|
|
60
|
+
|
|
61
|
+
**Not yet supported:** RGB / full-colour lamps (Light HSL / xyL). The author's
|
|
62
|
+
hardware is tunable-white only, so colour is left unimplemented rather than
|
|
63
|
+
shipped untested — contributions with colour hardware to validate against are
|
|
64
|
+
welcome.
|
|
65
|
+
|
|
66
|
+
## Two deliverables
|
|
67
|
+
|
|
68
|
+
This repository ships two things, mirroring the `pymadoka-ng` / `daikin_madoka`
|
|
69
|
+
split:
|
|
70
|
+
|
|
71
|
+
1. **`btmesh`** (`src/btmesh/`) — a Home-Assistant-independent Python library
|
|
72
|
+
implementing the mesh stack: crypto (k1–k4 derivations, AES-CMAC, AES-CCM,
|
|
73
|
+
network obfuscation), network/transport/access layers, proxy-PDU
|
|
74
|
+
segmentation/reassembly, a provisioner, and a GATT bearer that runs over
|
|
75
|
+
`bleak` / `habluetooth` (so it works through ESPHome Bluetooth proxies).
|
|
76
|
+
2. **`bluetooth_mesh`** (`custom_components/bluetooth_mesh/`) — a HACS custom
|
|
77
|
+
integration: config flow, storage, a connection coordinator, and Home
|
|
78
|
+
Assistant entities (starting with `light`).
|
|
79
|
+
|
|
80
|
+
## How it works
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
Home Assistant (pure-Python mesh stack: btmesh)
|
|
84
|
+
│ bleak / habluetooth
|
|
85
|
+
▼
|
|
86
|
+
ESPHome BLE proxy (existing fleet) ← or any local BT adapter
|
|
87
|
+
│ GATT: Mesh Proxy Service (proxy protocol tunnel)
|
|
88
|
+
▼
|
|
89
|
+
A mesh node with GATT Proxy enabled (any powered lamp)
|
|
90
|
+
│ advertising bearer (mesh relay)
|
|
91
|
+
▼
|
|
92
|
+
The rest of the mesh network
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
One GATT connection serves the whole network: the stack maintains a single
|
|
96
|
+
tunnel to one proxy node, and the mesh relays messages to every other node.
|
|
97
|
+
No BlueZ meshd, no dedicated ESP32 firmware — it works on Home Assistant OS in
|
|
98
|
+
a VM with no local radio.
|
|
99
|
+
|
|
100
|
+
## Coexistence with the vendor app (shared keys)
|
|
101
|
+
|
|
102
|
+
Rather than replicating a vendor's proprietary activation step, this project
|
|
103
|
+
**rides on the mesh network the vendor app already created**. The app exports
|
|
104
|
+
its network as a `.connect` JSON (ThingOS format) containing the NetKey, the
|
|
105
|
+
AppKey, and each node's unicast address. The integration imports that file,
|
|
106
|
+
connects its proxy to the same network, and sends **standard**, app-keyed mesh
|
|
107
|
+
messages (Generic OnOff Set, Light Lightness Set, …) to each node's unicast
|
|
108
|
+
address.
|
|
109
|
+
|
|
110
|
+
Because both sides share the same keys, the vendor app and Home Assistant can
|
|
111
|
+
control the **same lamps** — HA drives the same lights the app provisioned.
|
|
112
|
+
|
|
113
|
+
A mesh node has a **single** GATT-proxy connection slot, so Home Assistant and
|
|
114
|
+
the app cannot both hold it at once. The integration keeps its proxy connection
|
|
115
|
+
open by default (so commands are instant), which means the vendor app cannot
|
|
116
|
+
connect while HA is loaded. If you still want to use the app, set a **keep-alive
|
|
117
|
+
timeout** (Settings → Devices & Services → *Bluetooth Mesh* → **Configure**):
|
|
118
|
+
after that many idle seconds HA releases the lamp so the app can take over, at
|
|
119
|
+
the cost of a few-second reconnect on HA's next command. `0` = always
|
|
120
|
+
connected.
|
|
121
|
+
|
|
122
|
+
## Installation
|
|
123
|
+
|
|
124
|
+
1. **Add the repository to HACS** as a custom repository
|
|
125
|
+
(HACS → Integrations → ⋯ → Custom repositories), category **Integration**,
|
|
126
|
+
then install *Bluetooth Mesh* and restart Home Assistant.
|
|
127
|
+
2. **Export your network** from the vendor app as a `.connect` file.
|
|
128
|
+
3. **Add the integration** (Settings → Devices & Services → Add Integration →
|
|
129
|
+
*Bluetooth Mesh*) and paste the contents of the `.connect` file when
|
|
130
|
+
prompted. Keep this file private — it contains your mesh network keys and
|
|
131
|
+
should never be committed to a repository.
|
|
132
|
+
4. *(Optional)* **Configure** the keep-alive timeout if you want to keep using
|
|
133
|
+
the vendor app — see [Coexistence](#coexistence-with-the-vendor-app-shared-keys).
|
|
134
|
+
|
|
135
|
+
A Bluetooth transport is required: an ESPHome Bluetooth proxy on your network,
|
|
136
|
+
or a local Bluetooth adapter usable by Home Assistant. At least one mesh lamp
|
|
137
|
+
must be powered and in range of that proxy for Home Assistant to reach the
|
|
138
|
+
network.
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Library only (fast; no Home Assistant):
|
|
144
|
+
uv sync
|
|
145
|
+
uv run pytest tests # btmesh library test suite
|
|
146
|
+
|
|
147
|
+
# Full suite (library + HA integration) in a HA-equipped environment:
|
|
148
|
+
pip install -e .
|
|
149
|
+
pip install -r requirements-test.txt
|
|
150
|
+
pytest tests # library + tests/ha/ (Home Assistant) together
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The `tests/ha/` tree is guarded with `pytest.importorskip` so it stays skipped
|
|
154
|
+
when Home Assistant is not installed, and runs for real when it is. CI installs
|
|
155
|
+
Home Assistant and runs the combined suite on Linux; see
|
|
156
|
+
`.github/workflows/tests.yml` (pytest) and `.github/workflows/validate.yml`
|
|
157
|
+
(hassfest + HACS).
|
|
158
|
+
|
|
159
|
+
### Vendored library
|
|
160
|
+
|
|
161
|
+
`src/btmesh/` is the canonical library (the source of truth, published to PyPI).
|
|
162
|
+
Because HACS installs `custom_components/bluetooth_mesh/` as-is, the integration
|
|
163
|
+
ships a **vendored copy** of the library at
|
|
164
|
+
`custom_components/bluetooth_mesh/btmesh/`, so it installs with no external
|
|
165
|
+
`btmesh` dependency (only `cryptography`, which Home Assistant already provides).
|
|
166
|
+
After changing anything under `src/btmesh/`, re-sync the vendored copy:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
python scripts/sync_vendored_btmesh.py
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Documentation
|
|
173
|
+
|
|
174
|
+
Design and feasibility write-ups live in [`docs/plans/`](docs/plans/):
|
|
175
|
+
|
|
176
|
+
- [`2026-07-18-btmesh-design.md`](docs/plans/2026-07-18-btmesh-design.md) —
|
|
177
|
+
design document (problem, architecture, key decisions).
|
|
178
|
+
- [`2026-07-18-phase0-feasibility.md`](docs/plans/2026-07-18-phase0-feasibility.md) —
|
|
179
|
+
Phase 0 feasibility plan.
|
|
180
|
+
- [`2026-07-19-phase0-report.md`](docs/plans/2026-07-19-phase0-report.md) —
|
|
181
|
+
Phase 0 report (the hardware-validated breakthrough).
|
|
182
|
+
- [`2026-07-19-phase1-library-and-ha-integration.md`](docs/plans/2026-07-19-phase1-library-and-ha-integration.md) —
|
|
183
|
+
Phase 1 plan (library + HA integration).
|
|
184
|
+
|
|
185
|
+
## Credit and honesty
|
|
186
|
+
|
|
187
|
+
This project grew out of reverse-engineering a Häfele Connect Mesh setup after
|
|
188
|
+
its vendor gateway was discontinued. The mesh crypto and codec layers are
|
|
189
|
+
implemented from the public Bluetooth SIG Mesh specification and validated
|
|
190
|
+
against the specification's official sample vectors; the coexistence approach
|
|
191
|
+
was discovered empirically against real hardware. It is an independent,
|
|
192
|
+
unofficial project and is not affiliated with or endorsed by Häfele, ThingOS,
|
|
193
|
+
or the Bluetooth SIG.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
[MIT](LICENSE) © 2026 David Simon.
|
btmesh-0.1.0/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Bluetooth Mesh for Home Assistant
|
|
2
|
+
|
|
3
|
+
A pure-Python **Bluetooth SIG Mesh** stack and a **Home Assistant** integration
|
|
4
|
+
that lets HA control Bluetooth Mesh lighting — Häfele Connect Mesh (Loox),
|
|
5
|
+
other ThingOS-based luminaires, and standard SIG-Mesh lights — **with no extra
|
|
6
|
+
hardware** beyond what most installs already have: an ESPHome Bluetooth proxy or
|
|
7
|
+
a local Bluetooth adapter.
|
|
8
|
+
|
|
9
|
+
Home Assistant has no native Bluetooth Mesh support, which orphans entire
|
|
10
|
+
product families of "app-only" mesh lights. Existing workarounds need either a
|
|
11
|
+
discontinued vendor gateway or an experimental BlueZ `bluetooth-meshd` setup
|
|
12
|
+
that cannot run on Home Assistant OS. This project removes both requirements.
|
|
13
|
+
|
|
14
|
+
> **Status:** working, validated on real hardware. A Häfele Connect Mesh
|
|
15
|
+
> tunable-white lamp is controlled end-to-end from Home Assistant through an
|
|
16
|
+
> ESPHome Bluetooth proxy — on/off, brightness, and colour temperature — with a
|
|
17
|
+
> kept-alive proxy connection that makes commands feel instant. See
|
|
18
|
+
> [What it controls](#what-it-controls) for the current capability surface.
|
|
19
|
+
|
|
20
|
+
## What it controls
|
|
21
|
+
|
|
22
|
+
Each provisioned lighting node becomes one Home Assistant `light` entity, with
|
|
23
|
+
capabilities read from its mesh composition:
|
|
24
|
+
|
|
25
|
+
| Node model | HA capability |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| Generic OnOff (`0x1000`) | on / off |
|
|
28
|
+
| Light Lightness (`0x1300`) | + brightness |
|
|
29
|
+
| Light CTL (`0x1303` / `0x1306`) | + colour temperature (tunable white) |
|
|
30
|
+
|
|
31
|
+
**Not yet supported:** RGB / full-colour lamps (Light HSL / xyL). The author's
|
|
32
|
+
hardware is tunable-white only, so colour is left unimplemented rather than
|
|
33
|
+
shipped untested — contributions with colour hardware to validate against are
|
|
34
|
+
welcome.
|
|
35
|
+
|
|
36
|
+
## Two deliverables
|
|
37
|
+
|
|
38
|
+
This repository ships two things, mirroring the `pymadoka-ng` / `daikin_madoka`
|
|
39
|
+
split:
|
|
40
|
+
|
|
41
|
+
1. **`btmesh`** (`src/btmesh/`) — a Home-Assistant-independent Python library
|
|
42
|
+
implementing the mesh stack: crypto (k1–k4 derivations, AES-CMAC, AES-CCM,
|
|
43
|
+
network obfuscation), network/transport/access layers, proxy-PDU
|
|
44
|
+
segmentation/reassembly, a provisioner, and a GATT bearer that runs over
|
|
45
|
+
`bleak` / `habluetooth` (so it works through ESPHome Bluetooth proxies).
|
|
46
|
+
2. **`bluetooth_mesh`** (`custom_components/bluetooth_mesh/`) — a HACS custom
|
|
47
|
+
integration: config flow, storage, a connection coordinator, and Home
|
|
48
|
+
Assistant entities (starting with `light`).
|
|
49
|
+
|
|
50
|
+
## How it works
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Home Assistant (pure-Python mesh stack: btmesh)
|
|
54
|
+
│ bleak / habluetooth
|
|
55
|
+
▼
|
|
56
|
+
ESPHome BLE proxy (existing fleet) ← or any local BT adapter
|
|
57
|
+
│ GATT: Mesh Proxy Service (proxy protocol tunnel)
|
|
58
|
+
▼
|
|
59
|
+
A mesh node with GATT Proxy enabled (any powered lamp)
|
|
60
|
+
│ advertising bearer (mesh relay)
|
|
61
|
+
▼
|
|
62
|
+
The rest of the mesh network
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
One GATT connection serves the whole network: the stack maintains a single
|
|
66
|
+
tunnel to one proxy node, and the mesh relays messages to every other node.
|
|
67
|
+
No BlueZ meshd, no dedicated ESP32 firmware — it works on Home Assistant OS in
|
|
68
|
+
a VM with no local radio.
|
|
69
|
+
|
|
70
|
+
## Coexistence with the vendor app (shared keys)
|
|
71
|
+
|
|
72
|
+
Rather than replicating a vendor's proprietary activation step, this project
|
|
73
|
+
**rides on the mesh network the vendor app already created**. The app exports
|
|
74
|
+
its network as a `.connect` JSON (ThingOS format) containing the NetKey, the
|
|
75
|
+
AppKey, and each node's unicast address. The integration imports that file,
|
|
76
|
+
connects its proxy to the same network, and sends **standard**, app-keyed mesh
|
|
77
|
+
messages (Generic OnOff Set, Light Lightness Set, …) to each node's unicast
|
|
78
|
+
address.
|
|
79
|
+
|
|
80
|
+
Because both sides share the same keys, the vendor app and Home Assistant can
|
|
81
|
+
control the **same lamps** — HA drives the same lights the app provisioned.
|
|
82
|
+
|
|
83
|
+
A mesh node has a **single** GATT-proxy connection slot, so Home Assistant and
|
|
84
|
+
the app cannot both hold it at once. The integration keeps its proxy connection
|
|
85
|
+
open by default (so commands are instant), which means the vendor app cannot
|
|
86
|
+
connect while HA is loaded. If you still want to use the app, set a **keep-alive
|
|
87
|
+
timeout** (Settings → Devices & Services → *Bluetooth Mesh* → **Configure**):
|
|
88
|
+
after that many idle seconds HA releases the lamp so the app can take over, at
|
|
89
|
+
the cost of a few-second reconnect on HA's next command. `0` = always
|
|
90
|
+
connected.
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
1. **Add the repository to HACS** as a custom repository
|
|
95
|
+
(HACS → Integrations → ⋯ → Custom repositories), category **Integration**,
|
|
96
|
+
then install *Bluetooth Mesh* and restart Home Assistant.
|
|
97
|
+
2. **Export your network** from the vendor app as a `.connect` file.
|
|
98
|
+
3. **Add the integration** (Settings → Devices & Services → Add Integration →
|
|
99
|
+
*Bluetooth Mesh*) and paste the contents of the `.connect` file when
|
|
100
|
+
prompted. Keep this file private — it contains your mesh network keys and
|
|
101
|
+
should never be committed to a repository.
|
|
102
|
+
4. *(Optional)* **Configure** the keep-alive timeout if you want to keep using
|
|
103
|
+
the vendor app — see [Coexistence](#coexistence-with-the-vendor-app-shared-keys).
|
|
104
|
+
|
|
105
|
+
A Bluetooth transport is required: an ESPHome Bluetooth proxy on your network,
|
|
106
|
+
or a local Bluetooth adapter usable by Home Assistant. At least one mesh lamp
|
|
107
|
+
must be powered and in range of that proxy for Home Assistant to reach the
|
|
108
|
+
network.
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Library only (fast; no Home Assistant):
|
|
114
|
+
uv sync
|
|
115
|
+
uv run pytest tests # btmesh library test suite
|
|
116
|
+
|
|
117
|
+
# Full suite (library + HA integration) in a HA-equipped environment:
|
|
118
|
+
pip install -e .
|
|
119
|
+
pip install -r requirements-test.txt
|
|
120
|
+
pytest tests # library + tests/ha/ (Home Assistant) together
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The `tests/ha/` tree is guarded with `pytest.importorskip` so it stays skipped
|
|
124
|
+
when Home Assistant is not installed, and runs for real when it is. CI installs
|
|
125
|
+
Home Assistant and runs the combined suite on Linux; see
|
|
126
|
+
`.github/workflows/tests.yml` (pytest) and `.github/workflows/validate.yml`
|
|
127
|
+
(hassfest + HACS).
|
|
128
|
+
|
|
129
|
+
### Vendored library
|
|
130
|
+
|
|
131
|
+
`src/btmesh/` is the canonical library (the source of truth, published to PyPI).
|
|
132
|
+
Because HACS installs `custom_components/bluetooth_mesh/` as-is, the integration
|
|
133
|
+
ships a **vendored copy** of the library at
|
|
134
|
+
`custom_components/bluetooth_mesh/btmesh/`, so it installs with no external
|
|
135
|
+
`btmesh` dependency (only `cryptography`, which Home Assistant already provides).
|
|
136
|
+
After changing anything under `src/btmesh/`, re-sync the vendored copy:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
python scripts/sync_vendored_btmesh.py
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Documentation
|
|
143
|
+
|
|
144
|
+
Design and feasibility write-ups live in [`docs/plans/`](docs/plans/):
|
|
145
|
+
|
|
146
|
+
- [`2026-07-18-btmesh-design.md`](docs/plans/2026-07-18-btmesh-design.md) —
|
|
147
|
+
design document (problem, architecture, key decisions).
|
|
148
|
+
- [`2026-07-18-phase0-feasibility.md`](docs/plans/2026-07-18-phase0-feasibility.md) —
|
|
149
|
+
Phase 0 feasibility plan.
|
|
150
|
+
- [`2026-07-19-phase0-report.md`](docs/plans/2026-07-19-phase0-report.md) —
|
|
151
|
+
Phase 0 report (the hardware-validated breakthrough).
|
|
152
|
+
- [`2026-07-19-phase1-library-and-ha-integration.md`](docs/plans/2026-07-19-phase1-library-and-ha-integration.md) —
|
|
153
|
+
Phase 1 plan (library + HA integration).
|
|
154
|
+
|
|
155
|
+
## Credit and honesty
|
|
156
|
+
|
|
157
|
+
This project grew out of reverse-engineering a Häfele Connect Mesh setup after
|
|
158
|
+
its vendor gateway was discontinued. The mesh crypto and codec layers are
|
|
159
|
+
implemented from the public Bluetooth SIG Mesh specification and validated
|
|
160
|
+
against the specification's official sample vectors; the coexistence approach
|
|
161
|
+
was discovered empirically against real hardware. It is an independent,
|
|
162
|
+
unofficial project and is not affiliated with or endorsed by Häfele, ThingOS,
|
|
163
|
+
or the Bluetooth SIG.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
[MIT](LICENSE) © 2026 David Simon.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "btmesh"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Pure-Python Bluetooth SIG Mesh stack (rides on an existing mesh network)"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "David Simon" }]
|
|
10
|
+
keywords = ["bluetooth", "bluetooth-mesh", "ble", "mesh", "thingos", "home-assistant"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Home Automation",
|
|
16
|
+
"Topic :: System :: Networking",
|
|
17
|
+
]
|
|
18
|
+
dependencies = ["cryptography>=42"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/dasimon135/ha-bluetooth-mesh"
|
|
22
|
+
Repository = "https://github.com/dasimon135/ha-bluetooth-mesh"
|
|
23
|
+
Changelog = "https://github.com/dasimon135/ha-bluetooth-mesh/blob/main/CHANGELOG.md"
|
|
24
|
+
Issues = "https://github.com/dasimon135/ha-bluetooth-mesh/issues"
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
ble = ["bleak>=0.22", "bleak-retry-connector>=3"]
|
|
28
|
+
# Extra deps the phase0 hardware scripts need to actually talk to an ESP proxy.
|
|
29
|
+
# Not required for the test suite or `--help` (bleak/habluetooth are imported
|
|
30
|
+
# lazily inside btmesh.bearer), so a default `uv sync` stays lean.
|
|
31
|
+
harness = [
|
|
32
|
+
"bleak>=0.22",
|
|
33
|
+
"bleak-esphome>=2.0",
|
|
34
|
+
"aioesphomeapi>=24",
|
|
35
|
+
"habluetooth>=3",
|
|
36
|
+
"bleak-retry-connector>=3",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["hatchling"]
|
|
41
|
+
build-backend = "hatchling.build"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/btmesh"]
|
|
45
|
+
|
|
46
|
+
# Keep the sdist to the library itself. Without this, hatchling's default sdist
|
|
47
|
+
# sweeps the whole working tree — including phase0/ (hardware scripts and a
|
|
48
|
+
# decompiled vendor app bundle) and local logs — which is both bloat and content
|
|
49
|
+
# that must never be redistributed. Only the library + its metadata ship.
|
|
50
|
+
[tool.hatch.build.targets.sdist]
|
|
51
|
+
only-include = ["src/btmesh", "README.md", "LICENSE", "CHANGELOG.md"]
|
|
52
|
+
|
|
53
|
+
[dependency-groups]
|
|
54
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.24"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
asyncio_mode = "auto"
|
|
58
|
+
testpaths = ["tests", "phase0/tests"]
|
|
59
|
+
pythonpath = ["src", "phase0", "."]
|
|
File without changes
|