Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

specialized_turbo-0.1.0.tar.gz (24.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

specialized_turbo-0.1.0-py3-none-any.whl (18.1 kB view details)

Uploaded Python 3

File details

Details for the file specialized_turbo-0.1.0.tar.gz.

File metadata

  • Download URL: specialized_turbo-0.1.0.tar.gz
  • Upload date:
  • Size: 24.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for specialized_turbo-0.1.0.tar.gz
Algorithm Hash digest
SHA256 11484094241d9d42205b57fcc02a66fae75e1a55db924831dfd75127a4edc9f1
MD5 ca1e54a935b3681a1226e84dd9fa25b1
BLAKE2b-256 f69faf4d9689b6ac7c5b61f2d75819bc2fff3bef2add27aa0bb2b9e6e5bcdf36

See more details on using hashes here.

Provenance

The following attestation bundles were made for specialized_turbo-0.1.0.tar.gz:

Publisher: publish.yml on JamieMagee/specialized-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file specialized_turbo-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for specialized_turbo-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 29f70e482c8cbf53036819c1f3fbb3d6a46879a459e2a92db6c61aca1bfd80ab
MD5 70e4e65dabc92d7a223cea6a11890cfb
BLAKE2b-256 84afa9dd55b3079746f12bf3c1e5a784194322c318b371b1f8e9728521f58122

See more details on using hashes here.

Provenance

The following attestation bundles were made for specialized_turbo-0.1.0-py3-none-any.whl:

Publisher: publish.yml on JamieMagee/specialized-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page