walkingpad-controller 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- walkingpad_controller-0.1.0/.github/workflows/publish.yml +51 -0
- walkingpad_controller-0.1.0/.gitignore +16 -0
- walkingpad_controller-0.1.0/LICENSE +21 -0
- walkingpad_controller-0.1.0/PKG-INFO +184 -0
- walkingpad_controller-0.1.0/README.md +156 -0
- walkingpad_controller-0.1.0/pyproject.toml +45 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/__init__.py +57 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/const.py +130 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/controller.py +481 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/ftms.py +657 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/models.py +75 -0
- walkingpad_controller-0.1.0/src/walkingpad_controller/wilink.py +205 -0
- walkingpad_controller-0.1.0/tests/test_real_device.py +143 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
name: Build distribution
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Install build tools
|
|
23
|
+
run: pip install build
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: python -m build
|
|
27
|
+
|
|
28
|
+
- name: Upload artifacts
|
|
29
|
+
uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish-pypi:
|
|
35
|
+
name: Publish to PyPI
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment:
|
|
39
|
+
name: pypi
|
|
40
|
+
url: https://pypi.org/p/walkingpad-controller
|
|
41
|
+
permissions:
|
|
42
|
+
id-token: write
|
|
43
|
+
steps:
|
|
44
|
+
- name: Download artifacts
|
|
45
|
+
uses: actions/download-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
|
|
50
|
+
- name: Publish to PyPI
|
|
51
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mcdax
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: walkingpad-controller
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python library for controlling KingSmith WalkingPad treadmills over BLE (FTMS and legacy WiLink protocols)
|
|
5
|
+
Project-URL: Homepage, https://github.com/mcdax/walkingpad-controller
|
|
6
|
+
Project-URL: Repository, https://github.com/mcdax/walkingpad-controller
|
|
7
|
+
Project-URL: Issues, https://github.com/mcdax/walkingpad-controller/issues
|
|
8
|
+
Author: mcdax
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ble,bluetooth,ftms,kingsmith,treadmill,walkingpad
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Home Automation
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: bleak>=0.20.0
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: ph4-walkingpad>=1.0.0; extra == 'all'
|
|
25
|
+
Provides-Extra: wilink
|
|
26
|
+
Requires-Dist: ph4-walkingpad>=1.0.0; extra == 'wilink'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# walkingpad-controller
|
|
30
|
+
|
|
31
|
+
Python library for controlling KingSmith WalkingPad treadmills over Bluetooth Low Energy (BLE).
|
|
32
|
+
|
|
33
|
+
Supports both **FTMS** (Fitness Machine Service) and legacy **WiLink** protocols behind a unified API. Protocol is auto-detected based on the BLE device name and advertised services.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Unified API** — single `WalkingPadController` class for all device types
|
|
38
|
+
- **Auto protocol detection** — FTMS for newer KS-HD-* devices, WiLink for older models
|
|
39
|
+
- **Real-time status** — speed, distance, duration, calories, steps via BLE notifications
|
|
40
|
+
- **Cold-start handling** — retry logic for KingSmith FTMS devices that need START_OR_RESUME before accepting speed commands
|
|
41
|
+
- **Reconnect recovery** — pending target speed is automatically re-applied after BLE reconnection
|
|
42
|
+
- **KingSmith extensions** — step counter via proprietary FTMS extension (bit 13)
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install walkingpad-controller
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For legacy WiLink device support (older WalkingPad models):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install walkingpad-controller[wilink]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import asyncio
|
|
60
|
+
from bleak import BleakScanner
|
|
61
|
+
from walkingpad_controller import WalkingPadController
|
|
62
|
+
|
|
63
|
+
async def main():
|
|
64
|
+
# Find your treadmill
|
|
65
|
+
device = await BleakScanner.find_device_by_name("KS-HD-Z1D")
|
|
66
|
+
|
|
67
|
+
# Create controller (protocol auto-detected from BLE name)
|
|
68
|
+
controller = WalkingPadController(ble_device=device)
|
|
69
|
+
|
|
70
|
+
# Connect
|
|
71
|
+
await controller.connect()
|
|
72
|
+
print(f"Protocol: {controller.protocol.value}")
|
|
73
|
+
print(f"Speed range: {controller.min_speed}-{controller.max_speed} km/h")
|
|
74
|
+
|
|
75
|
+
# Start at 3.0 km/h
|
|
76
|
+
await controller.start(target_speed=3.0)
|
|
77
|
+
|
|
78
|
+
# Read status
|
|
79
|
+
print(f"Speed: {controller.status.speed} km/h")
|
|
80
|
+
print(f"Steps: {controller.status.steps}")
|
|
81
|
+
|
|
82
|
+
# Stop and disconnect
|
|
83
|
+
await controller.stop()
|
|
84
|
+
await controller.disconnect()
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Status Callbacks
|
|
90
|
+
|
|
91
|
+
Register callbacks to receive real-time status updates:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from walkingpad_controller import WalkingPadController, TreadmillStatus
|
|
95
|
+
|
|
96
|
+
def on_status(status: TreadmillStatus):
|
|
97
|
+
print(f"Speed: {status.speed} km/h, Distance: {status.distance}m, "
|
|
98
|
+
f"Duration: {status.duration}s, Calories: {status.calories}, "
|
|
99
|
+
f"Steps: {status.steps}")
|
|
100
|
+
|
|
101
|
+
controller = WalkingPadController(ble_device=device)
|
|
102
|
+
controller.register_status_callback(on_status)
|
|
103
|
+
controller.register_disconnect_callback(lambda: print("Disconnected!"))
|
|
104
|
+
await controller.connect()
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## API Reference
|
|
108
|
+
|
|
109
|
+
### WalkingPadController
|
|
110
|
+
|
|
111
|
+
The main entry point. Auto-detects protocol and delegates to the appropriate backend.
|
|
112
|
+
|
|
113
|
+
| Property / Method | Description |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `protocol` | Detected protocol (`ProtocolType.FTMS` or `ProtocolType.WILINK`) |
|
|
116
|
+
| `connected` | Whether the device is currently connected |
|
|
117
|
+
| `status` | Current `TreadmillStatus` |
|
|
118
|
+
| `min_speed` / `max_speed` | Speed range in km/h (read from device for FTMS) |
|
|
119
|
+
| `speed_increment` | Speed step size in km/h |
|
|
120
|
+
| `connect()` | Connect and auto-detect protocol |
|
|
121
|
+
| `disconnect()` | Disconnect from the device |
|
|
122
|
+
| `start(target_speed=None)` | Start the belt, optionally at a target speed |
|
|
123
|
+
| `stop()` | Stop the belt |
|
|
124
|
+
| `set_speed(speed_kmh)` | Set speed (starts belt if stopped) |
|
|
125
|
+
| `switch_mode(mode)` | Switch operating mode (WiLink: auto/manual/standby) |
|
|
126
|
+
| `register_status_callback(cb)` | Register a `TreadmillStatus` callback |
|
|
127
|
+
| `register_disconnect_callback(cb)` | Register a disconnect callback |
|
|
128
|
+
| `update_ble_device(device)` | Update BLE device reference after rediscovery |
|
|
129
|
+
|
|
130
|
+
### TreadmillStatus
|
|
131
|
+
|
|
132
|
+
Dataclass with real-time treadmill data:
|
|
133
|
+
|
|
134
|
+
| Field | Type | Description |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `belt_state` | `int` | 0=stopped, 1=active, 5=standby, 9=starting |
|
|
137
|
+
| `speed` | `float` | Current speed in km/h |
|
|
138
|
+
| `mode` | `int` | Operating mode (0=auto, 1=manual, 2=standby) |
|
|
139
|
+
| `distance` | `int` | Total distance in meters |
|
|
140
|
+
| `duration` | `int` | Elapsed time in seconds |
|
|
141
|
+
| `steps` | `int` | Step count (FTMS KingSmith extension) |
|
|
142
|
+
| `calories` | `int` | Total energy in kcal |
|
|
143
|
+
| `calories_per_hour` | `int` | Energy rate |
|
|
144
|
+
| `heart_rate` | `int` | Heart rate in bpm (if available) |
|
|
145
|
+
| `timestamp` | `float` | Unix timestamp of last update |
|
|
146
|
+
|
|
147
|
+
### Protocol-Specific Controllers
|
|
148
|
+
|
|
149
|
+
For advanced use, you can use the protocol controllers directly:
|
|
150
|
+
|
|
151
|
+
- **`FTMSController`** — FTMS protocol (newer KS-HD-* devices)
|
|
152
|
+
- **`WiLinkController`** — Legacy WiLink protocol (older WalkingPad models, requires `[wilink]` extra)
|
|
153
|
+
|
|
154
|
+
## Supported Devices
|
|
155
|
+
|
|
156
|
+
### FTMS Protocol (tested)
|
|
157
|
+
- KingSmith KS-Z1D (BLE name: `KS-HD-Z1D`)
|
|
158
|
+
- Other KingSmith devices with BLE names starting with `KS-HD-`
|
|
159
|
+
|
|
160
|
+
### WiLink Protocol (via ph4-walkingpad)
|
|
161
|
+
- WalkingPad A1, A1 Pro
|
|
162
|
+
- WalkingPad C1, C2
|
|
163
|
+
- Other models supported by [ph4-walkingpad](https://github.com/niclasku/ph4-walkingpad)
|
|
164
|
+
|
|
165
|
+
## Known Behavior
|
|
166
|
+
|
|
167
|
+
### FTMS Cold Start
|
|
168
|
+
KingSmith FTMS devices require a `START_OR_RESUME` command before the belt will accept speed commands. After a cold start, there is a delay of several seconds before `SET_TARGET_SPEED` takes effect. The library handles this automatically with retry logic.
|
|
169
|
+
|
|
170
|
+
### BLE Connection Drops
|
|
171
|
+
KingSmith FTMS devices may drop the BLE connection shortly after a cold start, especially at weaker signal strength. The library stores the pending target speed and can re-apply it after reconnection via the status callback mechanism.
|
|
172
|
+
|
|
173
|
+
### Connection Exclusivity
|
|
174
|
+
Only one BLE client can connect to the treadmill at a time. If Home Assistant holds the connection, the KS Fit app cannot connect, and vice versa.
|
|
175
|
+
|
|
176
|
+
## Requirements
|
|
177
|
+
|
|
178
|
+
- Python 3.10+
|
|
179
|
+
- [bleak](https://github.com/hbldh/bleak) >= 0.20.0
|
|
180
|
+
- [ph4-walkingpad](https://github.com/niclasku/ph4-walkingpad) >= 1.0.0 (optional, for WiLink devices)
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# walkingpad-controller
|
|
2
|
+
|
|
3
|
+
Python library for controlling KingSmith WalkingPad treadmills over Bluetooth Low Energy (BLE).
|
|
4
|
+
|
|
5
|
+
Supports both **FTMS** (Fitness Machine Service) and legacy **WiLink** protocols behind a unified API. Protocol is auto-detected based on the BLE device name and advertised services.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Unified API** — single `WalkingPadController` class for all device types
|
|
10
|
+
- **Auto protocol detection** — FTMS for newer KS-HD-* devices, WiLink for older models
|
|
11
|
+
- **Real-time status** — speed, distance, duration, calories, steps via BLE notifications
|
|
12
|
+
- **Cold-start handling** — retry logic for KingSmith FTMS devices that need START_OR_RESUME before accepting speed commands
|
|
13
|
+
- **Reconnect recovery** — pending target speed is automatically re-applied after BLE reconnection
|
|
14
|
+
- **KingSmith extensions** — step counter via proprietary FTMS extension (bit 13)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install walkingpad-controller
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For legacy WiLink device support (older WalkingPad models):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install walkingpad-controller[wilink]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import asyncio
|
|
32
|
+
from bleak import BleakScanner
|
|
33
|
+
from walkingpad_controller import WalkingPadController
|
|
34
|
+
|
|
35
|
+
async def main():
|
|
36
|
+
# Find your treadmill
|
|
37
|
+
device = await BleakScanner.find_device_by_name("KS-HD-Z1D")
|
|
38
|
+
|
|
39
|
+
# Create controller (protocol auto-detected from BLE name)
|
|
40
|
+
controller = WalkingPadController(ble_device=device)
|
|
41
|
+
|
|
42
|
+
# Connect
|
|
43
|
+
await controller.connect()
|
|
44
|
+
print(f"Protocol: {controller.protocol.value}")
|
|
45
|
+
print(f"Speed range: {controller.min_speed}-{controller.max_speed} km/h")
|
|
46
|
+
|
|
47
|
+
# Start at 3.0 km/h
|
|
48
|
+
await controller.start(target_speed=3.0)
|
|
49
|
+
|
|
50
|
+
# Read status
|
|
51
|
+
print(f"Speed: {controller.status.speed} km/h")
|
|
52
|
+
print(f"Steps: {controller.status.steps}")
|
|
53
|
+
|
|
54
|
+
# Stop and disconnect
|
|
55
|
+
await controller.stop()
|
|
56
|
+
await controller.disconnect()
|
|
57
|
+
|
|
58
|
+
asyncio.run(main())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Status Callbacks
|
|
62
|
+
|
|
63
|
+
Register callbacks to receive real-time status updates:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from walkingpad_controller import WalkingPadController, TreadmillStatus
|
|
67
|
+
|
|
68
|
+
def on_status(status: TreadmillStatus):
|
|
69
|
+
print(f"Speed: {status.speed} km/h, Distance: {status.distance}m, "
|
|
70
|
+
f"Duration: {status.duration}s, Calories: {status.calories}, "
|
|
71
|
+
f"Steps: {status.steps}")
|
|
72
|
+
|
|
73
|
+
controller = WalkingPadController(ble_device=device)
|
|
74
|
+
controller.register_status_callback(on_status)
|
|
75
|
+
controller.register_disconnect_callback(lambda: print("Disconnected!"))
|
|
76
|
+
await controller.connect()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API Reference
|
|
80
|
+
|
|
81
|
+
### WalkingPadController
|
|
82
|
+
|
|
83
|
+
The main entry point. Auto-detects protocol and delegates to the appropriate backend.
|
|
84
|
+
|
|
85
|
+
| Property / Method | Description |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `protocol` | Detected protocol (`ProtocolType.FTMS` or `ProtocolType.WILINK`) |
|
|
88
|
+
| `connected` | Whether the device is currently connected |
|
|
89
|
+
| `status` | Current `TreadmillStatus` |
|
|
90
|
+
| `min_speed` / `max_speed` | Speed range in km/h (read from device for FTMS) |
|
|
91
|
+
| `speed_increment` | Speed step size in km/h |
|
|
92
|
+
| `connect()` | Connect and auto-detect protocol |
|
|
93
|
+
| `disconnect()` | Disconnect from the device |
|
|
94
|
+
| `start(target_speed=None)` | Start the belt, optionally at a target speed |
|
|
95
|
+
| `stop()` | Stop the belt |
|
|
96
|
+
| `set_speed(speed_kmh)` | Set speed (starts belt if stopped) |
|
|
97
|
+
| `switch_mode(mode)` | Switch operating mode (WiLink: auto/manual/standby) |
|
|
98
|
+
| `register_status_callback(cb)` | Register a `TreadmillStatus` callback |
|
|
99
|
+
| `register_disconnect_callback(cb)` | Register a disconnect callback |
|
|
100
|
+
| `update_ble_device(device)` | Update BLE device reference after rediscovery |
|
|
101
|
+
|
|
102
|
+
### TreadmillStatus
|
|
103
|
+
|
|
104
|
+
Dataclass with real-time treadmill data:
|
|
105
|
+
|
|
106
|
+
| Field | Type | Description |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `belt_state` | `int` | 0=stopped, 1=active, 5=standby, 9=starting |
|
|
109
|
+
| `speed` | `float` | Current speed in km/h |
|
|
110
|
+
| `mode` | `int` | Operating mode (0=auto, 1=manual, 2=standby) |
|
|
111
|
+
| `distance` | `int` | Total distance in meters |
|
|
112
|
+
| `duration` | `int` | Elapsed time in seconds |
|
|
113
|
+
| `steps` | `int` | Step count (FTMS KingSmith extension) |
|
|
114
|
+
| `calories` | `int` | Total energy in kcal |
|
|
115
|
+
| `calories_per_hour` | `int` | Energy rate |
|
|
116
|
+
| `heart_rate` | `int` | Heart rate in bpm (if available) |
|
|
117
|
+
| `timestamp` | `float` | Unix timestamp of last update |
|
|
118
|
+
|
|
119
|
+
### Protocol-Specific Controllers
|
|
120
|
+
|
|
121
|
+
For advanced use, you can use the protocol controllers directly:
|
|
122
|
+
|
|
123
|
+
- **`FTMSController`** — FTMS protocol (newer KS-HD-* devices)
|
|
124
|
+
- **`WiLinkController`** — Legacy WiLink protocol (older WalkingPad models, requires `[wilink]` extra)
|
|
125
|
+
|
|
126
|
+
## Supported Devices
|
|
127
|
+
|
|
128
|
+
### FTMS Protocol (tested)
|
|
129
|
+
- KingSmith KS-Z1D (BLE name: `KS-HD-Z1D`)
|
|
130
|
+
- Other KingSmith devices with BLE names starting with `KS-HD-`
|
|
131
|
+
|
|
132
|
+
### WiLink Protocol (via ph4-walkingpad)
|
|
133
|
+
- WalkingPad A1, A1 Pro
|
|
134
|
+
- WalkingPad C1, C2
|
|
135
|
+
- Other models supported by [ph4-walkingpad](https://github.com/niclasku/ph4-walkingpad)
|
|
136
|
+
|
|
137
|
+
## Known Behavior
|
|
138
|
+
|
|
139
|
+
### FTMS Cold Start
|
|
140
|
+
KingSmith FTMS devices require a `START_OR_RESUME` command before the belt will accept speed commands. After a cold start, there is a delay of several seconds before `SET_TARGET_SPEED` takes effect. The library handles this automatically with retry logic.
|
|
141
|
+
|
|
142
|
+
### BLE Connection Drops
|
|
143
|
+
KingSmith FTMS devices may drop the BLE connection shortly after a cold start, especially at weaker signal strength. The library stores the pending target speed and can re-apply it after reconnection via the status callback mechanism.
|
|
144
|
+
|
|
145
|
+
### Connection Exclusivity
|
|
146
|
+
Only one BLE client can connect to the treadmill at a time. If Home Assistant holds the connection, the KS Fit app cannot connect, and vice versa.
|
|
147
|
+
|
|
148
|
+
## Requirements
|
|
149
|
+
|
|
150
|
+
- Python 3.10+
|
|
151
|
+
- [bleak](https://github.com/hbldh/bleak) >= 0.20.0
|
|
152
|
+
- [ph4-walkingpad](https://github.com/niclasku/ph4-walkingpad) >= 1.0.0 (optional, for WiLink devices)
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
MIT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "walkingpad-controller"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python library for controlling KingSmith WalkingPad treadmills over BLE (FTMS and legacy WiLink protocols)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "mcdax" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["walkingpad", "kingsmith", "treadmill", "bluetooth", "ble", "ftms"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Home Automation",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"bleak>=0.20.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
wilink = [
|
|
33
|
+
"ph4-walkingpad>=1.0.0",
|
|
34
|
+
]
|
|
35
|
+
all = [
|
|
36
|
+
"walkingpad-controller[wilink]",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/mcdax/walkingpad-controller"
|
|
41
|
+
Repository = "https://github.com/mcdax/walkingpad-controller"
|
|
42
|
+
Issues = "https://github.com/mcdax/walkingpad-controller/issues"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/walkingpad_controller"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""walkingpad-controller — Python library for controlling WalkingPad treadmills over BLE.
|
|
2
|
+
|
|
3
|
+
Supports both FTMS (Fitness Machine Service) and legacy WiLink protocols.
|
|
4
|
+
Protocol is auto-detected based on the BLE device name and services.
|
|
5
|
+
|
|
6
|
+
Quick start:
|
|
7
|
+
|
|
8
|
+
from bleak import BleakScanner
|
|
9
|
+
from walkingpad_controller import WalkingPadController
|
|
10
|
+
|
|
11
|
+
device = await BleakScanner.find_device_by_name("KS-HD-Z1D")
|
|
12
|
+
controller = WalkingPadController(ble_device=device)
|
|
13
|
+
await controller.connect()
|
|
14
|
+
await controller.start(target_speed=3.0)
|
|
15
|
+
print(controller.status)
|
|
16
|
+
await controller.stop()
|
|
17
|
+
await controller.disconnect()
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from .const import (
|
|
21
|
+
FTMS_NAME_PREFIXES,
|
|
22
|
+
FTMS_SERVICE_UUID,
|
|
23
|
+
WILINK_SERVICE_UUID,
|
|
24
|
+
BeltState,
|
|
25
|
+
FTMSOpcode,
|
|
26
|
+
FTMSResultCode,
|
|
27
|
+
OperatingMode,
|
|
28
|
+
ProtocolType,
|
|
29
|
+
)
|
|
30
|
+
from .controller import WalkingPadController
|
|
31
|
+
from .ftms import FTMSController
|
|
32
|
+
from .models import DeviceCapabilities, SpeedRange, TreadmillStatus
|
|
33
|
+
from .wilink import WiLinkController
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
# Main controller
|
|
39
|
+
"WalkingPadController",
|
|
40
|
+
# Protocol-specific controllers
|
|
41
|
+
"FTMSController",
|
|
42
|
+
"WiLinkController",
|
|
43
|
+
# Data models
|
|
44
|
+
"TreadmillStatus",
|
|
45
|
+
"SpeedRange",
|
|
46
|
+
"DeviceCapabilities",
|
|
47
|
+
# Enums
|
|
48
|
+
"BeltState",
|
|
49
|
+
"OperatingMode",
|
|
50
|
+
"ProtocolType",
|
|
51
|
+
"FTMSOpcode",
|
|
52
|
+
"FTMSResultCode",
|
|
53
|
+
# Constants
|
|
54
|
+
"FTMS_SERVICE_UUID",
|
|
55
|
+
"WILINK_SERVICE_UUID",
|
|
56
|
+
"FTMS_NAME_PREFIXES",
|
|
57
|
+
]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Constants, enums, and BLE UUIDs for WalkingPad treadmill control."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum, IntEnum, unique
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# --- BLE UUIDs ---
|
|
7
|
+
|
|
8
|
+
# Standard FTMS Service
|
|
9
|
+
FTMS_SERVICE_UUID = "00001826-0000-1000-8000-00805f9b34fb"
|
|
10
|
+
|
|
11
|
+
# FTMS Characteristics
|
|
12
|
+
FTMS_FEATURE_UUID = "00002acc-0000-1000-8000-00805f9b34fb"
|
|
13
|
+
TREADMILL_DATA_UUID = "00002acd-0000-1000-8000-00805f9b34fb"
|
|
14
|
+
TRAINING_STATUS_UUID = "00002ad3-0000-1000-8000-00805f9b34fb"
|
|
15
|
+
SUPPORTED_SPEED_RANGE_UUID = "00002ad4-0000-1000-8000-00805f9b34fb"
|
|
16
|
+
SUPPORTED_INCLINATION_RANGE_UUID = "00002ad5-0000-1000-8000-00805f9b34fb"
|
|
17
|
+
FITNESS_MACHINE_STATUS_UUID = "00002ada-0000-1000-8000-00805f9b34fb"
|
|
18
|
+
FTMS_CONTROL_POINT_UUID = "00002ad9-0000-1000-8000-00805f9b34fb"
|
|
19
|
+
|
|
20
|
+
# Custom KingSmith Supplement Service
|
|
21
|
+
SUPPLEMENT_SERVICE_UUID = "24e2521c-f63b-48ed-85be-c5330a00fdf7"
|
|
22
|
+
SUPPLEMENT_NOTIFY_UUID = "24e2521c-f63b-48ed-85be-c5330b00fdf7"
|
|
23
|
+
SUPPLEMENT_WRITE_UUID = "24e2521c-f63b-48ed-85be-c5330d00fdf7"
|
|
24
|
+
|
|
25
|
+
# Legacy WiLink Service (for older devices)
|
|
26
|
+
WILINK_SERVICE_UUID = "0000fe00-0000-1000-8000-00805f9b34fb"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# --- Enums ---
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@unique
|
|
33
|
+
class ProtocolType(Enum):
|
|
34
|
+
"""Supported BLE communication protocols."""
|
|
35
|
+
|
|
36
|
+
WILINK = "wilink" # Legacy protocol (service 0xFE00, ph4-walkingpad)
|
|
37
|
+
FTMS = "ftms" # Standard FTMS (service 0x1826)
|
|
38
|
+
UNKNOWN = "unknown"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@unique
|
|
42
|
+
class BeltState(IntEnum):
|
|
43
|
+
"""Belt states."""
|
|
44
|
+
|
|
45
|
+
STOPPED = 0
|
|
46
|
+
ACTIVE = 1
|
|
47
|
+
STANDBY = 5
|
|
48
|
+
STARTING = 9
|
|
49
|
+
UNKNOWN = 1000
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@unique
|
|
53
|
+
class OperatingMode(Enum):
|
|
54
|
+
"""Treadmill operating modes."""
|
|
55
|
+
|
|
56
|
+
AUTO = 0 # Belt starts/stops on foot detection
|
|
57
|
+
MANUAL = 1 # Speed set via commands
|
|
58
|
+
STANDBY = 2 # Belt off
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# --- FTMS Protocol Constants ---
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@unique
|
|
65
|
+
class FTMSOpcode(IntEnum):
|
|
66
|
+
"""FTMS Control Point opcodes (Bluetooth SIG standard)."""
|
|
67
|
+
|
|
68
|
+
REQUEST_CONTROL = 0x00
|
|
69
|
+
RESET = 0x01
|
|
70
|
+
SET_TARGET_SPEED = 0x02 # param: UINT16 in 0.01 km/h
|
|
71
|
+
SET_TARGET_INCLINATION = 0x03 # param: INT16 in 0.1%
|
|
72
|
+
START_OR_RESUME = 0x07
|
|
73
|
+
STOP_OR_PAUSE = 0x08 # param: UINT8 (0x01=Stop, 0x02=Pause)
|
|
74
|
+
RESPONSE_CODE = 0x80
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@unique
|
|
78
|
+
class FTMSResultCode(IntEnum):
|
|
79
|
+
"""FTMS Control Point result codes."""
|
|
80
|
+
|
|
81
|
+
SUCCESS = 0x01
|
|
82
|
+
OPCODE_NOT_SUPPORTED = 0x02
|
|
83
|
+
INVALID_PARAMETER = 0x03
|
|
84
|
+
OPERATION_FAILED = 0x04
|
|
85
|
+
CONTROL_NOT_PERMITTED = 0x05
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@unique
|
|
89
|
+
class FTMSStopPauseParam(IntEnum):
|
|
90
|
+
"""Parameter for the Stop or Pause opcode."""
|
|
91
|
+
|
|
92
|
+
STOP = 0x01
|
|
93
|
+
PAUSE = 0x02
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TreadmillDataFlags:
|
|
97
|
+
"""Bit flags for the FTMS Treadmill Data characteristic (0x2ACD).
|
|
98
|
+
|
|
99
|
+
Per the Bluetooth FTMS specification, the flags field is a 16-bit value.
|
|
100
|
+
If a bit is SET, the corresponding optional field is PRESENT in the data.
|
|
101
|
+
Instantaneous Speed is always present (mandatory field).
|
|
102
|
+
|
|
103
|
+
Bit 13 (0x2000) is a KingSmith-specific extension that carries 3 extra
|
|
104
|
+
bytes: a uint16 LE step count + 1 zero byte. The step counter is
|
|
105
|
+
pressure-sensor based -- it only increments when someone is walking.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
MORE_DATA = 0x0001
|
|
109
|
+
AVERAGE_SPEED = 0x0002
|
|
110
|
+
TOTAL_DISTANCE = 0x0004
|
|
111
|
+
INCLINATION = 0x0008
|
|
112
|
+
ELEVATION_GAIN = 0x0010
|
|
113
|
+
INSTANTANEOUS_PACE = 0x0020
|
|
114
|
+
AVERAGE_PACE = 0x0040
|
|
115
|
+
EXPENDED_ENERGY = 0x0080
|
|
116
|
+
HEART_RATE = 0x0100
|
|
117
|
+
METABOLIC_EQUIVALENT = 0x0200
|
|
118
|
+
ELAPSED_TIME = 0x0400
|
|
119
|
+
REMAINING_TIME = 0x0800
|
|
120
|
+
FORCE_ON_BELT = 0x1000
|
|
121
|
+
KINGSMITH_EXTENSION = 0x2000
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# BLE name prefixes known to use FTMS protocol.
|
|
125
|
+
# These devices have service 0x1826 but NOT 0xFE00.
|
|
126
|
+
FTMS_NAME_PREFIXES = ("KS-HD-",)
|
|
127
|
+
|
|
128
|
+
# Default connection parameters
|
|
129
|
+
MAX_CONNECT_RETRIES = 3
|
|
130
|
+
RETRY_DELAY_SECONDS = 2.0
|