Z1 WalkingPad MCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Z1 WalkingPad MCPstart walking at 2.5 km/h"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Z1 WalkingPad
Control a KingSmith WalkingPad Z1 treadmill from your Mac β menu-bar app, CLI, or AI assistant. Reverse-engineered BLE protocol, fully documented.
The WalkingPad Z1 (KS-HD-Z1D, firmware V0.0.6) speaks standard Bluetooth FTMS β but locks it behind a vendor unlock handshake that stops every generic client cold. This project cracked that gate and ships two independent, production-ready implementations around it:
π₯οΈ macOS menu-bar app β native SwiftUI, lives in your menu bar, Apple-clean UI
π€ MCP server + CLI + Python library β drive the treadmill from AI assistants (Claude, Kimi, β¦) or scripts
Everything is documented well enough to build your own client in any language: see the protocol quick reference.
Features
βΆοΈ Start / stop / pause, set speed, Β±nudge steppers (1.6β6.4 km/h range)
π Live telemetry: speed, distance, elapsed time, steps β self-calibrating estimate that learns your stride-vs-speed curve (pad counters are unreliable at slow speeds; belt distance is exact)
π₯ Calorie estimate via the ACSM walking metabolic equation (research-backed; Β±10β15%)
π§ The pad is the master β app reflects reality even when you use the physical remote
π Pad is the master for counters; calories and steps persist across reconnects (gap-credited) β or flip on "Persist stats across sessions" to accumulate until you hit Clear
π΄ Exit stops the belt and puts the pad in standby
π Zero measurable battery impact (0.0% CPU, 0.0 power score)
πΊπΈ Imperial/metric units, synced to the pad's own display
π Session history as JSON; Apple Health bridge via iCloud + iOS Shortcut
Related MCP server: Apple Health MCP
Quick start
macOS menu-bar app
git clone https://github.com/slandau3/z1-walkingpad-mcp.git
cd z1-walkingpad-mcp/macos
bash build-app.sh --install # builds + signs Z1WalkingPad.app β /ApplicationsLaunch it, click the figure.walk menu-bar icon β Connect. Requires only the macOS Command Line Tools β no Xcode, no Python.
MCP server / CLI (Python)
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e ".[dev,mcp]"
.venv/bin/python -m z1_walkingpad_mcp start --speed 2.5 # CLI: start at 2.5 km/h
.venv/bin/python -m z1_walkingpad_mcp.server # MCP: stdio serverMCP client config:
{
"mcpServers": {
"z1-walkingpad": {
"command": "/path/to/z1-walkingpad-mcp/.venv/bin/python",
"args": ["-m", "z1_walkingpad_mcp.server"],
"env": { "Z1_WEIGHT_KG": "80" }
}
}
}Tools: treadmill_status Β· treadmill_start Β· treadmill_set_speed Β· treadmill_speed_up Β· treadmill_speed_down Β· treadmill_pause Β· treadmill_stop
Contents
The two frontends
macOS menu-bar app | MCP server / CLI / Python lib | |
Purpose | Manual daily control | AI-assistant control and scripts |
Code | Swift, CoreBluetooth ( | Python, bleak ( |
Install |
|
|
Units | Imperial/metric setting (default Imperial) | km/h + kg; |
Session log | in-app summary | JSON in |
Settings | units, body weight, speed step | env vars |
Both talk to the pad directly and independently β neither needs the other. Only one BLE connection at a time: quit the app (or stop the MCP server) before using the other.
Behavior nuances (what to expect)
The pad is the master β of everything. Belt state, time, distance, and steps are shown exactly as the pad reports them, however they were changed (app, remote, or the pad's own timer). When the pad resets its counters (on Stop, or on its own schedule), the display follows.
Calories are the one thing we compute (the pad reports none) β but they follow the same lifecycle: the estimate resets when the pad's counters reset, so the numbers never disagree about what "this session" is.
Calories and steps persist across reconnects. Saved every second; disconnect while the belt is still moving and both counts continue, with the disconnected gap estimated (avg-speed calories, stride-based steps) β because the pad's session never ended.
Want a trip odometer instead? Settings β Persist stats across sessions makes time/distance/steps/kcal keep accumulating across Stops (the pad's resets are folded in) until you hit the Clear button beside it. Off = pad-as-master.
Step counts are corrected, not just relayed. Consumer step counters degrade badly below ~3 km/h (research: 20β40% error at desk speeds), but belt distance is mechanically exact β so once you've walked at β₯3 km/h for a few minutes, the app has learned your personal stride-vs-speed curve and derives slow-speed steps from distance instead. Until then it shows the pad's raw count.
Start on a moving belt is a no-op (the pad refuses it) β
start()skips the command when the belt is already moving.Exit stops the belt and sleeps the pad, then quits β never hangs more than 3 s.
Battery: unmeasurable. 0.0% CPU / 0.0 power-impact; BLE at one small packet per second is designed for coin-cell devices.
One user at a time: the pad accepts a single BLE connection β if Connect spins forever, something else (phone app, the other frontend) is holding it.
Usage
macOS menu-bar app
Click the menu-bar icon β Connect. Big speed readout with β/+ steppers, Start/Stop, live elapsed/distance/steps/kcal grid, last-session line. Settings (expandable): Imperial/Metric (also syncs the pad's own LED units), body weight, speed per β/+ tap. Exit stops the belt and sleeps the pad. Details: macos/README.md.
CLI
.venv/bin/python -m z1_walkingpad_mcp status # connect, unlock, dump properties
.venv/bin/python -m z1_walkingpad_mcp start --speed 2.5 # start belt, set 2.5 km/h
.venv/bin/python -m z1_walkingpad_mcp start --duration 30 # auto-stop after 30 s
.venv/bin/python -m z1_walkingpad_mcp up --delta 0.2 # nudge speed up (default 0.1)
.venv/bin/python -m z1_walkingpad_mcp down # nudge speed down
.venv/bin/python -m z1_walkingpad_mcp stop # stop + session summarytreadmill_stop / CLI stop returns the session summary (duration, distance, steps, avg speed, kcal) and writes it to ~/.z1-walkingpad/ (sessions.jsonl + per-session JSON; override with Z1_SESSIONS_DIR).
How it works (the protocol)
The Z1 speaks standard Bluetooth SIG FTMS (0x1826) for control and telemetry β but everything is gated behind a vendor unlock handshake on the KingSmith supplement service (24e2521c-β¦-c5330a00fdf7). Until the unlock frame lands, the pad silently ignores every FTMS Control Point write and suppresses all notifications. This is why generic FTMS clients connect fine but can do nothing.
No bonding, no pairing, no MTU requirement: the name-derived unlock token is the entire auth mechanism.
Connect sequence
Subscribe the supplement notify characteristic (
β¦b00fdf7) before writing anythingWrite the unlock frame to
β¦d00fdf7without response:71 00 05 01 <T> CCwhereT = LE32(last 4 chars of BLE name) + 1andCC = sum(all prior bytes) & 0xFF. ForKS-HD-Z1D:71 00 05 01 2e 5a 31 44 74Pad replies
71 80on the notify char (usually <100 ms) β unlockedOptional session init:
SYS_INFO(71 01 08 <unix LE32> <uid LE32> CCβ71 81),SETTING_GET(72 00 01 00 73β72 80property dump)FTMS now behaves like the textbook spec and telemetry notifications flow
GATT map
UUID | Props | Purpose |
| service | standard FTMS fitness machine service |
| read | fitness machine features |
| read | supported speed range β 1.6β6.4 km/h (0.1 steps) |
| notify | treadmill data (live telemetry, ~1 frame/s while running) |
| notify | machine status (04 started, 01/02 stopped) |
| write, indicate | FTMS control point (start/stop/speed) |
| service | KingSmith supplement service (the gate) |
| notify | supplement read channel |
| write, write-no-rsp | supplement write channel |
| β | JieLi-chip OTA β do not touch |
Vendor (supplement) frames
[cmd0, cmd1, len, data[len], checksum] checksum = sum(all prior bytes) & 0xFFWrites go to
β¦d00fdf7as write without response, paced β₯400 ms (faster writes are dropped)Unlock:
71 00 05 01 <T> CCβ71 80SYS_INFO:
71 01 08 <ts LE32> <uid LE32> CCβ71 81Property read all:
72 00 01 00 73β72 80, data = 4-byte records[id, error, valLo, valHi]Property write:
72 01 03 <id> <lo> <hi> CCβ72 81(data[1]=0= OK)Unsolicited: property pushes
72 50, exercise-record events73 50, fault records73 51Never send frames starting with
0xE8(OTA mode β brick risk)
Properties observed on the Z1: 1 units/language (bit 1 = miles; written when the app syncs display units), 2 auto-stop, 4 motor version, 5 last error, 6 child lock, 8 switches (buzzer/light), 10 device mode β bits 5β7: 0=manual, 1=auto, 2=sleep (used by Exit).
FTMS control (post-unlock)
Control point 0x2AD9, write with response, pace β₯400 ms. Indication replies: [0x80, request-op, result, β¦].
Op | Bytes | Effect |
Request Control |
| required once before any command |
Reset |
| |
Set Target Speed |
| e.g. 2.5 km/h = |
Start/Resume |
| belt ramps to minimum speed (1.6 km/h); fails (4) if belt already moving |
Stop / Pause |
| Stop finalizes the pad session (resets its counters) |
Result | Meaning |
1 | success |
2 | op not supported |
3 | invalid parameter |
4 | failed |
5 | control not permitted β re-send |
Typical session: 00 β 07 β 02 β¦ β 08 01.
Quick reference (build-your-own cheat sheet)
Connection constants:
Scan name prefix
KS-HD-Z1; vendor writes β₯400 ms apart (dropped if faster); write-without-response on the vendor char, with-response on the control pointAwait unlock
71 80up to ~10 s (usually <100 ms); other vendor replies ~3 s; control indications ~3 s
Vendor channel (β¦d00fdf7 write / β¦b00fdf7 notify, frame [cmd0, cmd1, len, data, sum&0xFF]):
Frame | Bytes | Reply |
Unlock |
|
|
SYS_INFO |
|
|
Property read (all / one) |
|
|
Property write |
|
|
Func/method info |
|
|
(unsolicited) | β |
|
Vendor control tunnel (alternative to FTMS control point): 77 01 <len> <op> <paramsβ¦> CC β reply 77 81, data[0]=op, status data[1] (0 or 0x81 = OK). Ops mirror FTMS: start 77 01 01 07 7F, stop 77 01 02 08 01 82, speed 77 01 03 02 <u16 LE km/hΓ100> CC.
Machine status (0x2ADA, notify): 04 started, 02 user stop/pause, 01 safety-key stop, 05 speed changed, FF control lost.
Telemetry flags (0x2ACD, u16 LE then fields in order): bit0 clear β speed u16 (km/hΓ100) Β· bit1 avg speed u16 Β· bit2 distance u24 m Β· bit3 incline+ramp s16Γ2 Β· bit4 Β±elevation u16Γ2 Β· bit5 pace u8 Β· bit6 avg pace u8 Β· bit7 energy u16+u16+u8 Β· bit8 HR u8 Β· bit9 MET u8 Β· bit10 elapsed u16 s Β· bit11 remaining u16 s Β· bit13 steps u16 (KingSmith). Z1 sends flags 0x2404 + speed.
Device facts: firmware V0.0.6 (0x2A26), speed range 1.6β6.4 km/h (0x2AD4, u16Γ2 km/hΓ100), one BLE connection at a time, no GAP service.
Telemetry
FTMS treadmill data 0x2ACD: flags u16 LE, fields in flag order. The Z1 sends distance (bit 2, u24 m), elapsed time (bit 10, u16 s), step count (bit 13, u16 β KingSmith extension), plus instantaneous speed (bit 0 clear, u16 km/hΓ100). Counters are cumulative and persist across BLE connections while the session is open.
What the pad does NOT provide
Calories / heart rate β no HR sensor, no energy bit in telemetry. Calories are computed locally (below).
Display/screen control β not exposed over BLE at all; the LED panel cycling is RF-remote only.
Incline β fixed hardware.
Health metrics
Calories use the ACSM walking metabolic equation (exercise-physiology standard, level grade):
VO2 (ml/kg/min) = 0.1 Γ speed(m/min) + 3.5
kcal/min = VO2 Γ weight_kg / 200Continuous in speed; chosen over the Compendium MET table because research shows fixed MET buckets misclassify intensity at slow walking speeds (PubMed 35876127). Expect Β±10β15% real-world error. Weight: Z1_WEIGHT_KG (Python) or the in-app setting (macOS).
Apple Health: HealthKit is unavailable on macOS (verified: HKHealthStore.isHealthDataAvailable() β false), so sessions reach Health via the iPhone: point Z1_SESSIONS_DIR at an iCloud Drive folder and use the iOS Shortcut recipe in docs/apple-health.md.
Development
# Python: tests + typecheck-friendly layout (src/)
.venv/bin/python -m pytest tests/ # 16 unit tests, no BLE needed
# Swift: build + framework-free test suite (CLT ships no XCTest)
cd macos && swift build && swift run z1tests # 54 checks
# Hardware smoke test (no belt movement)
cd macos && swift run z1smokeRepository layout:
src/z1_walkingpad_mcp/
βββ protocol.py/constants.py Core: frame builders/parsers, UUIDs, opcodes
βββ client.py Core: Z1Treadmill async BLE client
βββ metrics.py Core: ACSM calorie estimation
βββ cli.py Frontend: command line
βββ server.py Frontend: MCP server
βββ strava.py Frontend: optional Strava upload (unused by default)
macos/ Frontend: native menu-bar app (Swift, independent build)
docs/ protocol.md Β· reverse-engineering.md Β· apple-health.md Β· strava.md
scripts/ The BLE reverse-engineering record (incl. the winning replay)
tests/ Python unit testsHow this was found
Static reverse engineering under constraints (no official-app install, no extra hardware): blutter disassembly of the KS Fit Android app, upstream issue archaeology, and the breakthrough β the duttke.de Web Bluetooth implementation, whose unlock variant is the one firmware V0.0.6 accepts. Full story: docs/reverse-engineering.md. Byte-level spec: docs/protocol.md.
Safety model
Operation | Mutating? | Notes |
Scan / connect / read / notify | No | Unlock is a no-op auth handshake, not a command |
Request Control ( | Yes (control claim) | Required before commands; does not move belt |
Start / Stop / Set Speed / Sleep | Yes | Only ever sent deliberately (Exit sends stop + sleep) |
The pad allows one BLE connection at a time; close other controllers before connecting.
Acknowledgments
duttke.de/en/walkingpad β the Web Bluetooth implementation whose unlock frame variant cracked the gate
mcdax/walkingpad-controller β FTMS/WiLink library and KS Fit protocol docs
worawit/blutter β Flutter AOT disassembler used on the KS Fit APKs
License
MIT β see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceLocal-first MCP server that connects AI agents to your Withings body, sleep, activity and heart data.Last updated231503MIT
- AlicenseAqualityAmaintenanceLocal-first MCP server that reads Apple Health export files (export.xml/zip) and exposes activity, sleep, HRV, and workout data to AI agents, keeping all data on your machine.Last updated181111MIT
- Alicense-qualityBmaintenanceMCP server for controlling an ESP32-C3 PetOS watch over BLE. Enables scanning, connecting, sending pet actions, and showing frames through MCP tools.Last updatedMIT
- Alicense-qualityCmaintenanceControl a Govee BLE RGBIC LED strip from Claude (or any MCP client) over Bluetooth on macOS.Last updatedMIT
Related MCP Connectors
MCP server for Withings health data β sleep, activity, heart, and body metrics.
Open-source MCP server for Zerodha Kite Connect. Portfolio, market data, backtesting, alerts.
MCP server wrapping the Tesla Fleet API and TeslaMate API
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/slandau3/z1-walkingpad-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server