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.4.0

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.4.0
File Size Uploaded
dualsense_py-0.4.0.tar.gz 29.4 kB Details

Built distribution (wheel)

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

Total release size: 61.6 kB

Release files / dualsense_py-0.4.0.tar.gz

Download URL dualsense_py-0.4.0.tar.gz
Size 29.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d87d1af67bf7ef8d526d239fb52c027cc264773f98af48b2afd53f7de6e9edf3
BLAKE2b-256 checksum
How to use checksums
828103e48dec8fa5418eaf8c6a5d199bd5bc226026c335b0b9f9b0067cb53422
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 26, 2026.

Transparency log

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

Download URL dualsense_py-0.4.0-py3-none-any.whl
Size 32.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25f2109fb355272223306073863a10fcc0c5c37606c682a619cd00e0fa4af793
BLAKE2b-256 checksum
How to use checksums
00a379a9c822293a9d6a78ead144f19235fa8aba4aa6b3576fe3b57f0c84f654
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 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

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