Skip to main content

pymvtreadmill 🏃💨

A modern Python 3.13+ library to control and read data from Mobvoi Home Treadmills and Horizon fitness devices.

Features

  • Real-time Reading: Parses Speed directly from BLE notifications.
  • Modern Async: Built on asyncio and bleak.
  • Control: Set target speed (km/h) via Python commands.
  • Auto-Reconnect: Robust connection handling for continuous use.
  • Type Safe: Fully typed with modern Python 3.13+ syntax.
  • MQTT Bridge: Built-in CLI to bridge treadmill data to MQTT.

Data Protocol

The treadmill sends data as BLE notifications. Currently, only Speed is parsed.

Feature Resolution Protocol
Speed (Mobvoi) 0.01 km/h Bytes 3-4 (Big Endian)
Speed (Horizon) 0.1 km/h Bytes 3-4 (Big Endian)

Note: The current implementation defaults to Mobvoi resolution (0.01 km/h).

Installation

uv pip install pymvtreadmill

Using pip

pip install pymvtreadmill

MQTT Bridge (CLI)

This package includes a command-line interface (CLI) to bridge treadmill data to an MQTT broker (e.g., for Home Assistant).

Usage

pymvtreadmill --help

Arguments

Argument Description Default
--treadmill-name Name filter for the treadmill Mobvoi
--mqtt-host MQTT broker hostname (Required for MQTT) -
--mqtt-port MQTT broker port 1883
--mqtt-username MQTT username -
--mqtt-password MQTT password -
--mqtt-topic MQTT topic for speed homeassistant/sensor/treadmill/speed/state

Running with Docker

You can run the project using Docker. This is useful if you want to run the treadmill controller in an isolated environment.

Prerequisites

  • Docker installed on your machine.
  • A Bluetooth adapter compatible with Linux (BlueZ).

Building the Image

docker build -t pymvtreadmill .

Running the CLI

To access the Bluetooth adapter from within the container, you need to share the DBus socket and run in privileged mode (or with NET_ADMIN capabilities).

By default, the container runs the pymvtreadmill CLI. You can pass arguments directly to it.

# Show help
docker run --rm -it pymvtreadmill --help

# Run as MQTT Bridge
docker run --rm -it \
  --net=host \
  --privileged \
  -v /var/run/dbus:/var/run/dbus \
  pymvtreadmill --mqtt-host 192.168.1.100 --mqtt-username myuser --mqtt-password mypass

Running Custom Scripts

If you want to run a custom python script (like the examples), you need to override the entrypoint:

docker run --rm -it \
  --entrypoint python \
  --net=host \
  --privileged \
  -v /var/run/dbus:/var/run/dbus \
  -v $(pwd)/my_script.py:/app/my_script.py \
  pymvtreadmill my_script.py

Docker Compose

For a more permanent setup, you can use the provided docker-compose.yml file.

  1. Edit docker-compose.yml to set your MQTT broker details in the command section.
  2. Run:
    docker-compose up -d
    

Usage (Library)

import asyncio
from pymvtreadmill import TreadmillClient

async def main():
    # You can connect by:
    # 1. Scanning (default): TreadmillClient()
    # 2. Address: TreadmillClient().connect("AA:BB:CC:DD:EE:FF")
    # 3. BLEDevice: TreadmillClient().connect(ble_device)

    async with TreadmillClient() as client:
        print(f"Connected to {client.client.address}")

        # Read properties
        print(f"Speed: {client.speed} km/h")
        print(f"Inclination: {client.inclination}%")
        print(f"Distance: {client.distance} m")

        # Set speed to 2.5 km/h
        await client.set_speed(2.5)

        # Keep running for a bit
        await asyncio.sleep(10)

if __name__ == "__main__":
    asyncio.run(main())

API Reference

TreadmillClient

Properties (Read-Only):

  • speed (float): Current speed in km/h.
  • inclination (float | None): Current inclination percentage (0.0 - 15.0+).
  • distance (int | None): Current session distance in meters.
  • total_distance (int): Total accumulated distance in meters across sessions.
  • last_run_distance (int | None): Distance of the last completed run session.
  • is_running (bool): Whether the treadmill is currently active.

Methods:

  • connect(device: BLEDevice | str | None = None): Connects to the treadmill. Accepts a bleak.backends.device.BLEDevice, a MAC address string, or None (triggers a scan).
  • disconnect(): Disconnects from the device.
  • set_speed(speed_kmh: float): Sets the target speed in km/h.

Development

This project strictly requires Python 3.13 or newer. We use uv for dependency management.

Setup

./scripts/setup_env.sh

Verification

uv run ruff check .
uv run black --check .
uv run mypy .
uv run pytest

Please read AGENTS.md before contributing to understand the architectural guidelines and protocol details.

Metadata

Release files for pymvtreadmill 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymvtreadmill 0.1.0
File Size Uploaded
pymvtreadmill-0.1.0.tar.gz 44.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pymvtreadmill 0.1.0
File Interpreter ABI Platform
pymvtreadmill-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.2 kB

Release files / pymvtreadmill-0.1.0.tar.gz

Download URL pymvtreadmill-0.1.0.tar.gz
Size 44.2 kB
Tags Source
SHA-256 checksum
How to use checksums
9d7849ff96c37daa2e564550aa5a11af8ea4fab7560ffd8e1155acbbf0e1c558
BLAKE2b-256 checksum
How to use checksums
1da3210faf98ab950955c94cb83bc396cd35251623df9f0024fdbfd20ae366e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Feb 24, 2026.

Transparency log

Release files / pymvtreadmill-0.1.0-py3-none-any.whl

Download URL pymvtreadmill-0.1.0-py3-none-any.whl
Size 11.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ccc27300a1e22dc7d1bc2897857e48bcb5e3287d5b10775c14d24df9c6b8f9ec
BLAKE2b-256 checksum
How to use checksums
5b303037675f779ebcf75328c3022ba2814279c3b70affb34a175b06ce142c36
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Feb 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page