Examples
01. Basic
- File: 01_basic.py
- Summary: Basic example: Read SpaceMouse input with context manager.
- Run:
python examples/01_basic.py
examples/01_basic.py
"""Basic example: Read SpaceMouse input with context manager.
This is the simplest way to use PySpaceMouse. The context manager
ensures the device is properly closed when you're done.
"""
import pyspacemouse
from pyspacemouse import AxisConvention
# Using context manager (recommended)
with pyspacemouse.open(axis_convention=AxisConvention.HID_Z_UP) as device:
print(f"Connected to: {device.name}")
print("Move the SpaceMouse to see values (Ctrl+C to exit)")
while True:
state = device.read()
if state.has_motion():
print(
f"x={state.x:+.2f} y={state.y:+.2f} z={state.z:+.2f} "
f"roll={state.roll:+.2f} pitch={state.pitch:+.2f} yaw={state.yaw:+.2f}"
)
02. Callbacks
- File: 02_callbacks.py
- Summary: Callbacks example: React to button presses and axis movements.
- Run:
python examples/02_callbacks.py
examples/02_callbacks.py
"""Callbacks example: React to button presses and axis movements.
This example shows how to use callbacks for:
- Button presses (individual or combinations)
- Axis movements with filtering
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
# Button callbacks receive (state, buttons, pressed_buttons)
def on_button_left(state, buttons, pressed):
print("LEFT button pressed!")
def on_button_right(state, buttons, pressed):
print("RIGHT button pressed!")
def on_both_buttons(state, buttons, pressed):
print("BOTH buttons pressed together!")
# General button callback for any button change
def on_any_button(state, buttons):
active = [i for i, b in enumerate(buttons) if b]
if active:
print(f"Buttons active: {active}")
# Configure button callbacks
button_callbacks = [
pyspacemouse.ButtonCallback(0, on_button_left),
pyspacemouse.ButtonCallback(1, on_button_right),
pyspacemouse.ButtonCallback([0, 1], on_both_buttons), # Both at once
]
# Open with callbacks
with pyspacemouse.open(
axis_convention=AxisConvention.HID_Z_UP,
dof_callback=pyspacemouse.print_state, # Built-in DOF printer
button_callback=on_any_button,
button_callbacks=button_callbacks,
) as device:
print(f"Connected to: {device.name}")
print("Move the SpaceMouse or press buttons (Ctrl+C to exit)")
print()
while True:
device.read() # Must call read() to process callbacks
time.sleep(0.01)
03. Multi Device
- File: 03_multi_device.py
- Summary: Multi-device example: Connect to multiple SpaceMouse devices.
- Run:
python examples/03_multi_device.py
examples/03_multi_device.py
"""Multi-device example: Connect to multiple SpaceMouse devices.
This example shows how to open two SpaceMouse devices simultaneously,
useful for dual-hand control or controlling multiple robots.
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
def main():
# First, discover connected devices
connected = pyspacemouse.get_connected_devices_by_path()
print(f"Found {len(connected)} spacemouse device(s): {list(connected.values())}")
if len(connected) < 2:
print("This example requires 2 SpaceMouse devices connected.")
print("Tip: Use a 3Dconnexion Universal Receiver with device_index parameter")
return
# Arbitrarily take the first two devices found
path0 = list(connected.keys())[0]
path1 = list(connected.keys())[1]
# Open two devices by path
with pyspacemouse.open_by_path(path0, axis_convention=AxisConvention.HID_Z_UP) as left_hand:
with pyspacemouse.open_by_path(
path1, axis_convention=AxisConvention.HID_Z_UP
) as right_hand:
print(f"Left hand: {left_hand.name}")
print(f"Right hand: {right_hand.name}")
print()
print("Move both devices (Ctrl+C to exit)")
while True:
left = left_hand.read()
right = right_hand.read()
if left.has_motion() or right.has_motion():
print(
f"Left: x={left.x:+.2f} y={left.y:+.2f} z={left.z:+.2f} | "
f"Right: x={right.x:+.2f} y={right.y:+.2f} z={right.z:+.2f}"
)
time.sleep(0.01)
if __name__ == "__main__":
main()
04. Open By Path
- File: 04_open_by_path.py
- Summary: Open by path example: Connect to a specific HID device by filesystem path.
- Run:
python examples/04_open_by_path.py
examples/04_open_by_path.py
"""Open by path example: Connect to a specific HID device by filesystem path.
This is useful when you need to ensure you always connect to the same
physical device, regardless of enumeration order.
Note: Device paths vary by OS:
- Linux: /dev/hidraw0, /dev/hidraw1, etc.
- macOS: /dev/hidraw0 or similar (may require special setup)
- Windows: Uses different path format
"""
import pyspacemouse
from pyspacemouse import AxisConvention
def main():
# First, list all HID devices to find the path you need
print("All HID devices:")
for product, manufacturer, vid, pid in pyspacemouse.get_all_hid_devices():
print(f" {product or 'Unknown'} by {manufacturer or 'Unknown'}")
print(f" VID: {vid:#06x}, PID: {pid:#06x}")
print()
# Example: Open by specific path (Linux example)
# Replace with actual path from your system
device_path = "/dev/hidraw0"
try:
with pyspacemouse.open_by_path(
device_path,
axis_convention=AxisConvention.HID_Z_UP,
) as device:
print(f"Connected to: {device.name} at {device_path}")
print("Move the device (Ctrl+C to exit)")
print()
while True:
state = device.read()
if state.has_motion():
print(
f"x={state.x:+.2f} y={state.y:+.2f} z={state.z:+.2f} "
f"r={state.roll:+.2f} p={state.pitch:+.2f} y={state.yaw:+.2f}"
)
except FileNotFoundError as e:
print(f"Device path not found: {e}")
print("Try running with a valid device path for your system.")
except ValueError as e:
print(f"Device not supported: {e}")
if __name__ == "__main__":
main()
05. Discovery
- File: 05_discovery.py
- Summary: Device discovery example: List and inspect available devices.
- Run:
python examples/05_discovery.py
examples/05_discovery.py
"""Device discovery example: List and inspect available devices.
This example shows how to discover what SpaceMouse devices are
connected and what device types are supported.
"""
import pyspacemouse
def main():
print("=" * 60)
print("PySpaceMouse Device Discovery")
print("=" * 60)
print()
# 1. List connected SpaceMouse devices
print("Connected SpaceMouse devices:")
connected = pyspacemouse.get_connected_devices_by_path()
if connected:
for name in connected.values():
print(f" ✓ {name}")
else:
print(" (none found)")
print()
# 2. List all supported device types
print("Supported device types:")
supported = pyspacemouse.get_supported_devices()
for supported_name, vid, pid in supported:
# Check if this device type is connected
status = " "
path_if_connected = ""
for path, name in connected.items():
if name == supported_name:
status = "✓"
path_if_connected = f" (path: {path})"
print(
f" [{status}] {supported_name} (VID: {vid:#06x}, PID: {pid:#06x}){path_if_connected}"
)
print()
# 3. List ALL HID devices (for debugging)
print("All HID devices on system:")
all_hid = pyspacemouse.get_all_hid_devices()
if all_hid:
for product, manufacturer, vid, pid in all_hid:
product = product or "Unknown"
manufacturer = manufacturer or "Unknown"
print(f" - {product} by {manufacturer}")
print(f" VID: {vid:#06x}, PID: {pid:#06x}")
else:
print(" (none found - is hidapi installed?)")
print()
# 4. Show device specs (advanced)
print("Device specifications loaded from TOML:")
specs = pyspacemouse.get_device_specs()
print(f" {len(specs)} device types configured")
print()
if __name__ == "__main__":
main()
06. Axis Callbacks
- File: 06_axis_callbacks.py
- Summary: Axis callbacks example: React to specific axis movements.
- Run:
python examples/06_axis_callbacks.py
examples/06_axis_callbacks.py
"""Axis callbacks example: React to specific axis movements.
This example shows DofCallback for per-axis handling with:
- Positive/negative direction callbacks
- Filtering (deadzone)
- Rate limiting (sleep)
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
def on_x_positive(state, value):
"""Called when X axis moves positive (right)."""
print(f"→ X positive: {value:+.2f}")
def on_x_negative(state, value):
"""Called when X axis moves negative (left)."""
print(f"← X negative: {value:+.2f}")
def on_z_move(state, value):
"""Called when Z axis moves (up/down)."""
direction = "↑ UP" if value > 0 else "↓ DOWN"
print(f"{direction}: {abs(value):.2f}")
# Configure per-axis callbacks
dof_callbacks = [
# X axis with separate callbacks for positive/negative
pyspacemouse.DofCallback(
axis="x",
callback=on_x_positive,
callback_minus=on_x_negative,
filter=0.1, # Deadzone: ignore values < 0.1
sleep=0.1, # Rate limit: max 10 calls/second
),
# Z axis with single callback for both directions
pyspacemouse.DofCallback(
axis="z",
callback=on_z_move,
filter=0.15,
sleep=0.05,
),
]
with pyspacemouse.open(
dof_callbacks=dof_callbacks,
axis_convention=AxisConvention.HID_Z_UP,
) as device:
print(f"Connected to: {device.name}")
print("Move X axis (left/right) or Z axis (up/down)")
print("Ctrl+C to exit")
print()
while True:
device.read()
time.sleep(0.01)
07. Led
- File: 07_led.py
- Summary: LED control example: Blink the SpaceMouse LED.
- Run:
python examples/07_led.py
examples/07_led.py
"""LED control example: Blink the SpaceMouse LED.
This example demonstrates how to control the LED on SpaceMouse devices.
The LED will toggle on and off every 0.5 seconds.
Note: Not all devices have controllable LEDs.
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
# Using context manager (recommended)
with pyspacemouse.open(axis_convention=AxisConvention.HID_Z_UP) as device:
print(f"Connected to: {device.name}")
print("LED will blink every 0.5 seconds (Ctrl+C to exit)")
print()
led_state = True
while True:
device.set_led(led_state)
led_state = not led_state
time.sleep(0.5)
08. Buttons
- File: 08_buttons.py
- Summary: Button names example: Show button names when pressed.
- Run:
python examples/08_buttons.py
examples/08_buttons.py
"""Button names example: Show button names when pressed.
This example demonstrates how to get the configured button names
from devices.toml when buttons are pressed.
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
def on_button_change(state, buttons):
"""Print names of all currently pressed buttons."""
# Find indices of pressed buttons
pressed_indices = [i for i, b in enumerate(buttons) if b]
if pressed_indices:
# Get button names from device
names = [device.get_button_name(i) for i in pressed_indices]
print(f"Pressed: {', '.join(names)}")
# Open device
with pyspacemouse.open(
button_callback=on_button_change,
axis_convention=AxisConvention.HID_Z_UP,
) as device:
print(f"Connected to: {device.name}")
print(f"Device has {len(device.info.button_names)} buttons:")
for i, name in enumerate(device.info.button_names):
print(f" [{i}] {name}")
print()
print("Press buttons to see their names (Ctrl+C to exit)")
print()
while True:
device.read()
time.sleep(0.01)
09. Invert Rotations
- File: 09_invert_rotations.py
- Summary: Example: Invert rotation axes
- Run:
python examples/09_invert_rotations.py
examples/09_invert_rotations.py
#!/usr/bin/env python3
"""Example: Inverting rotations
This example shows how to invert axes, in this case the rotation axes (roll, pitch, yaw).
Just for demonstration purposes, this would be pretty weird :)
"""
import time
import pyspacemouse
from pyspacemouse import AxisConvention
def example_invert_rotations():
"""Show how to invert rotation axes"""
print("\n" + "=" * 60)
print("Invert rotations")
print("=" * 60)
connected = pyspacemouse.get_connected_devices()
if not connected:
print("No devices connected!")
return
specs = pyspacemouse.get_device_specs()
base_spec_legacy = specs[connected[0]]
base_spec = pyspacemouse.apply_axis_convention(base_spec_legacy, AxisConvention.HID_Z_UP)
# Invert roll, pitch, and yaw
fixed_spec = pyspacemouse.modify_device_info(
base_spec,
name=f"{connected[0]} (Fixed Rotations)",
# Translation axes (x, y, z) can also be inverted here
invert_axes=["roll", "pitch", "yaw"],
)
with pyspacemouse.open(device_spec=fixed_spec) as device:
print(f"Connected to: {device.name}")
print("Rotations are now inverted!\n")
for _ in range(500):
state = device.read()
if any([state.roll, state.pitch, state.yaw]):
print(
f"x={state.x:+.2f} y={state.y:+.2f} z={state.z:+.2f} "
f"roll={state.roll:+.2f} pitch={state.pitch:+.2f} yaw={state.yaw:+.2f}"
)
time.sleep(0.01)
if __name__ == "__main__":
try:
example_invert_rotations()
except KeyboardInterrupt:
print("\nExiting...")
10. Custom Config Unity
- File: 10_custom_config_unity.py
- Summary: Example: Custom device configuration with axis remapping.
- Run:
python examples/10_custom_config_unity.py
examples/10_custom_config_unity.py
#!/usr/bin/env python3
"""Example: Custom device configuration
This example shows how to create entirely custom device configurations,
In this case for a "Spacemouse Wireless New", following the left-handed Unity convention (Z forward, X right, Y up).
If you have a totally custom HID device, you just need to know the byte layout of the device and you can create a custom configuration for it.
"""
import time
import pyspacemouse
def example_unity_convention():
"""Create entirely custom device configuration with the Unity convention."""
print("\n" + "=" * 60)
print("Create custom device spec (Unity convention)")
print("=" * 60)
# This shows how to create a completely custom device spec
# Useful for unsupported devices or complete remapping
custom_spec = pyspacemouse.create_device_info(
name="CustomSpaceMouse",
vendor_id=0x256F, # 3Dconnexion
product_id=0xC63A, # SpaceMouse Wireless New
mappings={
# Each mapping: (channel, byte1, byte2, scale)
# Scale: 1 = normal direction, -1 = inverted
"x": (1, 1, 2, 1),
"y": (1, 5, 6, -1),
"z": (1, 3, 4, -1),
"yaw": (1, 9, 10, 1),
"pitch": (1, 11, 12, 1),
"roll": (1, 7, 8, -1),
},
buttons={
"LEFT": (3, 1, 0),
"RIGHT": (3, 1, 1),
},
)
print(f"Created custom Unity spec: {custom_spec.name}")
print(f" VID/PID: {custom_spec.vendor_id:#06x}/{custom_spec.product_id:#06x}")
print(f" Axes: {list(custom_spec.mappings.keys())}")
print(f" Buttons: {custom_spec.button_names}")
with pyspacemouse.open(device_spec=custom_spec) as device:
print(f"Connected to: {device.name} with custom spec")
for _ in range(5000):
state = device.read()
if any([state.x, state.y, state.z, state.roll, state.pitch, state.yaw]):
print(
f"x={state.x:+.2f} y={state.y:+.2f} z={state.z:+.2f} "
f"roll={state.roll:+.2f} pitch={state.pitch:+.2f} yaw={state.yaw:+.2f}"
)
time.sleep(0.01)
if __name__ == "__main__":
try:
example_unity_convention()
except KeyboardInterrupt:
print("\nExiting...")