Python library for communicating with Specialized Turbo e-bikes over Bluetooth Low Energy
Project description
specialized-turbo
Read telemetry from Specialized Turbo e-bikes (Vado, Levo, Creo) over Bluetooth Low Energy. Speed, power, cadence, battery, motor temp, odometer, assist level -- all the data the Mission Control app sees, in Python.
Based on the Gen 2 "TURBOHMI2017" protocol, reverse-engineered by Sepp62/LevoEsp32Ble.
Uses bleak for BLE, async throughout. Includes a CLI. Full protocol docs in docs/protocol.md.
Installation
pip install specialized-turbo
Quick Start
Python API
import asyncio
from specialized_turbo import SpecializedConnection, TelemetryMonitor
async def main():
async with SpecializedConnection("DC:DD:BB:4A:D6:55", pin=946166) as conn:
monitor = TelemetryMonitor(conn)
await monitor.start()
# Stream telemetry as it arrives
async for msg in monitor.stream():
print(f"{msg.field_name} = {msg.converted_value} {msg.unit}")
asyncio.run(main())
Access the snapshot
Instead of streaming, you can just read the snapshot after collecting for a bit:
async with SpecializedConnection("DC:DD:BB:4A:D6:55", pin=946166) as conn:
monitor = TelemetryMonitor(conn)
await monitor.start()
await asyncio.sleep(5) # collect data
snap = monitor.snapshot
print(f"Speed: {snap.motor.speed_kmh} km/h")
print(f"Battery: {snap.battery.charge_pct}%")
print(f"Power: {snap.motor.rider_power_w} W (rider) + {snap.motor.motor_power_w} W (motor)")
print(f"Cadence: {snap.motor.cadence_rpm} RPM")
print(f"Assist: {snap.motor.assist_level}")
Query a specific value
from specialized_turbo import SpecializedConnection, Sender, BatteryChannel
async with SpecializedConnection("DC:DD:BB:4A:D6:55", pin=946166) as conn:
msg = await conn.request_value(Sender.BATTERY, BatteryChannel.CHARGE_PERCENT)
print(f"Battery: {msg.converted_value}%")
CLI
Scan for bikes:
specialized-turbo scan
specialized-turbo scan --timeout 15
Stream telemetry:
specialized-turbo telemetry DC:DD:BB:4A:D6:55 --pin 946166
specialized-turbo telemetry DC:DD:BB:4A:D6:55 --pin 946166 --format json
specialized-turbo telemetry DC:DD:BB:4A:D6:55 --pin 946166 --duration 30
Read a single value:
specialized-turbo read list # show available fields
specialized-turbo read battery_charge_percent DC:DD:BB:4A:D6:55 --pin 946166
specialized-turbo read speed DC:DD:BB:4A:D6:55 --pin 946166 --format json
Dump GATT services (for debugging):
specialized-turbo services DC:DD:BB:4A:D6:55 --pin 946166
Available fields
| Field | Unit | Description |
|---|---|---|
battery_capacity_wh |
Wh | Total battery capacity |
battery_remaining_wh |
Wh | Remaining energy |
battery_health |
% | Battery health |
battery_temp |
°C | Battery temperature |
battery_charge_cycles |
cycles | Number of charge cycles |
battery_voltage |
V | Battery voltage |
battery_current |
A | Battery current draw |
battery_charge_percent |
% | State of charge |
rider_power |
W | Rider pedal power |
cadence |
RPM | Pedaling cadence |
speed |
km/h | Current speed |
odometer |
km | Total distance |
assist_level |
— | OFF / ECO / TRAIL / TURBO |
motor_temp |
°C | Motor temperature |
motor_power |
W | Electric motor power |
wheel_circumference |
mm | Wheel circumference setting |
assist_lev1_pct |
% | ECO assist percentage |
assist_lev2_pct |
% | TRAIL assist percentage |
assist_lev3_pct |
% | TURBO assist percentage |
acceleration |
% | Acceleration sensitivity |
Pairing
The bike needs a 6-digit PIN for BLE pairing, shown on its TCU screen. Pass it via --pin (CLI) or pin= (Python).
On Windows, bleak's WinRT backend can handle passkey pairing programmatically. If that doesn't work, pair through Windows Bluetooth Settings first, then connect without --pin.
Protocol docs
See docs/protocol.md for the full protocol reference: UUIDs, message format, field definitions with conversion formulas, authentication, and communication patterns.
Development
uv sync --extra dev
uv run pytest
License
MIT
Credits
Protocol reverse-engineered by Sepp62/LevoEsp32Ble (C++/ESP32, MIT).
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 specialized_turbo-0.2.4.tar.gz.
File metadata
- Download URL: specialized_turbo-0.2.4.tar.gz
- Upload date:
- Size: 29.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ac6fd158a113fe8cffa2723fe7b6779483ad96b2cb3a03bc288eab096d55374
|
|
| MD5 |
7ba1192c8bff0662addbc356c6fd3c0a
|
|
| BLAKE2b-256 |
016a74ab932e89992a8a30bb4835fbea2e4747d579824b2cc2a025b665f75ead
|
Provenance
The following attestation bundles were made for specialized_turbo-0.2.4.tar.gz:
Publisher:
publish.yml on JamieMagee/specialized-turbo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
specialized_turbo-0.2.4.tar.gz -
Subject digest:
5ac6fd158a113fe8cffa2723fe7b6779483ad96b2cb3a03bc288eab096d55374 - Sigstore transparency entry: 1217830393
- Sigstore integration time:
-
Permalink:
JamieMagee/specialized-turbo@5fd7491b833e5a7756237d0bb0961ee8b27628ac -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/JamieMagee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5fd7491b833e5a7756237d0bb0961ee8b27628ac -
Trigger Event:
release
-
Statement type:
File details
Details for the file specialized_turbo-0.2.4-py3-none-any.whl.
File metadata
- Download URL: specialized_turbo-0.2.4-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff8bcc2ed32468c9cedca9e27e5127570e6c7916ed6322e66043ef64577a9322
|
|
| MD5 |
287ddbe87ec35ad81589bed31e55f301
|
|
| BLAKE2b-256 |
73d915e193dcd986e75106ed7b5880bfe518a3dd98a01210c23879e7a49e6219
|
Provenance
The following attestation bundles were made for specialized_turbo-0.2.4-py3-none-any.whl:
Publisher:
publish.yml on JamieMagee/specialized-turbo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
specialized_turbo-0.2.4-py3-none-any.whl -
Subject digest:
ff8bcc2ed32468c9cedca9e27e5127570e6c7916ed6322e66043ef64577a9322 - Sigstore transparency entry: 1217830397
- Sigstore integration time:
-
Permalink:
JamieMagee/specialized-turbo@5fd7491b833e5a7756237d0bb0961ee8b27628ac -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/JamieMagee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5fd7491b833e5a7756237d0bb0961ee8b27628ac -
Trigger Event:
release
-
Statement type: