# Joy-Con Python Library
A Linux-based Python library for reading and decoding button information from Nintendo Joy-Con controllers using the evdev interface.
> **Note:** This library currently only supports Linux. Windows and macOS support would require implementing HID or other platform-specific backends.
## Features
- **Linux Support** - Works on Linux with the evdev interface
- **Evdev Backend** - Direct communication with Joy-Con controllers via Linux input subsystem
- **Real-time Input** - Read button states and analog sticks in real-time
- **Multiple Controllers** - Support for left, right, and Pro controllers
- **Dual Joy-Con Mode** - Use two Joy-Cons as a single controller
- **Event System** - Register callbacks for button press/release events
- **Polling Mode** - Continuous input monitoring at configurable rates
- **Clean API** - Simple, intuitive interface with Google-style docstrings
- **Low Latency** - Optimized for minimal input lag with device grabbing and efficient event reading
## Requirements
- Python 3.10 or higher
- Linux operating system
- Bluetooth-connected Joy-Con controllers
- Dependencies installed automatically:
- `evdev` (Linux event device interface)
## Installation
```bash
pip install joycon-lib
```
### Prerequisites - Bluetooth Setup
#### Linux Setup
Before using the library, you need to set up your Linux system:
**1. Enable the hid_nintendo kernel driver (REQUIRED):**
```bash
# Load the driver - this is required for Joy-Con support
sudo modprobe hid_nintendo
# To load automatically on boot (recommended):
echo "hid_nintendo" | sudo tee /etc/modules-load.d/hid_nintendo.conf
```
> **Note:** Without the `hid_nintendo` driver, Joy-Con controllers won't be recognized as input devices.
**2. Add udev rules for non-root access (RECOMMENDED):**
Without these rules, you'll need to run your Python scripts with `sudo`.
```bash
# Create udev rules file
sudo tee /etc/udev/rules.d/50-joycon.rules << 'EOF'
# Nintendo Joy-Con (L/R) and Pro Controller
KERNEL=="event*", ATTRS{name}=="*Joy-Con*", MODE="0666"
KERNEL=="event*", ATTRS{name}=="*Pro Controller*", MODE="0666"
EOF
# Reload and apply rules
sudo udevadm control --reload-rules
sudo udevadm trigger
```
**3. Connect Joy-Con via Bluetooth:**
```bash
# Open Bluetooth control
bluetoothctl
# In bluetoothctl:
power on
agent on
default-agent
scan on
# Press sync button on Joy-Con (small button between SL/SR)
# Wait for Joy-Con to appear, note the MAC address
pair XX:XX:XX:XX:XX:XX
trust XX:XX:XX:XX:XX:XX
connect XX:XX:XX:XX:XX:XX
# Exit bluetoothctl
exit
```
Or you can use the GUI to connect to the Joy-Con device via Bluetooth.
## Quick Start
### Basic Usage
```python
from joycon_lib import JoyCon, Button
import time
# Create and connect to Joy-Con
joycon = JoyCon(use_backend='evdev') # Linux only
joycon.connect() # Auto-finds first available Joy-Con
# Read button states
while True:
buttons = joycon.read_buttons()
if buttons.is_pressed(Button.A):
print("A button pressed!")
time.sleep(0.016) # ~60 FPS
# Disconnect when done
joycon.disconnect()
```
### Dual Joy-Con Mode
```python
from joycon_lib import DualJoyCon, Button
import time
# Create dual Joy-Con controller
dual = DualJoyCon(use_backend='evdev') # Linux only
dual.connect(auto_find=True) # Auto-finds and connects both Joy-Cons
# Start polling
dual.start_polling(rate=60)
# Read from both controllers as one
while True:
buttons = dual.get_button_state() # Combined buttons from both
left_stick, right_stick = dual.get_sticks() # Both analog sticks
if buttons.is_pressed(Button.A):
print("A pressed!")
if buttons.is_pressed(Button.L) and buttons.is_pressed(Button.R):
print("L+R combo!")
time.sleep(0.016)
dual.disconnect()
```
## Running Examples
### Quick Test
```bash
# Run the library's built-in test
python -m joycon_lib
```
### Example Scripts
The `examples/` directory contains various demonstration scripts:
| Script | Description |
|--------|-------------|
| `example_stick.py` | Test analog stick input |
| `example_dual.py` | Use both Joy-Cons independently |
| `example_evdev.py` | Test evdev backend (Linux only) |
```bash
cd examples/
python example_dual.py # Start with the interactive demo
```
## API Reference
### Core Classes
#### `JoyCon`
Main interface for single Joy-Con interaction.
**Methods:**
- `connect(device_path=None, device_type=None)` - Connect to a Joy-Con (evdev backend)
- `disconnect()` - Disconnect from the Joy-Con
- `read_buttons()` - Read current button state from device
- `get_button_state()` - Get current button state without reading
- `get_left_stick()` - Get left analog stick state
- `get_right_stick()` - Get right analog stick state
- `start_polling(rate=60)` - Start continuous polling
- `stop_polling()` - Stop polling
- `register_callback(event_type, callback)` - Register event callback
- `list_devices()` - List all available Joy-Con devices
- `get_device_info()` - Get information about connected device
#### `DualJoyCon`
Manages two Joy-Cons as a single controller.
**Methods:**
- `connect(auto_find=True, left_path=None, right_path=None)` - Connect to both Joy-Cons
- `disconnect()` - Disconnect both controllers
- `read_input()` - Read input from both Joy-Cons and combine
- `get_button_state()` - Get combined button state without reading
- `get_left_stick()` - Get left analog stick position
- `get_right_stick()` - Get right analog stick position
- `get_sticks()` - Get both analog stick positions as tuple
- `start_polling(rate=60)` - Start polling both controllers
- `stop_polling()` - Stop polling
- `is_connected()` - Check connection status of both Joy-Cons
#### `ButtonState`
Represents the current state of all buttons.
**Methods:**
- `is_pressed(button)` - Check if button is currently pressed
- `is_released(button)` - Check if button is currently released
- `just_pressed(button)` - Check if button was just pressed this frame
- `just_released(button)` - Check if button was just released this frame
- `get_pressed_names()` - Get list of pressed button names
- `to_dict()` - Convert button state to dictionary
### Button Mappings
| Controller | Buttons |
|------------|---------|
| **Right Joy-Con** | `Y`, `X`, `B`, `A`, `SR_RIGHT`, `SL_RIGHT`, `R`, `ZR`, `PLUS`, `R_STICK`, `HOME` |
| **Left Joy-Con** | `UP`, `DOWN`, `LEFT`, `RIGHT`, `SR_LEFT`, `SL_LEFT`, `L`, `ZL`, `MINUS`, `L_STICK`, `CAPTURE` |
| **Pro Controller** | All of the above |
#### `StickState`
Represents analog stick position and provides utility methods.
**Methods:**
- `get_angle()` - Get stick angle in degrees (0-360)
- `get_magnitude()` - Get displacement from center (0.0-1.0)
- `is_centered(deadzone=0.1)` - Check if stick is centered
- `get_direction_4way()` - Get 4-way directional input
- `get_direction_8way()` - Get 8-way directional input
**Attributes:**
- `x` - X-axis value (-1.0 to 1.0)
- `y` - Y-axis value (-1.0 to 1.0)
- `raw_x` - Raw X value from controller
- `raw_y` - Raw Y value from controller
## Troubleshooting
### Common Issues
**Joy-Con not detected:**
- Ensure Joy-Con is paired via Bluetooth
- Check battery level
- Try re-pairing the controller
- On Linux, verify udev rules are installed
**Permission denied errors (Linux):**
- Add udev rules as shown in setup
- Run with `sudo` (not recommended)
- Ensure `hid_nintendo` kernel module is loaded
**Connection drops:**
- Keep Joy-Con within Bluetooth range
- Check for interference from other devices
- Ensure Joy-Con firmware is up to date
## License
MIT License - See [LICENSE](LICENSE) file for details
Raw data
{
"_id": null,
"home_page": null,
"name": "joycon-lib",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.14,>=3.10",
"maintainer_email": null,
"keywords": "joycon, nintendo, controller, gamepad, input, evdev, linux",
"author": "Dexmate AI",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/85/1f/37bbe787b01f8bd55d6cdd62a6c46fdf374772ad1c8cae0e13be834e5983/joycon_lib-1.0.0.tar.gz",
"platform": null,
"description": "# Joy-Con Python Library\n\nA Linux-based Python library for reading and decoding button information from Nintendo Joy-Con controllers using the evdev interface.\n\n> **Note:** This library currently only supports Linux. Windows and macOS support would require implementing HID or other platform-specific backends.\n\n## Features\n\n- **Linux Support** - Works on Linux with the evdev interface\n- **Evdev Backend** - Direct communication with Joy-Con controllers via Linux input subsystem\n- **Real-time Input** - Read button states and analog sticks in real-time\n- **Multiple Controllers** - Support for left, right, and Pro controllers\n- **Dual Joy-Con Mode** - Use two Joy-Cons as a single controller\n- **Event System** - Register callbacks for button press/release events\n- **Polling Mode** - Continuous input monitoring at configurable rates\n- **Clean API** - Simple, intuitive interface with Google-style docstrings\n- **Low Latency** - Optimized for minimal input lag with device grabbing and efficient event reading\n\n## Requirements\n\n- Python 3.10 or higher\n- Linux operating system\n- Bluetooth-connected Joy-Con controllers\n- Dependencies installed automatically:\n - `evdev` (Linux event device interface)\n\n## Installation\n\n\n```bash\npip install joycon-lib\n```\n\n\n### Prerequisites - Bluetooth Setup\n\n#### Linux Setup\n\nBefore using the library, you need to set up your Linux system:\n\n**1. Enable the hid_nintendo kernel driver (REQUIRED):**\n\n```bash\n# Load the driver - this is required for Joy-Con support\nsudo modprobe hid_nintendo\n\n# To load automatically on boot (recommended):\necho \"hid_nintendo\" | sudo tee /etc/modules-load.d/hid_nintendo.conf\n```\n\n> **Note:** Without the `hid_nintendo` driver, Joy-Con controllers won't be recognized as input devices.\n\n**2. Add udev rules for non-root access (RECOMMENDED):**\n\nWithout these rules, you'll need to run your Python scripts with `sudo`.\n\n```bash\n# Create udev rules file\nsudo tee /etc/udev/rules.d/50-joycon.rules << 'EOF'\n# Nintendo Joy-Con (L/R) and Pro Controller\nKERNEL==\"event*\", ATTRS{name}==\"*Joy-Con*\", MODE=\"0666\"\nKERNEL==\"event*\", ATTRS{name}==\"*Pro Controller*\", MODE=\"0666\"\nEOF\n\n# Reload and apply rules\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\n**3. Connect Joy-Con via Bluetooth:**\n```bash\n# Open Bluetooth control\nbluetoothctl\n\n# In bluetoothctl:\npower on\nagent on\ndefault-agent\nscan on\n\n# Press sync button on Joy-Con (small button between SL/SR)\n# Wait for Joy-Con to appear, note the MAC address\n\npair XX:XX:XX:XX:XX:XX\ntrust XX:XX:XX:XX:XX:XX\nconnect XX:XX:XX:XX:XX:XX\n\n# Exit bluetoothctl\nexit\n```\n\nOr you can use the GUI to connect to the Joy-Con device via Bluetooth.\n\n## Quick Start\n\n### Basic Usage\n\n```python\nfrom joycon_lib import JoyCon, Button\nimport time\n\n# Create and connect to Joy-Con\njoycon = JoyCon(use_backend='evdev') # Linux only\njoycon.connect() # Auto-finds first available Joy-Con\n\n# Read button states\nwhile True:\n buttons = joycon.read_buttons()\n if buttons.is_pressed(Button.A):\n print(\"A button pressed!\")\n time.sleep(0.016) # ~60 FPS\n\n# Disconnect when done\njoycon.disconnect()\n```\n\n### Dual Joy-Con Mode\n\n```python\nfrom joycon_lib import DualJoyCon, Button\nimport time\n\n# Create dual Joy-Con controller\ndual = DualJoyCon(use_backend='evdev') # Linux only\ndual.connect(auto_find=True) # Auto-finds and connects both Joy-Cons\n\n# Start polling\ndual.start_polling(rate=60)\n\n# Read from both controllers as one\nwhile True:\n buttons = dual.get_button_state() # Combined buttons from both\n left_stick, right_stick = dual.get_sticks() # Both analog sticks\n \n if buttons.is_pressed(Button.A):\n print(\"A pressed!\")\n if buttons.is_pressed(Button.L) and buttons.is_pressed(Button.R):\n print(\"L+R combo!\")\n \n time.sleep(0.016)\n\ndual.disconnect()\n```\n\n## Running Examples\n\n### Quick Test\n\n```bash\n# Run the library's built-in test\npython -m joycon_lib\n```\n\n### Example Scripts\n\nThe `examples/` directory contains various demonstration scripts:\n\n| Script | Description |\n|--------|-------------|\n| `example_stick.py` | Test analog stick input |\n| `example_dual.py` | Use both Joy-Cons independently |\n| `example_evdev.py` | Test evdev backend (Linux only) |\n\n```bash\ncd examples/\npython example_dual.py # Start with the interactive demo\n```\n\n## API Reference\n\n### Core Classes\n\n#### `JoyCon`\n\nMain interface for single Joy-Con interaction.\n\n**Methods:**\n- `connect(device_path=None, device_type=None)` - Connect to a Joy-Con (evdev backend)\n- `disconnect()` - Disconnect from the Joy-Con\n- `read_buttons()` - Read current button state from device\n- `get_button_state()` - Get current button state without reading\n- `get_left_stick()` - Get left analog stick state\n- `get_right_stick()` - Get right analog stick state\n- `start_polling(rate=60)` - Start continuous polling\n- `stop_polling()` - Stop polling\n- `register_callback(event_type, callback)` - Register event callback\n- `list_devices()` - List all available Joy-Con devices\n- `get_device_info()` - Get information about connected device\n\n#### `DualJoyCon`\n\nManages two Joy-Cons as a single controller.\n\n**Methods:**\n- `connect(auto_find=True, left_path=None, right_path=None)` - Connect to both Joy-Cons\n- `disconnect()` - Disconnect both controllers\n- `read_input()` - Read input from both Joy-Cons and combine\n- `get_button_state()` - Get combined button state without reading\n- `get_left_stick()` - Get left analog stick position\n- `get_right_stick()` - Get right analog stick position\n- `get_sticks()` - Get both analog stick positions as tuple\n- `start_polling(rate=60)` - Start polling both controllers\n- `stop_polling()` - Stop polling\n- `is_connected()` - Check connection status of both Joy-Cons\n\n#### `ButtonState`\n\nRepresents the current state of all buttons.\n\n**Methods:**\n- `is_pressed(button)` - Check if button is currently pressed\n- `is_released(button)` - Check if button is currently released\n- `just_pressed(button)` - Check if button was just pressed this frame\n- `just_released(button)` - Check if button was just released this frame\n- `get_pressed_names()` - Get list of pressed button names\n- `to_dict()` - Convert button state to dictionary\n\n### Button Mappings\n\n| Controller | Buttons |\n|------------|---------|\n| **Right Joy-Con** | `Y`, `X`, `B`, `A`, `SR_RIGHT`, `SL_RIGHT`, `R`, `ZR`, `PLUS`, `R_STICK`, `HOME` |\n| **Left Joy-Con** | `UP`, `DOWN`, `LEFT`, `RIGHT`, `SR_LEFT`, `SL_LEFT`, `L`, `ZL`, `MINUS`, `L_STICK`, `CAPTURE` |\n| **Pro Controller** | All of the above |\n\n#### `StickState`\n\nRepresents analog stick position and provides utility methods.\n\n**Methods:**\n- `get_angle()` - Get stick angle in degrees (0-360)\n- `get_magnitude()` - Get displacement from center (0.0-1.0)\n- `is_centered(deadzone=0.1)` - Check if stick is centered\n- `get_direction_4way()` - Get 4-way directional input\n- `get_direction_8way()` - Get 8-way directional input\n\n**Attributes:**\n- `x` - X-axis value (-1.0 to 1.0)\n- `y` - Y-axis value (-1.0 to 1.0)\n- `raw_x` - Raw X value from controller\n- `raw_y` - Raw Y value from controller\n\n## Troubleshooting\n\n### Common Issues\n\n**Joy-Con not detected:**\n- Ensure Joy-Con is paired via Bluetooth\n- Check battery level\n- Try re-pairing the controller\n- On Linux, verify udev rules are installed\n\n**Permission denied errors (Linux):**\n- Add udev rules as shown in setup\n- Run with `sudo` (not recommended)\n- Ensure `hid_nintendo` kernel module is loaded\n\n**Connection drops:**\n- Keep Joy-Con within Bluetooth range\n- Check for interference from other devices\n- Ensure Joy-Con firmware is up to date\n\n## License\n\nMIT License - See [LICENSE](LICENSE) file for details\n",
"bugtrack_url": null,
"license": null,
"summary": "A Python library for reading button and stick input from Nintendo Joy-Con controllers",
"version": "1.0.0",
"project_urls": null,
"split_keywords": [
"joycon",
" nintendo",
" controller",
" gamepad",
" input",
" evdev",
" linux"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "2b651efeb9504018dca5056880135604b8c1ac7e97f732c5e4491c24778f4e9e",
"md5": "a88ebd263ad02d5202c864ea5e768893",
"sha256": "88d73663eed81b00bf65a0a1634649b8bc522fd2b208f5d58e3920c6fd7d21c8"
},
"downloads": -1,
"filename": "joycon_lib-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a88ebd263ad02d5202c864ea5e768893",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.14,>=3.10",
"size": 19254,
"upload_time": "2025-09-07T21:46:29",
"upload_time_iso_8601": "2025-09-07T21:46:29.654374Z",
"url": "https://files.pythonhosted.org/packages/2b/65/1efeb9504018dca5056880135604b8c1ac7e97f732c5e4491c24778f4e9e/joycon_lib-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "851f37bbe787b01f8bd55d6cdd62a6c46fdf374772ad1c8cae0e13be834e5983",
"md5": "55c26daf14cd1c542d85e6988b23d95c",
"sha256": "744cf888aad76da4ee867873141b1e408b0c547cb4e34cd3485d88b809f289fa"
},
"downloads": -1,
"filename": "joycon_lib-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "55c26daf14cd1c542d85e6988b23d95c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.14,>=3.10",
"size": 19294,
"upload_time": "2025-09-07T21:46:30",
"upload_time_iso_8601": "2025-09-07T21:46:30.760518Z",
"url": "https://files.pythonhosted.org/packages/85/1f/37bbe787b01f8bd55d6cdd62a6c46fdf374772ad1c8cae0e13be834e5983/joycon_lib-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-07 21:46:30",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "joycon-lib"
}