21 Commits

Author SHA1 Message Date
Florian Kaiser
b67bcb2b9d Bump to 0.7.0 2023-02-24 22:12:29 +01:00
Florian Kaiser
546a00c9b4 Add BT support, with crc calculation and batery readout 2023-02-24 22:11:15 +01:00
Flo
c0797ced0b Merge pull request #47 from flok/revert-45-bluetooth_impl
Revert "Bluetooth implementation"
2023-02-24 21:44:35 +01:00
Flo
bb3ab10b91 Revert "Bluetooth implementation" 2023-02-24 21:44:25 +01:00
Flo
875b5f66f8 Merge pull request #45 from kit-nya/bluetooth_impl
Bluetooth & Battery implementation
2023-02-24 12:55:43 +01:00
Kit
aebe1e581e Bluetooth implementation
Added in support for bluetooth. Also added in battery status support too.
2023-02-19 16:45:50 +09:00
Flo
de894fca7e Update setup.py 2023-02-05 16:32:04 +01:00
Flo
1b06033d4c Merge pull request #42 from triveria/add-bluetooth-readout
Add bluetooth readout
2023-02-05 16:30:11 +01:00
Michael Wagner
4105048784 Add option to leave channel-readout example with 'q' 2023-02-04 14:43:39 +01:00
Michael Wagner
e2c16cd29e Detect connection type and support bluetooth receiving 2023-02-02 22:27:59 +01:00
Michael Wagner
3b3ec445ad Hard code bluetooth mode for now 2023-02-02 20:21:03 +01:00
Michael Wagner
dc14382b62 Add example to continuously read out all states 2023-02-02 20:20:37 +01:00
Michael Wagner
bda77189f7 Add udev rule for reading controller without root privileges 2023-02-02 20:15:39 +01:00
Flo
d34302b494 Merge pull request #41 from CrimsonZen/patch-1
fixing leds.py example PlayerID reference
2023-01-14 19:13:54 +01:00
Chris Woytowitz
a808741b7f fixing leds.py example PlayerID reference 2023-01-14 09:01:50 -08:00
Flo
6f7413ecad Update docs_publish.yml 2023-01-05 20:49:25 +01:00
Flo
7c79dc5fbf Update setup.py 2023-01-04 17:57:12 +01:00
Flo
98271e866f Merge pull request #39 from dsb298/dsb298-contribution
fixed left/right motors
2023-01-04 17:56:24 +01:00
devlin
caa2062cea fixed left/right motors 2023-01-02 00:14:57 -05:00
Flo
9359e6eac4 Merge pull request #37 from flok/dev
Update doc
2022-10-23 09:56:49 +02:00
Florian Kaiser
c54f58bcee Update doc 2022-10-23 09:55:56 +02:00
12 changed files with 317 additions and 74 deletions

View File

@@ -1,5 +1,5 @@
name: publish_docs
on: [push, pull_request, workflow_dispatch]
on: [push, workflow_dispatch]
jobs:
docs:
runs-on: ubuntu-latest

11
70-ps5-controller.rules Normal file
View File

@@ -0,0 +1,11 @@
# ref.: https://boilingsteam.com/the-dualsense-is-making-even-more-sense/
# copy this file to /etc/udev/rules.d
# reload udev rules with:
# udevadm control --reload-rules
# udevadm trigger
# PS5 DualSense controller over USB hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ce6", MODE="0660", TAG+="uaccess"
# PS5 DualSense controller over bluetooth hidraw
KERNEL=="hidraw*", KERNELS=="*054C:0CE6*", MODE="0660", TAG+="uaccess"

View File

@@ -17,7 +17,15 @@ pip install --upgrade pydualsense
## Linux
On Linux based system you first need to install the hidapi through your package manager of your system.
On Linux based system you first need to add a udev rule to let the user access the PS5 controller without requiring root privileges.
```bash
sudo cp 70-ps5-controller.rules /etc/udev/rules.d
sudo udevadm control --reload-rules
sudo udevadm trigger
```
Then install the hidapi through your package manager of your system.
On an Ubuntu system the package ```libhidapi-dev``` is required.
@@ -71,7 +79,5 @@ Most stuff for this implementation were provided by and used from:
# Coming soon
- add bluetooth support
- add multiple controllers
- partially done
- add documentation using sphinx

View File

@@ -7,3 +7,4 @@ This is the front page for the API documentation of the **pydualsense** library.
.. toctree::
ds_enum
ds_main
ds_eventsystem

View File

@@ -0,0 +1,10 @@
pydualsense event system classes
=========================
The `Event System` implements the event system used for the button callbacks
.. automodule:: pydualsense.event_system
:noindex:
:members:
:undoc-members:
:show-inheritance:

View File

@@ -17,6 +17,7 @@ Contents
usage
api
examples
modules
TODOs

View File

@@ -8,7 +8,7 @@ dualsense.light.setColorI(255,0,0)
# mute microphone
dualsense.audio.setMicrophoneState(True)
# set all player 1 indicator on
dualsense.light.setPlayerID(PlayerID.player1)
dualsense.light.setPlayerID(PlayerID.PLAYER_1)
# sleep a little to see the result on the controller
# this is not needed in normal usage
import time; time.sleep(2)

View File

@@ -0,0 +1,54 @@
import curses
import time
from pydualsense import *
def print_states(stdscr):
curses.curs_set(0)
curses.use_default_colors()
stdscr.nodelay(1)
while True:
stdscr.erase()
pretty_states = [f"{state:03}" for state in dualsense.states]
stdscr.addstr(f"epoch: {time.time():.2f}\n")
stdscr.addstr(f"states[0:10]: {pretty_states[0:10]}\n")
stdscr.addstr(f"states[10:20]: {pretty_states[10:20]}\n")
stdscr.addstr(f"states[20:30]: {pretty_states[20:30]}\n")
stdscr.addstr(f"states[30:40]: {pretty_states[30:40]}\n")
stdscr.addstr(f"states[40:50]: {pretty_states[40:50]}\n")
stdscr.addstr(f"states[50:60]: {pretty_states[50:60]}\n")
stdscr.addstr(f"states[60:70]: {pretty_states[60:70]}\n")
stdscr.addstr(f"states[70:78]: {pretty_states[70:78]}\n")
stdscr.addstr("\n")
stdscr.addstr(f"square: {dualsense.state.square!s:>5} \t triangle: {dualsense.state.triangle!s:>5} \t circle: {dualsense.state.circle!s:>5} \t cross: {dualsense.state.cross!s:>5}\n")
stdscr.addstr(f"DpadUp: {dualsense.state.DpadUp!s:>5} \t DpadDown: {dualsense.state.DpadDown!s:>5} \t DpadLeft: {dualsense.state.DpadLeft!s:>5} \t DpadRight: {dualsense.state.DpadRight!s:>5}\n")
stdscr.addstr(f"L1: {dualsense.state.L1!s:>5} \t L2: {dualsense.state.L2:3} \t L2Btn: {dualsense.state.L2Btn!s:>5} \t L3: {dualsense.state.L3!s:>5} \t R1: {dualsense.state.R1!s:>5} \t R2: {dualsense.state.R2:3d} \t R2Btn: {dualsense.state.R2Btn!s:>5} \t R3: {dualsense.state.R3!s:>5}\n")
stdscr.addstr(f"share: {dualsense.state.share!s:>5} \t options: {dualsense.state.options!s:>5} \t ps: {dualsense.state.ps!s:>5} \t touch1: {dualsense.state.touch1!s:>5} \t touch2: {dualsense.state.touch2!s:>5} \t touchBtn: {dualsense.state.touchBtn!s:>5} \t touchRight: {dualsense.state.touchRight!s:>5} \t touchLeft: {dualsense.state.touchLeft!s:>5}\n")
stdscr.addstr(f"touchFinger1: {dualsense.state.touchFinger1} \t touchFinger2: {dualsense.state.touchFinger2}\n")
stdscr.addstr(f"micBtn: {dualsense.state.micBtn!s:>5}\n")
stdscr.addstr(f"RX: {dualsense.state.RX:4} \t RY: {dualsense.state.RY:4} \t LX: {dualsense.state.LX:4} \t LY: {dualsense.state.LY:4}\n")
stdscr.addstr(f"trackPadTouch0: ID: {dualsense.state.trackPadTouch0.ID} \t isActive: {dualsense.state.trackPadTouch0.isActive!s:>5} \t X: {dualsense.state.trackPadTouch0.X:4d} \t Y: {dualsense.state.trackPadTouch0.Y:4d}\n")
stdscr.addstr(f"trackPadTouch1: ID: {dualsense.state.trackPadTouch1.ID} \t isActive: {dualsense.state.trackPadTouch1.isActive!s:>5} \t X: {dualsense.state.trackPadTouch1.X:4d} \t Y: {dualsense.state.trackPadTouch1.Y:4d}\n")
stdscr.addstr(f"gyro: roll: {dualsense.state.gyro.Roll:6} \t pitch: {dualsense.state.gyro.Pitch:6} \t yaw: {dualsense.state.gyro.Yaw:6}\n")
stdscr.addstr(f"acc: X: {dualsense.state.accelerometer.X:6} \t Y: {dualsense.state.accelerometer.Y:6} \t Z: {dualsense.state.accelerometer.Z:6}\n")
stdscr.addstr(f"battery : Level: {dualsense.battery.Level:6} \t State : {dualsense.battery.State}")
stdscr.addstr("\n")
stdscr.addstr("Exit script with 'q'\n")
stdscr.refresh()
if stdscr.getch() == ord('q'):
break
dualsense = pydualsense()
dualsense.init()
while dualsense.states is None:
print("Waiting until connection is established...")
print(f"epoch: {time.time():.0f}")
time.sleep(0.5)
curses.wrapper(print_states)
dualsense.close()

47
pydualsense/checksum.py Normal file
View File

@@ -0,0 +1,47 @@
import array
# from South-River
hashTable = array.array('I', [
0xd202ef8d, 0xa505df1b, 0x3c0c8ea1, 0x4b0bbe37, 0xd56f2b94, 0xa2681b02, 0x3b614ab8, 0x4c667a2e,
0xdcd967bf, 0xabde5729, 0x32d70693, 0x45d03605, 0xdbb4a3a6, 0xacb39330, 0x35bac28a, 0x42bdf21c,
0xcfb5ffe9, 0xb8b2cf7f, 0x21bb9ec5, 0x56bcae53, 0xc8d83bf0, 0xbfdf0b66, 0x26d65adc, 0x51d16a4a,
0xc16e77db, 0xb669474d, 0x2f6016f7, 0x58672661, 0xc603b3c2, 0xb1048354, 0x280dd2ee, 0x5f0ae278,
0xe96ccf45, 0x9e6bffd3, 0x762ae69, 0x70659eff, 0xee010b5c, 0x99063bca, 0xf6a70, 0x77085ae6,
0xe7b74777, 0x90b077e1, 0x9b9265b, 0x7ebe16cd, 0xe0da836e, 0x97ddb3f8, 0xed4e242, 0x79d3d2d4,
0xf4dbdf21, 0x83dcefb7, 0x1ad5be0d, 0x6dd28e9b, 0xf3b61b38, 0x84b12bae, 0x1db87a14, 0x6abf4a82,
0xfa005713, 0x8d076785, 0x140e363f, 0x630906a9, 0xfd6d930a, 0x8a6aa39c, 0x1363f226, 0x6464c2b0,
0xa4deae1d, 0xd3d99e8b, 0x4ad0cf31, 0x3dd7ffa7, 0xa3b36a04, 0xd4b45a92, 0x4dbd0b28, 0x3aba3bbe,
0xaa05262f, 0xdd0216b9, 0x440b4703, 0x330c7795, 0xad68e236, 0xda6fd2a0, 0x4366831a, 0x3461b38c,
0xb969be79, 0xce6e8eef, 0x5767df55, 0x2060efc3, 0xbe047a60, 0xc9034af6, 0x500a1b4c, 0x270d2bda,
0xb7b2364b, 0xc0b506dd, 0x59bc5767, 0x2ebb67f1, 0xb0dff252, 0xc7d8c2c4, 0x5ed1937e, 0x29d6a3e8,
0x9fb08ed5, 0xe8b7be43, 0x71beeff9, 0x6b9df6f, 0x98dd4acc, 0xefda7a5a, 0x76d32be0, 0x1d41b76,
0x916b06e7, 0xe66c3671, 0x7f6567cb, 0x862575d, 0x9606c2fe, 0xe101f268, 0x7808a3d2, 0xf0f9344,
0x82079eb1, 0xf500ae27, 0x6c09ff9d, 0x1b0ecf0b, 0x856a5aa8, 0xf26d6a3e, 0x6b643b84, 0x1c630b12,
0x8cdc1683, 0xfbdb2615, 0x62d277af, 0x15d54739, 0x8bb1d29a, 0xfcb6e20c, 0x65bfb3b6, 0x12b88320,
0x3fba6cad, 0x48bd5c3b, 0xd1b40d81, 0xa6b33d17, 0x38d7a8b4, 0x4fd09822, 0xd6d9c998, 0xa1def90e,
0x3161e49f, 0x4666d409, 0xdf6f85b3, 0xa868b525, 0x360c2086, 0x410b1010, 0xd80241aa, 0xaf05713c,
0x220d7cc9, 0x550a4c5f, 0xcc031de5, 0xbb042d73, 0x2560b8d0, 0x52678846, 0xcb6ed9fc, 0xbc69e96a,
0x2cd6f4fb, 0x5bd1c46d, 0xc2d895d7, 0xb5dfa541, 0x2bbb30e2, 0x5cbc0074, 0xc5b551ce, 0xb2b26158,
0x4d44c65, 0x73d37cf3, 0xeada2d49, 0x9ddd1ddf, 0x3b9887c, 0x74beb8ea, 0xedb7e950, 0x9ab0d9c6,
0xa0fc457, 0x7d08f4c1, 0xe401a57b, 0x930695ed, 0xd62004e, 0x7a6530d8, 0xe36c6162, 0x946b51f4,
0x19635c01, 0x6e646c97, 0xf76d3d2d, 0x806a0dbb, 0x1e0e9818, 0x6909a88e, 0xf000f934, 0x8707c9a2,
0x17b8d433, 0x60bfe4a5, 0xf9b6b51f, 0x8eb18589, 0x10d5102a, 0x67d220bc, 0xfedb7106, 0x89dc4190,
0x49662d3d, 0x3e611dab, 0xa7684c11, 0xd06f7c87, 0x4e0be924, 0x390cd9b2, 0xa0058808, 0xd702b89e,
0x47bda50f, 0x30ba9599, 0xa9b3c423, 0xdeb4f4b5, 0x40d06116, 0x37d75180, 0xaede003a, 0xd9d930ac,
0x54d13d59, 0x23d60dcf, 0xbadf5c75, 0xcdd86ce3, 0x53bcf940, 0x24bbc9d6, 0xbdb2986c, 0xcab5a8fa,
0x5a0ab56b, 0x2d0d85fd, 0xb404d447, 0xc303e4d1, 0x5d677172, 0x2a6041e4, 0xb369105e, 0xc46e20c8,
0x72080df5, 0x50f3d63, 0x9c066cd9, 0xeb015c4f, 0x7565c9ec, 0x262f97a, 0x9b6ba8c0, 0xec6c9856,
0x7cd385c7, 0xbd4b551, 0x92dde4eb, 0xe5dad47d, 0x7bbe41de, 0xcb97148, 0x95b020f2, 0xe2b71064,
0x6fbf1d91, 0x18b82d07, 0x81b17cbd, 0xf6b64c2b, 0x68d2d988, 0x1fd5e91e, 0x86dcb8a4, 0xf1db8832,
0x616495a3, 0x1663a535, 0x8f6af48f, 0xf86dc419, 0x660951ba, 0x110e612c, 0x88073096, 0xff000000
])
def compute(buffer):
result = 0xeada2d49
for i in range(0, 74):
result = hashTable[(result&0xFF)^(buffer[i]&0xFF)]^(result>>8)
return result

View File

@@ -44,3 +44,13 @@ class TriggerModes(IntFlag):
Pulse_B = 0x2 | 0x04
Pulse_AB = 0x2 | 0x20 | 0x04
Calibration = 0xFC
class BatteryState(IntFlag):
POWER_SUPPLY_STATUS_DISCHARGING = 0x0
POWER_SUPPLY_STATUS_CHARGING = 0x1
POWER_SUPPLY_STATUS_FULL = 0x2
POWER_SUPPLY_STATUS_NOT_CHARGING = 0xb
POWER_SUPPLY_STATUS_ERROR = 0xf
POWER_SUPPLY_TEMP_OR_VOLTAGE_OUT_OF_RANGE = 0xa
POWER_SUPPLY_STATUS_UNKNOWN = 0x0

View File

@@ -7,9 +7,10 @@ if platform.startswith('Windows') and sys.version_info >= (3, 8):
os.add_dll_directory(os.getcwd())
import hidapi
from .enums import (LedOptions, PlayerID, PulseOptions, TriggerModes, Brightness, ConnectionType) # type: ignore
from .enums import (LedOptions, PlayerID, PulseOptions, TriggerModes, Brightness, ConnectionType, BatteryState) # type: ignore
import threading
from .event_system import Event
from .checksum import compute
from copy import deepcopy
@@ -20,6 +21,10 @@ logger.setLevel(logging.INFO)
class pydualsense:
OUTPUT_REPORT_USB = 0x02
OUTPUT_REPORT_BT = 0x31
def __init__(self, verbose: bool = False) -> None:
"""
initialise the library but dont connect to the controller. call :func:`init() <pydualsense.pydualsense.init>` to connect to the controller
@@ -98,33 +103,35 @@ class pydualsense:
self.triggerL = DSTrigger() # left trigger
self.triggerR = DSTrigger() # right trigger
self.state = DSState() # controller states
if platform.startswith('Windows'):
self.conType = self.determineConnectionType() # determine USB or BT connection
else:
# set for usb manually
self.input_report_length = 64
self.output_report_length = 64
self.battery = DSBattery()
self.conType = self.determineConnectionType() # determine USB or BT connection
self.ds_thread = True
self.report_thread = threading.Thread(target=self.sendReport)
self.report_thread.start()
self.states = None
def determineConnectionType(self) -> ConnectionType:
"""
Determine the connection type of the controller. eg USB or BT.
Currently only USB is supported.
We ask the controller for an input report with a length up to 100 bytes
and afterwords check the lenght of the received input report.
The connection type determines the length of the report.
This way of determining is not pretty but it works..
Returns:
ConnectionType: Detected connection type of the controller.
"""
if self.device._device.input_report_length == 64:
dummy_report = self.device.read(100)
input_report_length = len(dummy_report)
if 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:
elif input_report_length == 78:
self.input_report_length = 78
self.output_report_length = 78
return ConnectionType.BT
@@ -228,7 +235,15 @@ class pydualsense:
Args:
inReport (bytearray): read bytearray containing the state of the whole controller
"""
states = list(inReport) # convert bytes to list
if self.conType == ConnectionType.BT:
# the reports for BT and USB are structured the same,
# but there is one more byte at the start of the bluetooth report.
# We drop that byte, so that the format matches up again.
states = list(inReport)[1:] # convert bytes to list
else: # USB
states = list(inReport) # convert bytes to list
self.states = states
# states 0 is always 1
self.state.LX = states[1] - 127
self.state.LY = states[2] - 127
@@ -286,6 +301,11 @@ class pydualsense:
self.state.gyro.Yaw = int.from_bytes(([inReport[24], inReport[25]]), byteorder='little', signed=True)
self.state.gyro.Roll = int.from_bytes(([inReport[26], inReport[27]]), byteorder='little', signed=True)
# from kit-nya
battery = states[53]
self.battery.State = BatteryState((battery & 0xF0) >> 4)
self.battery.Level = min((battery & 0x0F) * 10 + 5, 100)
# first call we dont have a "last state" so we create if with the first occurence
if self.last_states is None:
self.last_states = deepcopy(self.state)
@@ -390,67 +410,141 @@ class pydualsense:
list: report to send to controller
"""
outReport = [0] * self.output_report_length # create empty list with range of output report
# packet type
outReport[0] = 0x2
if self.conType == ConnectionType.USB:
outReport = [0] * self.output_report_length # create empty list with range of output report
# packet type
outReport[0] = self.OUTPUT_REPORT_USB
# flags determing what changes this packet will perform
# 0x01 set the main motors (also requires flag 0x02); setting this by itself will allow rumble to gracefully terminate and then re-enable audio haptics, whereas not setting it will kill the rumble instantly and re-enable audio haptics.
# 0x02 set the main motors (also requires flag 0x01; without bit 0x01 motors are allowed to time out without re-enabling audio haptics)
# 0x04 set the right trigger motor
# 0x08 set the left trigger motor
# 0x10 modification of audio volume
# 0x20 toggling of internal speaker while headset is connected
# 0x40 modification of microphone volume
outReport[1] = 0xff # [1]
# flags determing what changes this packet will perform
# 0x01 set the main motors (also requires flag 0x02); setting this by itself will allow rumble to gracefully terminate and then re-enable audio haptics, whereas not setting it will kill the rumble instantly and re-enable audio haptics.
# 0x02 set the main motors (also requires flag 0x01; without bit 0x01 motors are allowed to time out without re-enabling audio haptics)
# 0x04 set the right trigger motor
# 0x08 set the left trigger motor
# 0x10 modification of audio volume
# 0x20 toggling of internal speaker while headset is connected
# 0x40 modification of microphone volume
outReport[1] = 0xff # [1]
# further flags determining what changes this packet will perform
# 0x01 toggling microphone LED
# 0x02 toggling audio/mic mute
# 0x04 toggling LED strips on the sides of the touchpad
# 0x08 will actively turn all LEDs off? Convenience flag? (if so, third parties might not support it properly)
# 0x10 toggling white player indicator LEDs below touchpad
# 0x20 ???
# 0x40 adjustment of overall motor/effect power (index 37 - read note on triggers)
# 0x80 ???
outReport[2] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2]
# further flags determining what changes this packet will perform
# 0x01 toggling microphone LED
# 0x02 toggling audio/mic mute
# 0x04 toggling LED strips on the sides of the touchpad
# 0x08 will actively turn all LEDs off? Convenience flag? (if so, third parties might not support it properly)
# 0x10 toggling white player indicator LEDs below touchpad
# 0x20 ???
# 0x40 adjustment of overall motor/effect power (index 37 - read note on triggers)
# 0x80 ???
outReport[2] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2]
outReport[3] = self.leftMotor # left low freq motor 0-255 # [3]
outReport[4] = self.rightMotor # right low freq motor 0-255 # [4]
outReport[3] = self.rightMotor # right low freq motor 0-255 # [3]
outReport[4] = self.leftMotor # left low freq motor 0-255 # [4]
# outReport[5] - outReport[8] audio related
# outReport[5] - outReport[8] audio related
# set Micrphone LED, setting doesnt effect microphone settings
outReport[9] = self.audio.microphone_led # [9]
# set Micrphone LED, setting doesnt effect microphone settings
outReport[9] = self.audio.microphone_led # [9]
outReport[10] = 0x10 if self.audio.microphone_mute is True else 0x00
outReport[10] = 0x10 if self.audio.microphone_mute is True else 0x00
# add right trigger mode + parameters to packet
outReport[11] = self.triggerR.mode.value
outReport[12] = self.triggerR.forces[0]
outReport[13] = self.triggerR.forces[1]
outReport[14] = self.triggerR.forces[2]
outReport[15] = self.triggerR.forces[3]
outReport[16] = self.triggerR.forces[4]
outReport[17] = self.triggerR.forces[5]
outReport[20] = self.triggerR.forces[6]
# add right trigger mode + parameters to packet
outReport[11] = self.triggerR.mode.value
outReport[12] = self.triggerR.forces[0]
outReport[13] = self.triggerR.forces[1]
outReport[14] = self.triggerR.forces[2]
outReport[15] = self.triggerR.forces[3]
outReport[16] = self.triggerR.forces[4]
outReport[17] = self.triggerR.forces[5]
outReport[20] = self.triggerR.forces[6]
outReport[22] = self.triggerL.mode.value
outReport[23] = self.triggerL.forces[0]
outReport[24] = self.triggerL.forces[1]
outReport[25] = self.triggerL.forces[2]
outReport[26] = self.triggerL.forces[3]
outReport[27] = self.triggerL.forces[4]
outReport[28] = self.triggerL.forces[5]
outReport[31] = self.triggerL.forces[6]
outReport[22] = self.triggerL.mode.value
outReport[23] = self.triggerL.forces[0]
outReport[24] = self.triggerL.forces[1]
outReport[25] = self.triggerL.forces[2]
outReport[26] = self.triggerL.forces[3]
outReport[27] = self.triggerL.forces[4]
outReport[28] = self.triggerL.forces[5]
outReport[31] = self.triggerL.forces[6]
outReport[39] = self.light.ledOption.value
outReport[42] = self.light.pulseOptions.value
outReport[43] = self.light.brightness.value
outReport[44] = self.light.playerNumber.value
outReport[45] = self.light.TouchpadColor[0]
outReport[46] = self.light.TouchpadColor[1]
outReport[47] = self.light.TouchpadColor[2]
outReport[39] = self.light.ledOption.value
outReport[42] = self.light.pulseOptions.value
outReport[43] = self.light.brightness.value
outReport[44] = self.light.playerNumber.value
outReport[45] = self.light.TouchpadColor[0]
outReport[46] = self.light.TouchpadColor[1]
outReport[47] = self.light.TouchpadColor[2]
elif self.conType == ConnectionType.BT:
outReport = [0] * self.output_report_length # create empty list with range of output report
# packet type
outReport[0] = self.OUTPUT_REPORT_BT # bt type
outReport[1] = 0x02
# flags determing what changes this packet will perform
# 0x01 set the main motors (also requires flag 0x02); setting this by itself will allow rumble to gracefully terminate and then re-enable audio haptics, whereas not setting it will kill the rumble instantly and re-enable audio haptics.
# 0x02 set the main motors (also requires flag 0x01; without bit 0x01 motors are allowed to time out without re-enabling audio haptics)
# 0x04 set the right trigger motor
# 0x08 set the left trigger motor
# 0x10 modification of audio volume
# 0x20 toggling of internal speaker while headset is connected
# 0x40 modification of microphone volume
outReport[2] = 0xff # [1]
# further flags determining what changes this packet will perform
# 0x01 toggling microphone LED
# 0x02 toggling audio/mic mute
# 0x04 toggling LED strips on the sides of the touchpad
# 0x08 will actively turn all LEDs off? Convenience flag? (if so, third parties might not support it properly)
# 0x10 toggling white player indicator LEDs below touchpad
# 0x20 ???
# 0x40 adjustment of overall motor/effect power (index 37 - read note on triggers)
# 0x80 ???
outReport[3] = 0x1 | 0x2 | 0x4 | 0x10 | 0x40 # [2]
outReport[4] = self.rightMotor # right low freq motor 0-255 # [3]
outReport[5] = self.leftMotor # left low freq motor 0-255 # [4]
# outReport[5] - outReport[8] audio related
# set Micrphone LED, setting doesnt effect microphone settings
outReport[10] = self.audio.microphone_led # [9]
outReport[11] = 0x10 if self.audio.microphone_mute is True else 0x00
# add right trigger mode + parameters to packet
outReport[12] = self.triggerR.mode.value
outReport[13] = self.triggerR.forces[0]
outReport[14] = self.triggerR.forces[1]
outReport[15] = self.triggerR.forces[2]
outReport[16] = self.triggerR.forces[3]
outReport[17] = self.triggerR.forces[4]
outReport[18] = self.triggerR.forces[5]
outReport[21] = self.triggerR.forces[6]
outReport[23] = self.triggerL.mode.value
outReport[24] = self.triggerL.forces[0]
outReport[25] = self.triggerL.forces[1]
outReport[26] = self.triggerL.forces[2]
outReport[27] = self.triggerL.forces[3]
outReport[28] = self.triggerL.forces[4]
outReport[29] = self.triggerL.forces[5]
outReport[32] = self.triggerL.forces[6]
outReport[40] = self.light.ledOption.value
outReport[43] = self.light.pulseOptions.value
outReport[44] = self.light.brightness.value
outReport[45] = self.light.playerNumber.value
outReport[46] = self.light.TouchpadColor[0]
outReport[47] = self.light.TouchpadColor[1]
outReport[48] = self.light.TouchpadColor[2]
crcChecksum = compute(outReport)
outReport[74] = (crcChecksum & 0x000000FF)
outReport[75] = (crcChecksum & 0x0000FF00) >> 8
outReport[76] = (crcChecksum & 0x00FF0000) >> 16
outReport[77] = (crcChecksum & 0xFF000000) >> 24
if self.verbose:
logger.debug(outReport)
@@ -760,3 +854,12 @@ class DSAccelerometer:
self.X = 0
self.Y = 0
self.Z = 0
class DSBattery:
"""
Class representing the Battery of the controller
"""
def __init__(self) -> None:
self.State = BatteryState.POWER_SUPPLY_STATUS_UNKNOWN
self.Level = 0

View File

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