From 0bf55f756b09abdeb7d4eb3df954c7527209bd02 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/7] 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 a54fb55b91f44a5087a336b36afbeb7600a797b5 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/7] 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 604c5f2800c3b0eb9fe7de87ee9e55ad0f0bdb60 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/7] 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 ecb42d9c0a2b1609b9bfbe539c3cea3de7291a9a 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/7] 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 1ab69d6c968545224cf4b0db7193e0fab2e78ff2 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Tue, 22 Dec 2020 14:32:39 +0100 Subject: [PATCH 5/7] Aligned the sticks x and y values so idle position is 0 on both axes --- pydualsense/pydualsense.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pydualsense/pydualsense.py b/pydualsense/pydualsense.py index cb0624f..e633a2f 100644 --- a/pydualsense/pydualsense.py +++ b/pydualsense/pydualsense.py @@ -207,10 +207,10 @@ class pydualsense: """ states = list(inReport) # convert bytes to list # states 0 is always 1 - self.state.LX = states[1] - self.state.LY = states[2] - self.state.RX = states[3] - self.state.RY = states[4] + self.state.LX = states[1] - 127 + self.state.LY = states[2] - 127 + self.state.RX = states[3] - 127 + self.state.RY = states[4] - 127 self.state.L2 = states[5] self.state.R2 = states[6] From fe435f6e360b2e23d61b9b7f50ad3b4f56899905 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:00:46 +0100 Subject: [PATCH 6/7] Update 0.3.0 * Added low freq motor support --- pydualsense/pydualsense.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/pydualsense/pydualsense.py b/pydualsense/pydualsense.py index e633a2f..6a6d690 100644 --- a/pydualsense/pydualsense.py +++ b/pydualsense/pydualsense.py @@ -13,12 +13,10 @@ class pydualsense: self.receive_buffer_size = 64 self.send_report_size = 48 self.color = (0,0,255) # set color around touchpad to blue + self.leftMotor = 0 + self.rightMotor = 0 - - - - - + def init(self): """initialize module and device """ @@ -79,6 +77,15 @@ class pydualsense: dual_sense = hid.Device(vid=detected_device['vendor_id'], pid=detected_device['product_id']) return dual_sense + def setLeftMotor(self, intensity: int): + if intensity > 255: + raise Exception('maximum intensity is 255') + self.leftMotor = intensity + + def setRightMotor(self, intensity: int): + if intensity > 255: + raise Exception('maximum intensity is 255') + self.rightMotor = intensity # right trigger def setRightTriggerMode(self, mode: TriggerModes): @@ -306,8 +313,8 @@ class pydualsense: # 0x80 ??? outReport[2] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2] - outReport[3] = 0 # left low freq motor 0-255 # [3] - outReport[4] = 0 # right low freq motor 0-255 # [4] + outReport[3] = self.leftMotor # left low freq motor 0-255 # [3] + outReport[4] = self.rightMotor # right low freq motor 0-255 # [4] # outReport[5] - outReport[8] audio related From d62e8d133e9732d7ac882ac0c64f8f8871cd9760 Mon Sep 17 00:00:00 2001 From: Florian Kaiser <37000563+flok@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:05:21 +0100 Subject: [PATCH 7/7] update version and readme --- README.md | 5 ++++- setup.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0226bde..0cf3d5d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ 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 @@ -32,7 +35,7 @@ See ``examples`` folder for some more ideas # Credits -Most stuff for this implementation were provided by published an used from: +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) diff --git a/setup.py b/setup.py index ca9da78..3244c29 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r") as fh: setup( name='pydualsense', - version='0.2.0', + version='0.3.0', description='use your DualSense (PS5) controller with python', long_description=long_description, long_description_content_type="text/markdown",