From 9560d8e6377d96315afa06503f00682c2b848947 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Sun, 29 Nov 2020 22:32:02 +0100 Subject: [PATCH 1/5] delete demo --- pydualsense/pydualsense-demo.py | 34 --------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 pydualsense/pydualsense-demo.py diff --git a/pydualsense/pydualsense-demo.py b/pydualsense/pydualsense-demo.py deleted file mode 100644 index 6f5a9f5..0000000 --- a/pydualsense/pydualsense-demo.py +++ /dev/null @@ -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_()) \ No newline at end of file From f1be774e687c8865c1b62b6e43ccc20a4b1b9502 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Sun, 29 Nov 2020 22:41:09 +0100 Subject: [PATCH 2/5] 0.2.0 - added more light functions - added docstrings for functions --- pydualsense/pydualsense.py | 54 +++++++++++++++++++++++++++++++++----- setup.py | 2 +- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/pydualsense/pydualsense.py b/pydualsense/pydualsense.py index ddbe05e..5ad799f 100644 --- a/pydualsense/pydualsense.py +++ b/pydualsense/pydualsense.py @@ -53,12 +53,6 @@ class pydualsense: return dual_sense - # color stuff - def setColor(self, r: int, g:int, b:int): - if r > 255 or g > 255 or b > 255: - raise Exception('colors have values from 0 to 255 only') - self.color = (r,g,b) - # right trigger def setRightTriggerMode(self, mode: TriggerModes): @@ -111,7 +105,53 @@ class pydualsense: def setMicrophoneLED(self, value): self.audio.microphone_led = value - def setPlayer(self, player : PlayerID): + # 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): diff --git a/setup.py b/setup.py index a48a734..ca9da78 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r") as fh: setup( name='pydualsense', - version='0.1.0', + version='0.2.0', description='use your DualSense (PS5) controller with python', long_description=long_description, long_description_content_type="text/markdown", From 3f165385552e3a3e395a71f09f161bd65f82b84a Mon Sep 17 00:00:00 2001 From: Florian K <37000563+flok@users.noreply.github.com> Date: Mon, 30 Nov 2020 21:25:13 +0100 Subject: [PATCH 3/5] added credits to README.md --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6003f51..0226bde 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ control your dualsense through python. using the hid library this module impleme # install -Just install the package from pypi +Just install the package from [pypi](https://pypi.org/project/pydualsense/) ```bash pip install pydualsense @@ -29,7 +29,15 @@ See ``examples`` folder for some more ideas # dependecies - hid >= 1.0.4 + +# Credits + +Most stuff for this implementation were provided by published an 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 - reading the states of the controller to enable a fully compatibility with python - partially done -- add documentation using sphinx \ No newline at end of file +- add documentation using sphinx From b004d2bc7b16d54fe5d69e59c63c52825acb0a50 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Tue, 22 Dec 2020 14:11:33 +0100 Subject: [PATCH 4/5] Added init function for better usability, added check for HIDGuardian usage --- pydualsense/pydualsense.py | 83 ++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/pydualsense/pydualsense.py b/pydualsense/pydualsense.py index 5ad799f..cb0624f 100644 --- a/pydualsense/pydualsense.py +++ b/pydualsense/pydualsense.py @@ -1,59 +1,85 @@ +from os import device_encoding import hid from .enums import (LedOptions, PlayerID, PulseOptions, TriggerModes, Brightness) import threading - +import sys +import winreg class pydualsense: def __init__(self, verbose: bool = False) -> None: # TODO: maybe add a init function to not automatically allocate controller when class is declared self.verbose = verbose - - self.device: hid.Device = self.__find_device() - - - - self.light = DSLight() # control led light of ds - self.audio = DSAudio() - self.triggerL = DSTrigger() - self.triggerR = DSTrigger() - + self.receive_buffer_size = 64 + self.send_report_size = 48 self.color = (0,0,255) # set color around touchpad to blue - self.receive_buffer_size = 64 - self.send_report_size = 48 - # controller states - self.state = DSState() + + + + + 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 self.ds_thread = True self.report_thread = threading.Thread(target=self.sendReport) self.report_thread.start() + self.init = True + def close(self): self.ds_thread = False self.report_thread.join() self.device.close() - 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) + 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): # TODO: detect connection mode, bluetooth has a bigger write buffer # TODO: implement multiple controllers working - if len(found_devices) != 1: - raise Exception('no dualsense controller detected') + if self._check_hide(): + 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 - # right trigger def setRightTriggerMode(self, mode: TriggerModes): """set the trigger mode for R2 @@ -63,6 +89,7 @@ class pydualsense: """ self.triggerR.mode = mode + def setRightTriggerForce(self, forceID: int, force: int): """set the right trigger force. trigger consist of 7 parameter @@ -77,7 +104,6 @@ class pydualsense: self.triggerR.setForce(id=forceID, force=force) - # left trigger def setLeftTriggerMode(self, mode: TriggerModes): """set the trigger mode for L2 @@ -86,6 +112,7 @@ class pydualsense: """ self.triggerL.mode = mode + def setLeftTriggerForce(self, forceID: int, force: int): """set the left trigger force. trigger consist of 7 parameter @@ -398,10 +425,6 @@ class DSLight: def setBrightness(self, brightness: Brightness): self._brightness = brightness - def setPlayerNumer(self, player): - if player > 5: - raise Exception('only 5 players supported. choose 1-5') - class DSAudio: From cc767d5fcd395e4467af8df02ccc0f2cff08d150 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Tue, 22 Dec 2020 14:58:21 +0100 Subject: [PATCH 5/5] add and update examples --- examples/README.md | 15 +++++++++++++++ examples/effects.py | 22 ++++++++++++++++++++++ examples/leds.py | 3 ++- examples/read_controller.py | 15 +++++++++++++++ examples/trigger_effects.py | 12 ------------ 5 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 examples/README.md create mode 100644 examples/effects.py create mode 100644 examples/read_controller.py delete mode 100644 examples/trigger_effects.py diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..23bd3e7 --- /dev/null +++ b/examples/README.md @@ -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 \ No newline at end of file diff --git a/examples/effects.py b/examples/effects.py new file mode 100644 index 0000000..bf8d4b6 --- /dev/null +++ b/examples/effects.py @@ -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() \ No newline at end of file diff --git a/examples/leds.py b/examples/leds.py index 285c0e5..039908b 100644 --- a/examples/leds.py +++ b/examples/leds.py @@ -2,12 +2,13 @@ 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.setPlayer(PlayerID.all) +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) diff --git a/examples/read_controller.py b/examples/read_controller.py new file mode 100644 index 0000000..adcb00b --- /dev/null +++ b/examples/read_controller.py @@ -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() + + diff --git a/examples/trigger_effects.py b/examples/trigger_effects.py deleted file mode 100644 index 72c06b4..0000000 --- a/examples/trigger_effects.py +++ /dev/null @@ -1,12 +0,0 @@ -from pydualsense import * - -# get dualsense instance -dualsense = pydualsense() -# set left trigger mode to rigid and put some force values on it -dualsense.setLeftTriggerMode(TriggerModes.Rigid) -dualsense.setLeftTriggerForce(1, 255) -# 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() \ No newline at end of file