Skip to content

Add Sperax P3 Max (wi-linktech WLT6200) protocol support#4

Open
mcdax wants to merge 2 commits into
mainfrom
add-sperax-p3max
Open

Add Sperax P3 Max (wi-linktech WLT6200) protocol support#4
mcdax wants to merge 2 commits into
mainfrom
add-sperax-p3max

Conversation

@mcdax

@mcdax mcdax commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Adds a third protocol backend (alongside FTMS and WiLink) for the Sperax P3 Max walking pad (BLE name SPERAX_P3MAX). The device uses a wi-linktech WLT6200 module speaking a custom framed protocol on vendor service 0xFFF0 — it is not FTMS (0x1826) and not the legacy KingSmith WiLink protocol (0xFE00).

Requested in hass-walkingpad#3.

Protocol

Fully documented in docs/sperax-p3max-protocol.md.

F5 | LEN | 00 | CMD | ARGS | CRC16 | FA
  • Byte-stuffing: body bytes 0xF00xFF are sent as F0 (byte & 0x0F)
  • CRC-16: poly 0xA327, init 0xFFFF, reflected, little-endian
  • Commands: hello 0x01, run 0x15 (speed = km/h×10, incline 0–2), vibration 0x16 (level 1–4), status poll 0x19
  • The device only streams status while polled, so the backend runs a keep-alive poll loop.

Reverse-engineered from a full HCI snoop capture of a real P3 Max session.

Changes

  • sperax.py (new)SperaxController backend + frame codec (crc16/encode/decode) + 0x19 status parser + poll loop
  • const.py — Sperax UUIDs, SPERAX_NAME_PREFIXES, ProtocolType.SPERAX
  • controller.py — name/service auto-detection + dispatch; new set_incline() / set_vibration() (Sperax-only)
  • __init__.py — export SperaxController and Sperax constants
  • tests/test_sperax_codec.py (new) — hardware-free codec/parser tests
  • docs/sperax-p3max-protocol.md (new) — full protocol reference

The unified WalkingPadController API (connect/start/set_speed/stop/…) works unchanged, so downstream consumers (e.g. hass-walkingpad) need no changes.

Verification

  • 7/7 unit tests pass (pytest tests/test_sperax_codec.py)
  • decode() validates 146/146 real captured frames; encode() round-trips every captured command frame byte-for-byte (incl. stuffing)
  • ruff clean on all added/changed files

Not yet confirmed on hardware

  • Speed range assumed 0.5–6.0 km/h (capture only reached 3.0; no readable range characteristic on this device)
  • Status distance/duration/steps map to raw counters that aren't unit-verified yet — speed, belt state and vibration level are verified

🤖 Generated with Claude Code

mcdax and others added 2 commits July 18, 2026 10:40
Adds a third protocol backend alongside FTMS and WiLink for the Sperax
P3 Max walking pad (BLE name SPERAX_P3MAX). The device uses a wi-linktech
WLT6200 module with a custom framed protocol on vendor service 0xFFF0 —
not FTMS (0x1826) and not the legacy KingSmith WiLink protocol (0xFE00).

Protocol (fully documented in docs/sperax-p3max-protocol.md):
  F5 | LEN | 00 | CMD | ARGS | CRC16 | FA
  - byte-stuffing: body bytes 0xF0-0xFF sent as F0 (byte & 0x0F)
  - CRC-16 poly 0xA327, init 0xFFFF, reflected, little-endian
  - commands: hello 0x01, run 0x15 (speed = km/h x10, incline 0-2),
    vibration 0x16 (level 1-4), status poll 0x19
  - device only streams status while polled -> keep-alive poll loop

Reverse-engineered from a full HCI snoop capture of a real P3 Max
session; the codec is verified against 146/146 distinct captured frames
and encode() reproduces every captured command frame byte-for-byte.

Changes:
  - sperax.py: SperaxController backend + frame codec + status parser
  - const.py: Sperax UUIDs, name prefixes, ProtocolType.SPERAX
  - controller.py: auto-detection + dispatch; set_incline/set_vibration
  - __init__.py: export SperaxController and Sperax constants
  - tests/test_sperax_codec.py: hardware-free codec/parser tests
  - docs/sperax-p3max-protocol.md: full protocol reference

Speed range (0.5-6.0 km/h) and status counters (distance/duration/steps)
are best-effort pending confirmation on hardware; speed, belt state and
vibration level are verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- P3 Max tops out at 12.0 km/h (confirmed by device owner, hass-walkingpad#3),
  not the previously-assumed 6.0. The speed byte still follows km/h x10.
- Record the vibration level (status offset 18) in TreadmillStatus.vibration_level
  so consumers can surface it.
- Doc: update open items (confirmed max speed; incline/decline capture still needed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant