Skip to main content

dualsense-py

A Python library for reading state from a DualSense controller and reacting to input events in real time.

This package exposes the controller as a live stream of events and state updates, with support for:

  • button press/release callbacks
  • analog stick changes
  • trigger values
  • accelerometer and gyroscope data
  • battery and orientation state
  • LED control
  • HID-backed controller discovery

Features

  • Read DualSense button states with callback hooks such as square_pressed() and circle_released()
  • Track joystick, trigger, accelerometer, gyroscope, and orientation values as they change
  • Detect available controllers using built-in utility functions
  • Set the controller LED color with set_led(red, green, blue)
  • Use the library with native HID device support through hidapi and SDL-based discovery helpers

Installation

Install from PyPI:

pip install dualsense-py

Install from the repository source:

git clone https://github.com/Rothen/dualsensepy.git
cd dualsensepy
pip install .

Quick start

import time

from dualsense_py.utils import get_all_dual_sense_controllers

controllers = get_all_dual_sense_controllers()
if not controllers:
    raise RuntimeError("No DualSense controller found")

controller = controllers[0]
controller.open()

controller.square_pressed(lambda _: print("Square pressed"))
controller.square_released(lambda _: print("Square released"))
controller.left_joy_stick_changed(lambda stick: print(f"Left stick: ({stick.x}, {stick.y})"))
controller.battery_changed(lambda battery: print(f"Battery: {battery.level_percentage}%"))
controller.set_led(0, 255, 0)

try:
    while True:
        time.sleep(0.1)
finally:
    controller.close()

A fuller, runnable version of this (button/stick/battery logging plus a light bar color cycle) lives at examples/basic_usage.py:

python examples/basic_usage.py

Controller discovery

The package includes helper functions for locating controllers:

from dualsense_py.utils import (
    get_all_controllers,
    get_all_dual_sense_controllers,
    get_available_controllers,
)

controllers = get_all_dual_sense_controllers()
for controller in controllers:
    print(controller)

# Generic discovery by vendor/product IDs
all_controllers = get_all_controllers(0x054C, 0x0CE6)

Backends

dualsense-py ships two backends for talking to the controller:

  • hidapi (get_all_dual_sense_controllers(), get_all_controllers()) — reads raw HID reports directly. This is the default used above and needs no extra setup. LED control (set_led) is not implemented on this backend yet and is a no-op.
  • SDL3 (get_available_controllers()) — uses SDL3's gamepad API for broader controller support and working LED control, at the cost of an explicit init step:
from dualsense_py.backends import SDL3Backend
from dualsense_py.utils import get_available_controllers

SDL3Backend.init()
controllers = get_available_controllers()

Call SDL3Backend.init() once before discovering controllers with get_available_controllers(). Controller state is read by polling (SDL_UpdateGamepads() plus the SDL_GetGamepad*() getters) rather than draining SDL's event queue, since DualSenseController reads each device from its own background thread and SDL only allows event pumping from the thread that called SDL_Init.

Event API

The controller exposes a set of event subscription methods. The naming follows the hardware input names and callback type.

Button press callbacks

controller.square_pressed(lambda _: print("Square pressed"))
controller.cross_pressed(lambda _: print("Cross pressed"))
controller.circle_pressed(lambda _: print("Circle pressed"))
controller.triangle_pressed(lambda _: print("Triangle pressed"))
controller.l1_pressed(lambda _: print("L1 pressed"))
controller.r1_pressed(lambda _: print("R1 pressed"))
controller.share_pressed(lambda _: print("Share pressed"))
controller.options_pressed(lambda _: print("Options pressed"))

Button release callbacks

controller.square_released(lambda _: print("Square released"))
controller.dpad_up_released(lambda _: print("D-pad up released"))
controller.dpad_down_released(lambda _: print("D-pad down released"))
controller.ps_released(lambda _: print("PS released"))

State change callbacks

controller.left_joy_stick_changed(lambda stick: print(stick))
controller.right_joy_stick_changed(lambda stick: print(stick))
controller.l2_trigger_changed(lambda value: print(f"L2: {value}"))
controller.r2_trigger_changed(lambda value: print(f"R2: {value}"))
controller.accelerometer_changed(lambda accel: print(accel))
controller.gyroscope_changed(lambda gyro: print(gyro))
controller.battery_changed(lambda battery: print(battery))
controller.orientation_changed(lambda orientation: print(orientation))
controller.touch_finger_1_changed(lambda finger: print(finger))
controller.touch_finger_2_changed(lambda finger: print(finger))

State objects

The library emits dataclasses representing controller state values. These are the actual payloads produced by the change listeners:

  • JoyStick(x, y)
  • Battery(level_percentage, full, charging)
  • Accelerometer(x, y, z)
  • Gyroscope(x, y, z)
  • Orientation(pitch, roll, yaw)
  • TouchFinger(active, id, x, y)
  • TriggerFeedback(active, value)

Example:

controller.left_joy_stick_changed(
    lambda stick: print(f"X={stick.x}, Y={stick.y}")
)

LED control

Set the light bar color on the controller:

controller.set_led(255, 0, 0)    # red
controller.set_led(0, 255, 0)    # green
controller.set_led(0, 0, 255)    # blue
controller.set_led(255, 255, 0)  # yellow

The values are standard 8-bit channel values from 0 to 255.

LED control currently requires the SDL3 backend — see Backends. On the hidapi backend, set_led is a no-op.

Runtime properties

The controller instance also exposes timing metadata while the read loop is running:

print(controller.read_time)
print(controller.loop_time)

Notes

  • The project is currently in an early stage and is best treated as a low-level controller library.
  • Controller access depends on the underlying HID / backend support in the current environment.
  • Event callbacks are subscription-based and can be used to build reactive input loops or game automation logic.

Development

Install the package with its test dependencies and run the test suite with pytest:

pip install -e .[dev]
pytest

The tests drive DualSenseController against an in-memory fake device (see tests/conftest.py), so no physical controller is required to run them.

License

This project is licensed under the MIT License.

Contributing

Contributions are welcome. If you want to improve the library, open an issue or submit a pull request with a clear description of the change and any validation steps.

Metadata

Release files for dualsense-py 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dualsense-py 0.3.2
File Size Uploaded
dualsense_py-0.3.2.tar.gz 28.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dualsense-py 0.3.2
File Interpreter ABI Platform
dualsense_py-0.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 60.7 kB

Release files / dualsense_py-0.3.2.tar.gz

Download URL dualsense_py-0.3.2.tar.gz
Size 28.8 kB
Tags Source
SHA-256 checksum
How to use checksums
4d1c10c373985177d2f5f1bb7536a8c151e6a47cd4c46c16c9d81b23deb3635c
BLAKE2b-256 checksum
How to use checksums
202ba30bf7096c955ff7022cbf64575b12d11459dac5e8b19c52ef32d944ad03
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / dualsense_py-0.3.2-py3-none-any.whl

Download URL dualsense_py-0.3.2-py3-none-any.whl
Size 31.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
53f7daa714a8f698050d2e3d7e98c866f47ee0b9334ab8b24a20c5cf3ca59e56
BLAKE2b-256 checksum
How to use checksums
e2227c6bcfa1c3a799b3c2bda69501673440e790cd1b864aa651bdd50790f65a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.4

2 release files

0.3.3

2 release files

This release

0.3.2 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page