17 Commits

Author SHA1 Message Date
Florian K
c39f3f2ea5 Merge pull request #6 from flok/examples
Examples
2020-12-22 15:12:05 +01:00
Florian K
ea319db5a3 Merge branch 'master' into examples 2020-12-22 15:09:36 +01:00
Florian Kaiser
d62e8d133e update version and readme 2020-12-22 15:05:21 +01:00
Florian Kaiser
fe435f6e36 Update 0.3.0
* Added low freq motor support
2020-12-22 15:00:46 +01:00
Florian Kaiser
cc767d5fcd add and update examples 2020-12-22 14:58:21 +01:00
Florian Kaiser
1ab69d6c96 Aligned the sticks x and y values so idle position is 0 on both axes 2020-12-22 14:32:39 +01:00
Florian Kaiser
b004d2bc7b Added init function for better usability, added check for HIDGuardian usage 2020-12-22 14:15:42 +01:00
Florian K
3f16538555 added credits to README.md 2020-12-22 14:15:42 +01:00
Florian Kaiser
f1be774e68 0.2.0
- added more light functions
- added docstrings for functions
2020-12-22 14:15:42 +01:00
Florian Kaiser
9560d8e637 delete demo 2020-12-22 14:15:42 +01:00
Florian Kaiser
ecb42d9c0a Added init function for better usability, added check for HIDGuardian usage 2020-12-22 14:11:33 +01:00
Florian K
604c5f2800 added credits to README.md 2020-11-30 21:25:13 +01:00
Florian Kaiser
a54fb55b91 0.2.0
- added more light functions
- added docstrings for functions
2020-11-29 22:41:09 +01:00
Florian Kaiser
0bf55f756b delete demo 2020-11-29 22:32:02 +01:00
Florian K
2a5afd7cb0 Merge pull request #2 from flok/examples
- examples
- verbose mode
2020-11-29 19:47:57 +01:00
Florian Kaiser
0a6fee2f85 Reference example in README 2020-11-29 19:44:53 +01:00
Florian Kaiser
2f5579cc49 Examples, closing controller HID device on close function 2020-11-29 19:40:37 +01:00
9 changed files with 229 additions and 94 deletions

View File

@@ -1,15 +1,17 @@
# pydualsense # pydualsense
control your dualsense through python. using the hid library this module implements the sending report for controlling you new PS5 controller. It creates a background thread to constantly update the controller. control your dualsense through python. using the hid library this module implements the sending report for controlling you new PS5 controller. It creates a background thread to constantly receive and update the controller.
# install # install
Just install the package from pypi Just install the package from [pypi](https://pypi.org/project/pydualsense/)
```bash ```bash
pip install pydualsense pip install pydualsense
``` ```
# usage # usage
```python ```python
from pydualsense import pydualsense from pydualsense import pydualsense
@@ -20,9 +22,26 @@ ds.setLeftTriggerMode(TriggerModes.Rigid)
ds.setLeftTriggerForce(1, 255) ds.setLeftTriggerForce(1, 255)
ds.close() # closing the controller ds.close() # closing the controller
``` ```
See ``examples`` folder for some more ideas
# Help wanted
Help wanted from people that want to use this and have feature requests. Just open a issue with the correct label.
# dependecies # dependecies
- hid >= 1.0.4 - hid >= 1.0.4
# Credits
Most stuff for this implementation were provided by and used from:
- [https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/](https://www.reddit.com/r/gamedev/comments/jumvi5/dualsense_haptics_leds_and_more_hid_output_report/)
- [https://github.com/Ryochan7/DS4Windows](https://github.com/Ryochan7/DS4Windows)
# Coming soon # Coming soon
- reading the states of the controller to enable a fully compatibility with python - partially done - reading the states of the controller to enable a fully compatibility with python - partially done

15
examples/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Examples
This folder contains some examples on applications for the library and its usage
## leds.py
The leds.py shows you how you can interact and change the lights of the controller
## effects.py
The effects.py show some effects of the controller
## read_controller.py
The read_controller.py display how you can access the button state of the controller

22
examples/effects.py Normal file
View File

@@ -0,0 +1,22 @@
from pydualsense import *
# get dualsense instance
dualsense = pydualsense()
dualsense.init()
print('Trigger Effect demo started')
dualsense.setLeftMotor(255)
dualsense.setRightMotor(100)
dualsense.setLeftTriggerMode(TriggerModes.Rigid)
dualsense.setLeftTriggerForce(1, 255)
dualsense.setRightTriggerMode(TriggerModes.Pulse_A)
dualsense.setRightTriggerForce(0, 200)
dualsense.setRightTriggerForce(1, 255)
dualsense.setRightTriggerForce(2, 175)
import time; time.sleep(3)
# terminate the thread for message and close the device
dualsense.close()

16
examples/leds.py Normal file
View File

@@ -0,0 +1,16 @@
from pydualsense import *
# get dualsense instance
dualsense = pydualsense()
dualsense.init()
# set color around touchpad to red
dualsense.setColor(0,0,255)
# enable microphone indicator
dualsense.setMicrophoneLED(1)
# set all player indicators on
dualsense.setPlayerID(PlayerID.all)
# sleep a little to see the result on the controller
# this is not needed in normal usage
import time; time.sleep(2)
# terminate the thread for message and close the device
dualsense.close()

View File

@@ -0,0 +1,15 @@
from pydualsense import *
# create dualsense
dualsense = pydualsense()
# find device and initialize
dualsense.init()
# read controller state until R1 is pressed
while not dualsense.state.R1:
print(f"Circle : {dualsense.state.circle} Cross : {dualsense.state.cross} L Stick X : {dualsense.state.LX} L Stick Y : {dualsense.state.LY}")
# close device
dualsense.close()

View File

@@ -1,34 +0,0 @@
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from interface import Ui_MainWindow
from pydualsense import pydualsense
def colorR(value):
global colorR
colorR = value
def colorG(value):
global colorG
colorG = value
def colorB(value):
global colorB
colorB = value
def send():
ds.setColor(colorR, colorG, colorB)
ds.sendReport()
if __name__ == "__main__":
global ds
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
ds = pydualsense()
# connect interface to
ui.slider_r.valueChanged.connect(colorR)
ui.slider_g.valueChanged.connect(colorG)
ui.slider_b.valueChanged.connect(colorB)
ui.pushButton.clicked.connect(send)
MainWindow.show()
sys.exit(app.exec_())

View File

@@ -1,56 +1,93 @@
from os import device_encoding
import hid import hid
from .enums import (LedOptions, PlayerID, from .enums import (LedOptions, PlayerID,
PulseOptions, TriggerModes, Brightness) PulseOptions, TriggerModes, Brightness)
import threading import threading
import sys
import winreg
class pydualsense: class pydualsense:
def __init__(self) -> None: def __init__(self, verbose: bool = False) -> None:
# TODO: maybe add a init function to not automatically allocate controller when class is declared # TODO: maybe add a init function to not automatically allocate controller when class is declared
self.device: hid.Device = self.__find_device() self.verbose = verbose
self.light = DSLight() # control led light of ds
self.audio = DSAudio()
self.triggerL = DSTrigger()
self.triggerR = DSTrigger()
self.color = (0,0,255) # set color around touchpad to blue
self.receive_buffer_size = 64 self.receive_buffer_size = 64
self.send_report_size = 48 self.send_report_size = 48
# controller states self.color = (0,0,255) # set color around touchpad to blue
self.state = DSState()
self.leftMotor = 0
self.rightMotor = 0
def init(self):
"""initialize module and device
"""
self.device: hid.Device = self.__find_device()
self.light = DSLight() # control led light of ds
self.audio = DSAudio() # ds audio setting
self.triggerL = DSTrigger() # left trigger
self.triggerR = DSTrigger() # right trigger
self.state = DSState() # controller states
# thread for receiving and sending # thread for receiving and sending
self.ds_thread = True self.ds_thread = True
self.report_thread = threading.Thread(target=self.sendReport) self.report_thread = threading.Thread(target=self.sendReport)
self.report_thread.start() self.report_thread.start()
self.init = True
def close(self): def close(self):
self.ds_thread = False self.ds_thread = False
self.report_thread.join() self.report_thread.join()
self.device.close()
def _check_hide(self):
"""check if hidguardian is used and controller is hidden
"""
if sys.platform.startswith('win32'):
try:
access_reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
access_key = winreg.OpenKey(access_reg, 'SYSTEM\CurrentControlSet\Services\HidGuardian\Parameters', 0, winreg.KEY_READ)
affected_devices = winreg.QueryValueEx(access_key, 'AffectedDevices')[0]
if "054C" in affected_devices and "0CE6" in affected_devices:
return True
return False
except OSError as e:
print(e)
else:
# TODO: find something for other platforms. Maybe not even needed on linux
return False
def __find_device(self): def __find_device(self):
devices = hid.enumerate(vid=0x054c)
found_devices = []
for device in devices:
if device['vendor_id'] == 0x054c and device['product_id'] == 0x0CE6:
found_devices.append(device)
# TODO: detect connection mode, bluetooth has a bigger write buffer # TODO: detect connection mode, bluetooth has a bigger write buffer
# TODO: implement multiple controllers working # TODO: implement multiple controllers working
if len(found_devices) != 1: if self._check_hide():
raise Exception('no dualsense controller detected') raise Exception('HIDGuardian detected. Delete the controller from HIDGuardian and restart PC to connect to controller')
detected_device = None
devices = hid.enumerate(vid=0x054c)
for device in devices:
if device['vendor_id'] == 0x054c and device['product_id'] == 0x0CE6:
detected_device = device
dual_sense = hid.Device(vid=found_devices[0]['vendor_id'], pid=found_devices[0]['product_id']) if detected_device == None:
raise Exception('No device detected')
dual_sense = hid.Device(vid=detected_device['vendor_id'], pid=detected_device['product_id'])
return dual_sense return dual_sense
def setLeftMotor(self, intensity: int):
if intensity > 255:
raise Exception('maximum intensity is 255')
self.leftMotor = intensity
# color stuff
def setColor(self, r: int, g:int, b:int): def setRightMotor(self, intensity: int):
if r > 255 or g > 255 or b > 255: if intensity > 255:
raise Exception('colors have values from 0 to 255 only') raise Exception('maximum intensity is 255')
self.color = (r,g,b) self.rightMotor = intensity
# right trigger # right trigger
@@ -62,6 +99,7 @@ class pydualsense:
""" """
self.triggerR.mode = mode self.triggerR.mode = mode
def setRightTriggerForce(self, forceID: int, force: int): def setRightTriggerForce(self, forceID: int, force: int):
"""set the right trigger force. trigger consist of 7 parameter """set the right trigger force. trigger consist of 7 parameter
@@ -76,7 +114,6 @@ class pydualsense:
self.triggerR.setForce(id=forceID, force=force) self.triggerR.setForce(id=forceID, force=force)
# left trigger
def setLeftTriggerMode(self, mode: TriggerModes): def setLeftTriggerMode(self, mode: TriggerModes):
"""set the trigger mode for L2 """set the trigger mode for L2
@@ -85,6 +122,7 @@ class pydualsense:
""" """
self.triggerL.mode = mode self.triggerL.mode = mode
def setLeftTriggerForce(self, forceID: int, force: int): def setLeftTriggerForce(self, forceID: int, force: int):
"""set the left trigger force. trigger consist of 7 parameter """set the left trigger force. trigger consist of 7 parameter
@@ -102,8 +140,56 @@ class pydualsense:
# TODO: audio # TODO: audio
# audio stuff # audio stuff
def setMicrophoneLED(self, value): def setMicrophoneLED(self, value):
self.audio.microphoneLED = value self.audio.microphone_led = value
# color stuff
def setColor(self, r: int, g:int, b:int):
"""sets the led colour around the touchpad
:param r: red channel, 0..255
:type r: int
:param g: green channel, 0..255
:type g: int
:param b: blue channel, 0..255
:type b: int
:raises Exception: wron color values
"""
if (r > 255 or g > 255 or b > 255) or (r < 0 or g < 0 or b < 0):
raise Exception('colors have values from 0 to 255 only')
self.color = (r,g,b)
def setLEDOption(self, option: LedOptions):
"""set led option
:param option: led option
:type option: LedOptions
"""
self.light.ledOption = option
def setPulseOption(self, option: PulseOptions):
"""set the pulse option for the leds
:param option: [description]
:type option: PulseOptions
"""
self.light.pulseOptions = option
def setBrightness(self, brightness: Brightness):
"""set the brightness of the player leds
:param brightness: brightness for the leds
:type brightness: Brightness
"""
self.light.brightness = brightness
def setPlayerID(self, player : PlayerID):
"""set the player ID. The controller has 5 white LED which signals
which player the controller is
:param player: the player id from 1 to 5
:type player: PlayerID
"""
self.light.playerNumber = player
def sendReport(self): def sendReport(self):
"""background thread handling the reading of the device and updating its states """background thread handling the reading of the device and updating its states
@@ -131,10 +217,10 @@ class pydualsense:
""" """
states = list(inReport) # convert bytes to list states = list(inReport) # convert bytes to list
# states 0 is always 1 # states 0 is always 1
self.state.LX = states[1] self.state.LX = states[1] - 127
self.state.LY = states[2] self.state.LY = states[2] - 127
self.state.RX = states[3] self.state.RX = states[3] - 127
self.state.RY = states[4] self.state.RY = states[4] - 127
self.state.L2 = states[5] self.state.L2 = states[5]
self.state.R2 = states[6] self.state.R2 = states[6]
@@ -189,7 +275,6 @@ class pydualsense:
# TODO: control mouse with touchpad for fun as DS4Windows # TODO: control mouse with touchpad for fun as DS4Windows
def writeReport(self, outReport): def writeReport(self, outReport):
"""Write the given report to the device """Write the given report to the device
@@ -231,8 +316,8 @@ class pydualsense:
# 0x80 ??? # 0x80 ???
outReport[2] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2] outReport[2] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2]
outReport[3]= 0 # left low freq motor 0-255 # [3] outReport[3] = self.leftMotor # left low freq motor 0-255 # [3]
outReport[4] = 0 # right low freq motor 0-255 # [4] outReport[4] = self.rightMotor # right low freq motor 0-255 # [4]
# outReport[5] - outReport[8] audio related # outReport[5] - outReport[8] audio related
@@ -265,7 +350,8 @@ class pydualsense:
outReport[45] = self.color[0] outReport[45] = self.color[0]
outReport[46] = self.color[1] outReport[46] = self.color[1]
outReport[47] = self.color[2] outReport[47] = self.color[2]
if self.verbose:
print(outReport)
return outReport return outReport
class DSTouchpad: class DSTouchpad:
@@ -349,10 +435,6 @@ class DSLight:
def setBrightness(self, brightness: Brightness): def setBrightness(self, brightness: Brightness):
self._brightness = brightness self._brightness = brightness
def setPlayerNumer(self, player):
if player > 5:
raise Exception('only 5 players supported. choose 1-5')
class DSAudio: class DSAudio:

View File

@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
setup( setup(
name='pydualsense', name='pydualsense',
version='0.1.0', version='0.3.0',
description='use your DualSense (PS5) controller with python', description='use your DualSense (PS5) controller with python',
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",