Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f5529f1463 | ||
|
b3ff9fd375 | ||
|
98b13798cd | ||
|
b51c8b49f6 | ||
|
cdbe03ad56 | ||
|
3a14ab3e7a | ||
|
32f9042abb | ||
|
4c86d71633 | ||
|
9c79d961f1 | ||
|
c1c10e4eac | ||
|
04ce807bc0 | ||
|
71a49da5d2 | ||
|
f4e1d73dd3 | ||
|
e766dca70f | ||
|
1e0b23da41 | ||
|
ff01788c89 | ||
|
c07b975bc5 | ||
|
7b0270fa7d | ||
|
83a37750d1 | ||
|
bc0eb35c3c |
21
.github/workflows/python-mypy.yml
vendored
21
.github/workflows/python-mypy.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: Mypy
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Mypy
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install mypy
|
|
||||||
- name: mypy
|
|
||||||
run: |
|
|
||||||
mypy pydualsense/
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -149,6 +149,9 @@ dmypy.json
|
|||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
### pycharm ###
|
||||||
|
.idea/*
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/python,vscode
|
# End of https://www.toptal.com/developers/gitignore/api/python,vscode
|
||||||
|
|
||||||
pydualsense/interface.py
|
pydualsense/interface.py
|
||||||
|
@@ -12,7 +12,7 @@ pip install pydualsense
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
from pydualsense import pydualsense
|
from pydualsense import pydualsense, TriggerModes
|
||||||
|
|
||||||
ds = pydualsense() # open controller
|
ds = pydualsense() # open controller
|
||||||
ds.init() # initialize controller
|
ds.init() # initialize controller
|
||||||
@@ -30,7 +30,7 @@ Help wanted from people that want to use this and have feature requests. Just op
|
|||||||
|
|
||||||
# dependecies
|
# dependecies
|
||||||
|
|
||||||
- hid >= 1.0.4
|
- hidapi-usb >= 0.2.6
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
|
@@ -5,8 +5,8 @@ dualsense = pydualsense()
|
|||||||
dualsense.init()
|
dualsense.init()
|
||||||
# set color around touchpad to red
|
# set color around touchpad to red
|
||||||
dualsense.light.setColorI(255,0,0)
|
dualsense.light.setColorI(255,0,0)
|
||||||
# enable microphone indicator
|
# mute microphone
|
||||||
dualsense.audio.setMicrophoneLED(1)
|
dualsense.audio.setMicrophoneMute(True)
|
||||||
# set all player 1 indicator on
|
# set all player 1 indicator on
|
||||||
dualsense.light.setPlayerID(PlayerID.player1)
|
dualsense.light.setPlayerID(PlayerID.player1)
|
||||||
# sleep a little to see the result on the controller
|
# sleep a little to see the result on the controller
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
from enum import IntFlag
|
from enum import IntFlag
|
||||||
|
|
||||||
|
class ConnectionType(IntFlag):
|
||||||
|
BT = 0x0,
|
||||||
|
USB = 0x1
|
||||||
class LedOptions(IntFlag):
|
class LedOptions(IntFlag):
|
||||||
Off=0x0,
|
Off=0x0,
|
||||||
PlayerLedBrightness=0x1,
|
PlayerLedBrightness=0x1,
|
||||||
|
18
pydualsense/hidguardian.py
Normal file
18
pydualsense/hidguardian.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import winreg
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def check_hide() -> bool:
|
||||||
|
"""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:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
@@ -1,21 +1,17 @@
|
|||||||
|
|
||||||
# needed for python > 3.8
|
# needed for python > 3.8
|
||||||
import os, sys
|
import os, sys
|
||||||
if sys.version_info >= (3,8):
|
if sys.platform.startswith('win32') and sys.version_info >= (3,8):
|
||||||
os.add_dll_directory(os.getcwd())
|
os.add_dll_directory(os.getcwd())
|
||||||
|
|
||||||
import hid # type: ignore
|
import hidapi
|
||||||
from .enums import (LedOptions, PlayerID,
|
from .enums import (LedOptions, PlayerID, PulseOptions, TriggerModes, Brightness, ConnectionType) # type: ignore
|
||||||
PulseOptions, TriggerModes, Brightness)
|
|
||||||
import threading
|
import threading
|
||||||
import winreg
|
|
||||||
class pydualsense:
|
class pydualsense:
|
||||||
|
|
||||||
def __init__(self, verbose: bool = False) -> 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.verbose = verbose
|
self.verbose = verbose
|
||||||
self.receive_buffer_size = 64
|
|
||||||
self.send_report_size = 48
|
|
||||||
|
|
||||||
self.leftMotor = 0
|
self.leftMotor = 0
|
||||||
self.rightMotor = 0
|
self.rightMotor = 0
|
||||||
@@ -24,7 +20,7 @@ class pydualsense:
|
|||||||
def init(self):
|
def init(self):
|
||||||
"""initialize module and device states
|
"""initialize module and device states
|
||||||
"""
|
"""
|
||||||
self.device: hid.Device = self.__find_device()
|
self.device: hidapi.Device = self.__find_device()
|
||||||
self.light = DSLight() # control led light of ds
|
self.light = DSLight() # control led light of ds
|
||||||
self.audio = DSAudio() # ds audio setting
|
self.audio = DSAudio() # ds audio setting
|
||||||
self.triggerL = DSTrigger() # left trigger
|
self.triggerL = DSTrigger() # left trigger
|
||||||
@@ -32,13 +28,25 @@ class pydualsense:
|
|||||||
|
|
||||||
self.state = DSState() # controller states
|
self.state = DSState() # controller states
|
||||||
|
|
||||||
|
self.conType = self.determineConnectionType() # determine USB or BT connection
|
||||||
|
|
||||||
|
|
||||||
# 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 determineConnectionType(self) -> ConnectionType:
|
||||||
|
|
||||||
|
if self.device._device.input_report_length == 64:
|
||||||
|
self.input_report_length = 64
|
||||||
|
self.output_report_length = 64
|
||||||
|
return ConnectionType.USB
|
||||||
|
elif self.device._device.input_report_length == 78:
|
||||||
|
self.input_report_length = 78
|
||||||
|
self.output_report_length = 78
|
||||||
|
return ConnectionType.BT
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
@@ -48,24 +56,8 @@ class pydualsense:
|
|||||||
self.report_thread.join()
|
self.report_thread.join()
|
||||||
self.device.close()
|
self.device.close()
|
||||||
|
|
||||||
def _check_hide(self) -> bool:
|
|
||||||
"""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)
|
|
||||||
|
|
||||||
return False
|
def __find_device(self) -> hidapi.Device:
|
||||||
|
|
||||||
|
|
||||||
def __find_device(self) -> hid.Device:
|
|
||||||
"""
|
"""
|
||||||
find HID device and open it
|
find HID device and open it
|
||||||
|
|
||||||
@@ -78,19 +70,21 @@ class pydualsense:
|
|||||||
"""
|
"""
|
||||||
# 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 self._check_hide():
|
if sys.platform.startswith('win32'):
|
||||||
raise Exception('HIDGuardian detected. Delete the controller from HIDGuardian and restart PC to connect to controller')
|
import pydualsense.hidguardian as hidguardian
|
||||||
detected_device: hid.Device = None
|
if hidguardian.check_hide():
|
||||||
devices = hid.enumerate(vid=0x054c)
|
raise Exception('HIDGuardian detected. Delete the controller from HIDGuardian and restart PC to connect to controller')
|
||||||
|
detected_device: hidapi.Device = None
|
||||||
|
devices = hidapi.enumerate(vendor_id=0x054c)
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if device['vendor_id'] == 0x054c and device['product_id'] == 0x0CE6:
|
if device.vendor_id == 0x054c and device.product_id == 0x0CE6:
|
||||||
detected_device = device
|
detected_device = device
|
||||||
|
|
||||||
|
|
||||||
if detected_device == None:
|
if detected_device == None:
|
||||||
raise Exception('No device detected')
|
raise Exception('No device detected')
|
||||||
|
|
||||||
dual_sense = hid.Device(vid=detected_device['vendor_id'], pid=detected_device['product_id'])
|
dual_sense = hidapi.Device(vendor_id=detected_device.vendor_id, product_id=detected_device.product_id)
|
||||||
return dual_sense
|
return dual_sense
|
||||||
|
|
||||||
def setLeftMotor(self, intensity: int):
|
def setLeftMotor(self, intensity: int):
|
||||||
@@ -135,10 +129,10 @@ class pydualsense:
|
|||||||
"""background thread handling the reading of the device and updating its states
|
"""background thread handling the reading of the device and updating its states
|
||||||
"""
|
"""
|
||||||
while self.ds_thread:
|
while self.ds_thread:
|
||||||
|
|
||||||
# read data from the input report of the controller
|
# read data from the input report of the controller
|
||||||
inReport = self.device.read(self.receive_buffer_size)
|
inReport = self.device.read(self.input_report_length)
|
||||||
|
if self.verbose:
|
||||||
|
print(inReport)
|
||||||
# decrypt the packet and bind the inputs
|
# decrypt the packet and bind the inputs
|
||||||
self.readInput(inReport)
|
self.readInput(inReport)
|
||||||
|
|
||||||
@@ -191,6 +185,7 @@ class pydualsense:
|
|||||||
misc2 = states[10]
|
misc2 = states[10]
|
||||||
self.state.ps = (misc2 & (1 << 0)) != 0
|
self.state.ps = (misc2 & (1 << 0)) != 0
|
||||||
self.state.touchBtn = (misc2 & 0x02) != 0
|
self.state.touchBtn = (misc2 & 0x02) != 0
|
||||||
|
self.state.micBtn = (misc2 & 0x04) != 0
|
||||||
|
|
||||||
|
|
||||||
# trackpad touch
|
# trackpad touch
|
||||||
@@ -233,7 +228,8 @@ class pydualsense:
|
|||||||
Returns:
|
Returns:
|
||||||
list: report to send to controller
|
list: report to send to controller
|
||||||
"""
|
"""
|
||||||
outReport = [0] * 48 # create empty list with range of output report
|
|
||||||
|
outReport = [0] * self.output_report_length # create empty list with range of output report
|
||||||
# packet type
|
# packet type
|
||||||
outReport[0] = 0x2
|
outReport[0] = 0x2
|
||||||
|
|
||||||
@@ -267,6 +263,8 @@ class pydualsense:
|
|||||||
# set Micrphone LED, setting doesnt effect microphone settings
|
# set Micrphone LED, setting doesnt effect microphone settings
|
||||||
outReport[9] = self.audio.microphone_led # [9]
|
outReport[9] = self.audio.microphone_led # [9]
|
||||||
|
|
||||||
|
outReport[10] = 0x10 if self.audio.microphone_mute == True else 0x00
|
||||||
|
|
||||||
# add right trigger mode + parameters to packet
|
# add right trigger mode + parameters to packet
|
||||||
outReport[11] = self.triggerR.mode.value
|
outReport[11] = self.triggerR.mode.value
|
||||||
outReport[12] = self.triggerR.forces[0]
|
outReport[12] = self.triggerR.forces[0]
|
||||||
@@ -488,15 +486,24 @@ class DSAudio:
|
|||||||
This doesnt change the mute/unmutes the microphone itself.
|
This doesnt change the mute/unmutes the microphone itself.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (int): On or off microphone LED
|
value (bool): On or off microphone LED
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Exception: false state for the led
|
Exception: false state for the led
|
||||||
"""
|
"""
|
||||||
if value > 1 or value < 0:
|
if not isinstance(value, bool):
|
||||||
raise Exception('Microphone LED can only be on or off (0 .. 1)')
|
raise TypeError('MicrophoneLED can only be a bool')
|
||||||
self.microphone_led = value
|
self.microphone_led = value
|
||||||
|
|
||||||
|
def setMicrophoneMute(self, state):
|
||||||
|
|
||||||
|
if not isinstance(state, bool):
|
||||||
|
raise TypeError('state needs to be bool')
|
||||||
|
|
||||||
|
self.setMicrophoneLED(state) # set led accordingly
|
||||||
|
self.microphone_mute = state
|
||||||
|
|
||||||
|
|
||||||
class DSTrigger:
|
class DSTrigger:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
# trigger modes
|
# trigger modes
|
||||||
@@ -538,4 +545,4 @@ class DSTrigger:
|
|||||||
if not isinstance(mode, TriggerModes):
|
if not isinstance(mode, TriggerModes):
|
||||||
raise TypeError('Trigger mode parameter needs to be of type `TriggerModes`')
|
raise TypeError('Trigger mode parameter needs to be of type `TriggerModes`')
|
||||||
|
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
4
setup.py
4
setup.py
@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='pydualsense',
|
name='pydualsense',
|
||||||
version='0.4.2',
|
version='0.5.2.5',
|
||||||
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",
|
||||||
@@ -14,5 +14,5 @@ setup(
|
|||||||
author='Florian K',
|
author='Florian K',
|
||||||
license='MIT License',
|
license='MIT License',
|
||||||
packages=setuptools.find_packages(),
|
packages=setuptools.find_packages(),
|
||||||
install_requires=['hid>=1.0.4']
|
install_requires=['hidapi-usb', 'cffi']
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user