openble-bondtouch
The Bond Touch bracelet BLE protocol (Impossible Labs), read from the Bond Touch Android app (11.2.0).
openble.bondtouch.uuids: the Bond services and characteristics, spelled out one by one because their fourth group differs, and the advertised names.openble.bondtouch.identify: matches advertisements, picks the generation from the GATT table and theff10API version byte, and lists what each generation needs.openble.bondtouch.v2: V2, V4 and Mini. Theffa4state,ffa5events and touch recording, the 20-byteffa6frames (0x26LED and motor steps,0x33three-LED frames, run queue) and the0x27config properties.openble.bondtouch.v1: the original Bond Touch. Its[cmdId, seq, payload]commands,ffa7results, 11 fixed LED colours, and gaps worked out from arrival times.openble.bondtouch.v3: Bond More. Theffb1commands and reports, and theffb2touch frames of 50 ms samples.openble.bondtouch.patterns: the[press, gap, …, press]millisecond patterns every generation shares, and the conversion to V3 samples.openble.bondtouch.link:BraceletLinkanswers the token, AUID and paired-signal requests, collects touches, and sends commands one at a time with the app's pauses. On V1 it also waits for each result.
Pure Python 3.11+ with no dependencies and no Bluetooth stack. BraceletLink takes a write(uuid, payload) coroutine and is fed notifications, so it runs over bleak, a test double, or anything else.
Everything here comes from static reading of the app, checked against one live bracelet: a V2 (Bond002, firmware 2.6.4) over bleak on macOS. Setup (including taking over from the official app with a double-tap), colours, played patterns with vibration, and received touches work on it. V1, V3, V4 and Mini have not been run yet. The manual records what that run showed and the questions still open.
pip install openble-bondtouch
Build frames
from openble.bondtouch import v2, v3
from openble.bondtouch.identify import generation_of
from openble.bondtouch.patterns import to_samples
generation_of(b"\x44")
# 'v4'
v2.show_colour((0x23, 0xE0, 0xE8)).hex(" ")
# '26 00 00 00 00 00 01 23 e0 e8 50 00 00 00 00 00 00 ff ff 00'
v2.play_touch([400, 200, 300], (0xFF, 0, 0)) # two 0x26 steps, then 32 00 11
v3.touch_frames(to_samples([400, 200, 300]), (0xFF, 0, 0), slot=1)
Drive a bracelet with bleak
import asyncio
from bleak import BleakClient
from openble.bondtouch.link import BraceletLink, detect_generation, notify_uuids
async def touch(address: str, auid: int) -> None:
async with BleakClient(address) as client:
async def read(uuid: str) -> bytes:
return bytes(await client.read_gatt_char(uuid))
async def write(uuid: str, payload: bytes) -> None:
# A V2 on 2.6.4 offers ffa6 without response only.
char = client.services.get_characteristic(uuid)
response = "write" in char.properties
await client.write_gatt_char(uuid, payload, response=response)
services = [service.uuid for service in client.services]
generation = await detect_generation(read, services)
link = BraceletLink(write, generation, auid, read=read)
for uuid in notify_uuids(generation):
await client.start_notify(uuid, lambda c, data, u=uuid: link.feed(u, data))
await link.start()
await link.wait_connected()
await link.play([400, 200, 400], (0xFF, 0x44, 0xA6))
message = await link.touches.get() # the wearer's next touch
print(message.pattern, message.slot)
await link.close()
Pick any 32-bit AUID and keep it. A bracelet set up with another AUID, for example by the official app, reports AUID_ERROR (and link.needs_setup), goes to its out-of-box state and flashes. The app then asks the wearer to double-tap it to continue, which the link answers.
Newer firmware also asks for OS-level bonding after setup. The app triggers it by writing v2.ancs(True) on V2, by subscribing to uuids.BOND_TRIGGER on V4 and Mini, and with createBond() on V3 (client.pair() in bleak). Once bonded it writes v2.after_bonding(generation).
Choices the manual leaves open
Where the app's code did not settle a detail, this library picks one and says so in the docstring:
v2.play_blendblends the colour at the start of each 200 ms frame.patterns.to_samplessqueezes patterns longer than 10 s by rounding run boundaries to the nearest sample.BraceletLink.clear_colouron V1 sends colour black.BraceletLinksends V1 commandseqfrom 0 after each connection.
Protocol notes
docs/bond-touch-ble-protocol.md: discovery, the GATT layout, the connection lifecycle, each generation's state, events and commands, firmware updates and the open questions.
Not affiliated with Impossible Labs. The name identifies the devices this interoperates with.
License
MIT License, copyright 2026 Kadima Tech.
Metadata
Release files for openble-bondtouch 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| openble_bondtouch-0.1.0.tar.gz | 47.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openble_bondtouch-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 74.7 kB
Release files / openble_bondtouch-0.1.0.tar.gz
| Download URL | openble_bondtouch-0.1.0.tar.gz |
|---|---|
| Size | 47.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
efccad0b1bc866febba54eb865517ddb880236785ca3c805da43fa6faf64f2cc
|
|
BLAKE2b-256 checksum How to use checksums |
d1c5e116e91f5f50ae277a5efbfc1d34f42171eceeb56c59ef1b59f44e92a205
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / openble_bondtouch-0.1.0-py3-none-any.whl
| Download URL | openble_bondtouch-0.1.0-py3-none-any.whl |
|---|---|
| Size | 27.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60f7c84bdf23b6f34b6f24d96af821e9e8c73fe666b1f13321b7c0703abec1a7
|
|
BLAKE2b-256 checksum How to use checksums |
297fdcdc879ccc05ff08a5be0c949c782e656fa328d733503159dbd616cbe2fd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for openble-bondtouch 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for openble_bondtouch-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.