Metadata-Version: 2.5
Name: pyspacemouse
Version: 2.1.0
Summary: Multiplatform Python interface to the 3DConnexion Space Mouse - forked from pyspacenavigator
Project-URL: Documentation, https://spacemouse.kubaandrysek.cz
Project-URL: Issues, https://github.com/JakubAndrysek/pyspacemouse/issues
Project-URL: Source, https://github.com/JakubAndrysek/pyspacemouse
Author-email: Jakub Andrýsek <email@kubaandrysek.cz>, Peter Mitrano <mitranopeter@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: 3d,3d-mouse,3dconnection,6 DoF,HID,open-source,pyspacemouse,python,spacemouse,spacenavigator
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Hardware :: Universal Serial Bus (USB) :: Human Interface Device (HID)
Requires-Python: >=3.8
Requires-Dist: easyhid
Requires-Dist: tomli; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.12.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs; extra == 'docs'
Requires-Dist: mkdocs-git-revision-date-localized-plugin; extra == 'docs'
Requires-Dist: mkdocs-glightbox; extra == 'docs'
Requires-Dist: mkdocs-material; extra == 'docs'
Requires-Dist: mkdocs-open-in-new-tab; extra == 'docs'
Requires-Dist: mkdocs-redirects; extra == 'docs'
Requires-Dist: mkdoxy; extra == 'docs'
Description-Content-Type: text/markdown

# PySpaceMouse

🎮 Multiplatform Python library for 3Dconnexion SpaceMouse devices using raw HID.

3Dconnexion Space Mouse in Python using raw HID.
Note: you **don't** need to install or use any of the drivers or 3Dconnexion software to use this package.
It interfaces with the controller directly with `hidapi` and python wrapper library `easyhid`.

<p align="center">
<a href="https://hit.kubaandrysek.cz/?url=https%3A%2F%2Fgithub.com%2FJakubAndrysek%2Fpyspacemouse&chart=true"><img src="https://hit.kubaandrysek.cz/?url=https%3A%2F%2Fgithub.com%2FJakubAndrysek%2Fpyspacemouse"/></a>
</p>

<p align="center">
<img src="https://img.shields.io/github/license/JakubAndrysek/pyspacemouse?style=flat-square">
<img src="https://img.shields.io/github/stars/JakubAndrysek/pyspacemouse?style=flat-square">
<img src="https://img.shields.io/github/forks/JakubAndrysek/pyspacemouse?style=flat-square">
<img src="https://img.shields.io/github/issues/JakubAndrysek/pyspacemouse?style=flat-square">
<a href="https://www.pepy.tech/projects/pyspacemouse" target="_blank"><img src="https://static.pepy.tech/badge/pyspacemouse"></a>
</p>

[PySpaceMouse](https://github.com/JakubAndrysek/pyspacemouse) is forked from: [johnhw/pyspacenavigator](https://github.com/johnhw/pyspacenavigator) and modified to be multiplatform.

![](https://github.com/JakubAndrysek/pyspacemouse/raw/master/media/spacemouse-robot.jpg)

## Supported 3Dconnexion devices

* SpaceNavigator
* SpaceMouse Pro / Pro Wireless
* SpaceMouse Wireless
* SpaceMouse Compact
* SpaceMouse Enterprise
* SpacePilot / SpacePilot Pro
* 3Dconnexion Universal Receiver
* ...
* [Add more devices](https://spacemouse.kubaandrysek.cz/CONTRIBUTING/)

## Installation

```bash
pip install pyspacemouse
```

## Quick Start

Here we use `has_motion()` to check if any of the spatial axes have non-zero values.
The optional argument is the threshold used, and is applied per-axis.

```python
import pyspacemouse

# Context manager (recommended) - automatically closes device
with pyspacemouse.open() as device:
    while True:
        state = device.read()
        if state.has_motion(0.01):
            print(state.x, state.y, state.z, state.roll, state.pitch, state.yaw)
```

## API Reference

From version 2.0.0 the API has been modularized and changed.
Please look at the [API Reference](./docs/mouseApi/index.md) and [Examples](https://github.com/JakubAndrysek/PySpaceMouse/tree/master/examples) for more information.

### Device Discovery

```python
import pyspacemouse

# List connected SpaceMouse devices
pyspacemouse.get_connected_devices()
# Returns: ["SpaceNavigator", "SpaceMouse Pro", ...]

# List connected SpaceMouse devices with paths
pyspacemouse.get_connected_devices_by_path()
# Returns: {"/dev/hidraw0": "SpaceNavigator", ...}

# List all supported device types
pyspacemouse.get_supported_devices()
# Returns: [(name, vendor_id, product_id), ...]

# List ALL HID devices (for debugging)
pyspacemouse.get_all_hid_devices()
# Returns: [(product, manufacturer, vid, pid), ...]
```

### Opening Devices

```python
# Auto-detect and open first device
with pyspacemouse.open() as device:
    state = device.read()

# Open specific device by name
with pyspacemouse.open(device="SpaceNavigator") as device:
    state = device.read()

# Open second device when multiple identical devices are connected
with pyspacemouse.open(device_index=1) as device:
    state = device.read()

# Open by filesystem path (Linux: /dev/hidraw0)
with pyspacemouse.open_by_path("/dev/hidraw0") as device:
    state = device.read()
```

### Reading State

```python
with pyspacemouse.open() as device:
    state = device.read()

    # 6-DOF axes (range: -1.0 to 1.0)
    print(state.x, state.y, state.z)  # Translation
    print(state.roll, state.pitch, state.yaw)  # Rotation

    # Buttons (list of 0/1)
    print(state.buttons)

    # Timestamp
    print(state.t)
```

### Axis Conventions

For new code, opt into an axis convention by passing an enum value on device open:

```python
import pyspacemouse
from pyspacemouse import AxisConvention

with pyspacemouse.open(axis_convention=AxisConvention.HID_Z_UP) as device:
    state = device.read()
```

<p align="center">
  <img src="./docs/assets/hid_z_up_axes.svg" alt="HID Z-up axis convention" width="220">
  <img src="./docs/assets/hid_axes.svg" alt="Raw HID axis convention" width="220">
  <img src="./docs/assets/legacy_axis.svg" alt="Legacy PySpaceMouse axis convention" width="220">
</p>

See [Axis Conventions](./docs/mouseApi/index.md#axis-conventions) for the full
mapping table. Custom
`device_spec` mappings are used exactly as provided and cannot be combined with
`axis_convention`.

### Callbacks

```python
import pyspacemouse
import time


# Button callback
def on_button(state, buttons, pressed):
    print(f"Button {pressed} pressed!")


button_callbacks = [
    pyspacemouse.ButtonCallback(0, on_button),  # Button 0
    pyspacemouse.ButtonCallback([0, 1], on_button),  # Both 0 and 1
]

# DOF callback with filtering
dof_callbacks = [
    pyspacemouse.DofCallback(
        axis="x",
        callback=lambda s, v: print(f"X: {v}"),
        callback_minus=lambda s, v: print(f"X negative: {v}"),
        filter=0.1,  # Deadzone
        sleep=0.05,  # Rate limit
    ),
]

with pyspacemouse.open(
    button_callbacks=button_callbacks,
    dof_callbacks=dof_callbacks,
) as device:
    while True:
        device.read()  # Triggers callbacks
        time.sleep(0.001)  # NOTE: avoid larger sleeps, which can cause data to buffer
```

### Custom Axis Mapping

Choose a built-in convention for common application frames:

```python
import pyspacemouse
from pyspacemouse import AxisConvention

with pyspacemouse.open(axis_convention=AxisConvention.ROS) as device:
    state = device.read()
```

For custom coordinate frames, remap or invert axes explicitly:

```python
import pyspacemouse

# Get existing device spec and modify axes
specs = pyspacemouse.get_device_specs()
base = specs["SpaceNavigator"]

custom = pyspacemouse.modify_device_info(
    base,
    remap_axes={"x": "y", "y": ("x", -1), "yaw": ("yaw", -1)},
)

with pyspacemouse.open(device_spec=custom) as device:
    state = device.read()
```

See [Custom Device Configuration](./docs/mouseApi/index.md#custom-device-configuration) for full API.

## CLI

```bash
pyspacemouse --list-connected    # Show connected devices
pyspacemouse --list-supported    # Show all supported types
pyspacemouse --list-hid          # Show all HID devices
pyspacemouse --test              # Test connection
pyspacemouse --version           # Show version
```

## Examples

See the [examples/](https://github.com/JakubAndrysek/PySpaceMouse/tree/master/examples) directory:

| Example | Description |
|---------|-------------|
| `01_basic.py` | Simple reading with context manager |
| `02_callbacks.py` | Button and DOF callbacks |
| `03_multi_device.py` | Using two devices simultaneously |
| `04_open_by_path.py` | Open specific device by path |
| `05_discovery.py` | List and inspect devices |
| `06_axis_callbacks.py` | Per-axis callbacks with filtering |
| `07_led.py` | LED control |
| `08_buttons.py` | Button names and handling |
| `09_invert_rotations.py` | Invert rotations |
| `10_custom_config_unity.py` | A totally custom device config using the Unity axis convention |

## Dependencies

### hidapi (C library)

- **Linux**: `sudo apt-get install libhidapi-dev`
- **macOS**: `brew install hidapi`
- **Windows**: Download from [hidapi releases](https://github.com/libusb/hidapi/releases)

### Linux permissions

```bash
echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"' | sudo tee /etc/udev/rules.d/99-hidraw-permissions.rules
sudo usermod -aG plugdev $USER
newgrp plugdev
```

### macOS PATH

```bash
export DYLD_LIBRARY_PATH=/opt/homebrew/Cellar/hidapi/<VERSION>/lib:$DYLD_LIBRARY_PATH
```

## Troubleshooting

See [troubleshooting.md](./troubleshooting.md) for help with common issues.

## Developing / Contributing

This project includes a `Makefile` with commands for creating a virtual environment (using hatch), and publishing to pypi.

You will need `hatch` and `pre-commit` for this.
You can get these by using

```bash
# Most recently tested with hatch 1.17.0
pipx install hatch pre-commit
```

If you're not familiar with pipx, it lets you install python tools into isolated environments in `~/.local`.

For building the documentation locally, you will also need `doxygen` installed and on the path.

## Used In

- [TeleMoMa](https://github.com/UT-Austin-RobIn/telemoma) - A Modular and Versatile Teleoperation System for Mobile Manipulation
- [SERL](https://github.com/rail-berkeley/serl) - SERL: A Software Suite for Sample-Efficient Robotic Reinforcement Learning
    - ![](https://github.com/rail-berkeley/serl/raw/e59dc0d2721399af2e629d7bcad678fa2ffce9ae/docs/images/tasks-banner.gif)
- [Pancake Robot](https://github.com/pauldw/pancake-robot)- An integration of the Ufactory Lite 6 robot arm with kitchenware to make pancakes.
- [GELLO](https://github.com/wuphilipp/gello_software) - GELLO: A General, Low-Cost, and Intuitive Teleoperation Framework for Robot Manipulators
    - ![image](https://github.com/wuphilipp/gello_software/assets/33494544/229d90b5-c758-4c14-ab37-d4b2ed7ad50b)
- [spacepad](https://github.com/brianpeiris/spacepad) - A simple python script that turns a spacemouse device into a standard gamepad
- [arm_xarm](https://github.com/johnrso/arm_xarm)
