Skip to content

Commit 6d6a081

Browse files
committed
refactor: modularize pyspacemouse core, introduce new API, and update examples and documentation.
1 parent 9f403df commit 6d6a081

34 files changed

+3090
-1562
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ name: CI
22

33
on:
44
push:
5-
branches:
6-
- master
7-
- main
85
pull_request:
96
branches:
107
- master

.github/workflows/deploy-web.yaml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@ on:
44
branches:
55
- master
66
- main
7+
workflow_dispatch:
8+
79
permissions:
810
contents: write
11+
912
jobs:
1013
deploy:
1114
runs-on: ubuntu-latest
1215
steps:
13-
- uses: actions/checkout@v3
14-
- uses: actions/setup-python@v4
16+
- uses: actions/checkout@v4
1517
with:
16-
python-version: 3.x
17-
- uses: actions/cache@v2
18+
fetch-depth: 0 # Required for git-revision-date-localized-plugin
19+
- uses: actions/setup-python@v5
1820
with:
19-
key: ${{ github.ref }}
21+
python-version: "3.12"
22+
- uses: actions/cache@v4
23+
with:
24+
key: mkdocs-${{ github.ref }}
2025
path: .cache
21-
- run: pip install -e ".[develop]"
22-
- run: pip install mkdoxy
23-
- run: sudo apt-get install doxygen
24-
- run: mkdocs gh-deploy --force
26+
- name: Install dependencies
27+
run: |
28+
pip install -e ".[docs]"
29+
sudo apt-get install -y doxygen
30+
- name: Deploy docs
31+
run: make docs-deploy

CONTRIBUTING.md

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,143 @@
66
3. Create a pull request to `master` and assign a reviewer.
77
4. The reviewer will review your code and merge it into `master`.
88

9+
## Adding new device
10+
11+
This section describes how to add support for a new SpaceMouse or similar 6-DOF device.
12+
13+
### 1. Find Device IDs
14+
15+
First, identify your device's **Vendor ID (VID)** and **Product ID (PID)**.
16+
17+
**Linux/macOS:**
18+
```bash
19+
lsusb
20+
# Example output: Bus 001 Device 013: ID 256f:c652 3Dconnexion Universal Receiver
21+
# VID = 256f, PID = c652
22+
```
23+
24+
**Using [hidapitester](https://github.com/todbot/hidapitester):**
25+
```bash
26+
./hidapitester --list
27+
# Example: 046D/C626: 3Dconnexion - SpaceNavigator
28+
```
29+
30+
### 2. Analyze HID Data
31+
32+
Use `hidapitester` to read raw data from your device:
33+
34+
```bash
35+
./hidapitester --vidpid <VID/PID> --open --read-input
36+
```
37+
38+
Move the SpaceMouse knob in each direction and identify which bytes change:
39+
40+
- **Channel 1**: Usually translation data (X, Y, Z)
41+
- **Channel 2**: Usually rotation data (pitch, roll, yaw)
42+
- **Channel 3**: Button data
43+
44+
Each axis is typically a signed 16-bit value (2 bytes, little-endian).
45+
46+
### 3. Add Device to `devices.toml`
47+
48+
Edit `pyspacemouse/devices.toml` and add your device:
49+
50+
```toml
51+
[YourDeviceName]
52+
hid_id = [0xVID, 0xPID] # Vendor ID and Product ID in hex
53+
led_id = [0x04, 0x01] # Optional: [report_id, on_value] for LED control
54+
axis_scale = 350.0 # Scaling factor (adjust based on device sensitivity)
55+
56+
# Axis mappings: [channel, byte1, byte2, scale]
57+
# - channel: HID report number (1, 2, or 3)
58+
# - byte1, byte2: Byte positions for the 16-bit value
59+
# - scale: 1 for normal, -1 for inverted axis
60+
mappings.x = [1, 1, 2, 1]
61+
mappings.y = [1, 3, 4, -1]
62+
mappings.z = [1, 5, 6, -1]
63+
mappings.pitch = [2, 1, 2, -1]
64+
mappings.roll = [2, 3, 4, -1]
65+
mappings.yaw = [2, 5, 6, 1]
66+
67+
# Button mappings: [channel, byte, bit]
68+
[YourDeviceName.buttons]
69+
LEFT = [3, 1, 0] # Channel 3, byte 1, bit 0
70+
RIGHT = [3, 1, 1] # Channel 3, byte 1, bit 1
71+
```
72+
73+
**Mapping format explained:**
74+
75+
| Field | Description |
76+
|-------|-------------|
77+
| `channel` | HID report ID (first byte of each HID message) |
78+
| `byte1`, `byte2` | Byte positions within the report (1-indexed) |
79+
| `scale` | Sign modifier: `1` = normal, `-1` = inverted |
80+
| `bit` | For buttons: which bit in the byte (0-7) |
81+
82+
### 4. Test Your Configuration
83+
84+
```python
85+
import pyspacemouse
86+
87+
# Check if device is recognized
88+
devices = pyspacemouse.get_connected_devices()
89+
print(devices) # Should show your device name
90+
91+
# Test reading
92+
with pyspacemouse.open() as device:
93+
while True:
94+
state = device.read()
95+
print(f"X:{state.x:.2f} Y:{state.y:.2f} Z:{state.z:.2f}")
96+
print(f"Roll:{state.roll:.2f} Pitch:{state.pitch:.2f} Yaw:{state.yaw:.2f}")
97+
print(f"Buttons: {state.buttons}")
98+
```
99+
100+
### 5. Fine-tune Axis Directions
101+
102+
If an axis moves in the wrong direction, change its `scale` from `1` to `-1` or vice versa in `devices.toml`.
103+
104+
### 6. Submit a Pull Request
105+
106+
Once your device works correctly:
107+
108+
1. Create a branch: `git checkout -b add-device-<name>`
109+
2. Add your device entry to `devices.toml`
110+
3. Test thoroughly with the examples in `examples/`
111+
4. Submit a PR with your device name and any notes about testing
112+
113+
### Using Custom Configuration (Without Modifying Library)
114+
115+
If you don't want to modify the library, use `create_device_info()`:
116+
117+
```python
118+
import pyspacemouse
119+
120+
device_spec = pyspacemouse.create_device_info({
121+
"name": "MyCustomDevice",
122+
"hid_id": [0x256F, 0xC652],
123+
"axis_scale": 350.0,
124+
"mappings": {
125+
"x": [1, 1, 2, 1],
126+
"y": [1, 3, 4, -1],
127+
"z": [1, 5, 6, -1],
128+
"pitch": [2, 1, 2, -1],
129+
"roll": [2, 3, 4, -1],
130+
"yaw": [2, 5, 6, 1],
131+
},
132+
"buttons": {
133+
"LEFT": [3, 1, 0],
134+
"RIGHT": [3, 1, 1],
135+
},
136+
})
137+
138+
with pyspacemouse.open(device_spec=device_spec) as device:
139+
state = device.read()
140+
```
141+
142+
See [Custom Device Configuration](https://spacemouse.kubaandrysek.cz/mouseApi/#custom-device-configuration) for more details.
9143

10144
## How to write documentation
11-
To install the required dependencies, run `pip install pyspacemouse[develop]`.
145+
To install the required dependencies, run `pip install pyspacemouse[docs]`.
12146

13147
Edit `README.md` only in the root folder. The documentation is automatically generated from `README.md` and `docs/` folder.
14148
To update documentation from root to `/docs` use macro `make fixRelativeLinkDocs` which will replace all relative links from `/` to `/docs` folder.

Makefile

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,59 @@
1-
.PHONY: package release test lint format
1+
.PHONY: package release test lint format build clean version publish
22

33
all: package
44

55
# Packaging (using Hatch)
6-
package:
7-
rm -f dist/*
6+
build:
87
hatch build
98

10-
install: package
9+
package: build
10+
11+
build-clean:
12+
hatch clean
13+
hatch build
14+
15+
install: build
1116
python3 -m pip install --no-deps --force dist/*.whl
1217

13-
release: package
18+
# Publishing (using Hatch)
19+
publish: build
20+
hatch publish
21+
22+
publish-test: build
23+
hatch publish --repo test
24+
25+
# Legacy twine commands (deprecated, use publish/publish-test)
26+
release: build
1427
twine upload --repository pypi dist/*
1528

16-
release-test: package
29+
release-test: build
1730
twine upload --repository testpypi dist/*
1831

32+
# Version management (using Hatch)
33+
version:
34+
hatch version
35+
36+
version-patch:
37+
hatch version patch
38+
39+
version-minor:
40+
hatch version minor
41+
42+
version-major:
43+
hatch version major
44+
45+
# Clean
1946
clean:
20-
rm -rf dist build pyspacemouse.egg-info
47+
hatch clean
48+
rm -rf dist build pyspacemouse.egg-info .ruff_cache
2149
find . -type d -name "__pycache__" -exec rm -rf {} +
2250
find . -type f -name "*.pyc" -delete
2351
find . -type f -name "*.pyo" -delete
2452
rm -f .coverage coverage.xml
2553

2654
# Development
2755
install-dev:
56+
hatch env create
2857
python3 -m pip install --editable ".[dev]"
2958

3059
# Code Quality

0 commit comments

Comments
 (0)