Add event system for states, added gyro and accelerometer
This commit is contained in:
@@ -1,15 +1,41 @@
|
||||
from pydualsense import *
|
||||
|
||||
|
||||
def cross_down(state):
|
||||
print(f'cross {state}')
|
||||
|
||||
|
||||
def circle_down(state):
|
||||
print(f'circle {state}')
|
||||
|
||||
|
||||
def dpad_down(state):
|
||||
print(f'dpad {state}')
|
||||
|
||||
|
||||
def joystick(stateX, stateY):
|
||||
print(f'lj {stateX} {stateY}')
|
||||
|
||||
|
||||
def gyro_changed(pitch, yaw, roll):
|
||||
print(f'{pitch}, {yaw}, {roll}')
|
||||
|
||||
|
||||
# create dualsense
|
||||
dualsense = pydualsense()
|
||||
# find device and initialize
|
||||
dualsense.init()
|
||||
|
||||
# add events handler functions
|
||||
dualsense.cross_pressed += cross_down
|
||||
dualsense.circle_pressed += circle_down
|
||||
dualsense.dpad_down += dpad_down
|
||||
dualsense.left_joystick_changed += joystick
|
||||
dualsense.gyro_changed += gyro_changed
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
|
@@ -26,11 +26,11 @@ class Brightness(IntFlag):
|
||||
|
||||
|
||||
class PlayerID(IntFlag):
|
||||
player1 = 4
|
||||
player2 = 10
|
||||
player3 = 21
|
||||
player4 = 27
|
||||
all = 31
|
||||
PLAYER_1 = 4
|
||||
PLAYER_2 = 10
|
||||
PLAYER_3 = 21
|
||||
PLAYER_4 = 27
|
||||
ALL = 31
|
||||
|
||||
|
||||
class TriggerModes(IntFlag):
|
||||
@@ -44,4 +44,3 @@ class TriggerModes(IntFlag):
|
||||
Pulse_B = 0x2 | 0x04
|
||||
Pulse_AB = 0x2 | 0x20 | 0x04
|
||||
Calibration = 0xFC
|
||||
|
||||
|
60
pydualsense/event_system.py
Normal file
60
pydualsense/event_system.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Event(object):
|
||||
"""
|
||||
Base class for the event driven system
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
initialise event system
|
||||
"""
|
||||
self._event_handler = []
|
||||
|
||||
def subscribe(self, fn):
|
||||
"""
|
||||
add a event subscription
|
||||
|
||||
Args:
|
||||
fn (function): _description_
|
||||
"""
|
||||
self._event_handler.append(fn)
|
||||
return self
|
||||
|
||||
def unsubscribe(self, fn):
|
||||
"""
|
||||
delete event subscription fn
|
||||
|
||||
Args:
|
||||
fn (function): _description_
|
||||
"""
|
||||
self._event_handler.remove(fn)
|
||||
return self
|
||||
|
||||
def __iadd__(self, fn):
|
||||
"""
|
||||
add event subscription fn
|
||||
|
||||
Args:
|
||||
fn (function): _description_
|
||||
"""
|
||||
self._event_handler.append(fn)
|
||||
return self
|
||||
|
||||
def __isub__(self, fn):
|
||||
"""
|
||||
delete event subscription fn
|
||||
|
||||
Args:
|
||||
fn (function): _description_
|
||||
"""
|
||||
self._event_handler.remove(fn)
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **keywargs):
|
||||
"""
|
||||
calls all event subscription functions
|
||||
"""
|
||||
for eventhandler in self._event_handler:
|
||||
eventhandler(*args, **keywargs)
|
@@ -1,5 +1,3 @@
|
||||
|
||||
# needed for python > 3.8
|
||||
import os
|
||||
import sys
|
||||
from sys import platform
|
||||
@@ -10,6 +8,8 @@ if platform.startswith('Windows') and sys.version_info >= (3, 8):
|
||||
import hidapi
|
||||
from .enums import (LedOptions, PlayerID, PulseOptions, TriggerModes, Brightness, ConnectionType) # type: ignore
|
||||
import threading
|
||||
from event_system import Event
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
class pydualsense:
|
||||
@@ -21,6 +21,55 @@ class pydualsense:
|
||||
self.leftMotor = 0
|
||||
self.rightMotor = 0
|
||||
|
||||
self.last_states = None
|
||||
|
||||
self.register_available_events()
|
||||
|
||||
def register_available_events(self):
|
||||
|
||||
# button events
|
||||
self.triangle_pressed = Event()
|
||||
self.circle_pressed = Event()
|
||||
self.cross_pressed = Event()
|
||||
self.square_pressed = Event()
|
||||
|
||||
# dpad events
|
||||
# TODO: add a event that sends the pressed key if any key is pressed
|
||||
# self.dpad_changed = Event()
|
||||
self.dpad_up = Event()
|
||||
self.dpad_down = Event()
|
||||
self.dpad_left = Event()
|
||||
self.dpad_right = Event()
|
||||
|
||||
# joystick
|
||||
self.left_joystick_changed = Event()
|
||||
self.right_joystick_changed = Event()
|
||||
|
||||
# trigger back buttons
|
||||
self.r1_changed = Event()
|
||||
self.r2_changed = Event()
|
||||
self.r3_changed = Event()
|
||||
|
||||
self.l1_changed = Event()
|
||||
self.l2_changed = Event()
|
||||
self.l3_changed = Event()
|
||||
|
||||
# misc
|
||||
self.ps_pressed = Event()
|
||||
self.touch_pressed = Event()
|
||||
self.microphone_pressed = Event()
|
||||
self.share_pressed = Event()
|
||||
self.option_pressed = Event()
|
||||
|
||||
# trackpad touch
|
||||
# handles 1 or 2 fingers
|
||||
#self.trackpad_frame_reported = Event()
|
||||
|
||||
# gyrometer events
|
||||
self.gyro_changed = Event()
|
||||
|
||||
self.accelerometer_changed = Event()
|
||||
|
||||
def init(self):
|
||||
"""initialize module and device states
|
||||
"""
|
||||
@@ -29,7 +78,6 @@ class pydualsense:
|
||||
self.audio = DSAudio() # ds audio setting
|
||||
self.triggerL = DSTrigger() # left trigger
|
||||
self.triggerR = DSTrigger() # right trigger
|
||||
|
||||
self.state = DSState() # controller states
|
||||
|
||||
if platform.startswith('Windows'):
|
||||
@@ -39,7 +87,6 @@ class pydualsense:
|
||||
self.input_report_length = 64
|
||||
self.output_report_length = 64
|
||||
|
||||
# thread for receiving and sending
|
||||
self.ds_thread = True
|
||||
self.report_thread = threading.Thread(target=self.sendReport)
|
||||
self.report_thread.start()
|
||||
@@ -200,12 +247,98 @@ class pydualsense:
|
||||
self.state.trackPadTouch1.X = ((inReport[39] & 0x0f) << 8) | (inReport[38])
|
||||
self.state.trackPadTouch1.Y = ((inReport[40]) << 4) | ((inReport[39] & 0xf0) >> 4)
|
||||
|
||||
# print(f'1Active = {self.state.trackPadTouch0.isActive}')
|
||||
# print(f'X1: {self.state.trackPadTouch0.X} Y2: {self.state.trackPadTouch0.Y}')
|
||||
# accelerometer
|
||||
self.state.accelerometer.X = int.from_bytes(([inReport[16], inReport[17]]), byteorder='little', signed=True)
|
||||
self.state.accelerometer.Y = int.from_bytes(([inReport[18], inReport[19]]), byteorder='little', signed=True)
|
||||
self.state.accelerometer.Z = int.from_bytes(([inReport[20], inReport[21]]), byteorder='little', signed=True)
|
||||
|
||||
# print(f'2Active = {self.state.trackPadTouch1.isActive}')
|
||||
# print(f'X2: {self.state.trackPadTouch1.X} Y2: {self.state.trackPadTouch1.Y}')
|
||||
# print(f'DPAD {self.state.DpadLeft} {self.state.DpadUp} {self.state.DpadRight} {self.state.DpadDown}')
|
||||
# gyrometer
|
||||
self.state.gyro.Pitch = int.from_bytes(([inReport[22], inReport[23]]), byteorder='little', signed=True)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
return
|
||||
|
||||
if self.state.circle != self.last_states.circle:
|
||||
self.circle_pressed(self.state.circle)
|
||||
|
||||
if self.state.cross != self.last_states.cross:
|
||||
self.cross_pressed(self.state.cross)
|
||||
|
||||
if self.state.triangle != self.last_states.triangle:
|
||||
self.triangle_pressed(self.state.triangle)
|
||||
|
||||
if self.state.square != self.last_states.square:
|
||||
self.square_pressed(self.state.square)
|
||||
|
||||
if self.state.DpadDown != self.last_states.DpadDown:
|
||||
self.dpad_down(self.state.DpadDown)
|
||||
|
||||
if self.state.DpadLeft != self.last_states.DpadLeft:
|
||||
self.dpad_left(self.state.DpadLeft)
|
||||
|
||||
if self.state.DpadRight != self.last_states.DpadRight:
|
||||
self.dpad_right(self.state.DpadRight)
|
||||
|
||||
if self.state.DpadUp != self.last_states.DpadUp:
|
||||
self.dpad_up(self.state.DpadUp)
|
||||
|
||||
if self.state.LX != self.last_states.LX or self.state.LY != self.last_states.LY:
|
||||
self.left_joystick_changed(self.state.LX, self.state.LY)
|
||||
|
||||
if self.state.RX != self.last_states.RX or self.state.RY != self.last_states.RY:
|
||||
self.right_joystick_changed(self.state.RX, self.state.RY)
|
||||
|
||||
if self.state.R1 != self.last_states.R1:
|
||||
self.r1_changed(self.state.R1)
|
||||
|
||||
if self.state.R2 != self.last_states.R2:
|
||||
self.r2_changed(self.state.R2)
|
||||
|
||||
if self.state.L1 != self.last_states.L1:
|
||||
self.l1_changed(self.state.L1)
|
||||
|
||||
if self.state.L2 != self.last_states.L2:
|
||||
self.l1_changed(self.state.L2)
|
||||
|
||||
if self.state.R3 != self.last_states.R3:
|
||||
self.r3_changed(self.state.R3)
|
||||
|
||||
if self.state.L3 != self.last_states.L3:
|
||||
self.l3_changed(self.state.L3)
|
||||
|
||||
if self.state.ps != self.last_states.ps:
|
||||
self.ps_pressed(self.state.ps)
|
||||
|
||||
if self.state.touchBtn != self.last_states.touchBtn:
|
||||
self.touch_pressed(self.state.touchBtn)
|
||||
|
||||
if self.state.micBtn != self.last_states.micBtn:
|
||||
self.microphone_pressed(self.state.micBtn)
|
||||
|
||||
if self.state.share != self.last_states.share:
|
||||
self.share_pressed(self.state.share)
|
||||
|
||||
if self.state.options != self.last_states.options:
|
||||
self.option_pressed(self.state.options)
|
||||
|
||||
if self.state.accelerometer.X != self.last_states.accelerometer.X or \
|
||||
self.state.accelerometer.Y != self.last_states.accelerometer.Y or \
|
||||
self.state.accelerometer.Z != self.last_states.accelerometer.Z:
|
||||
self.accelerometer_changed(self.state.accelerometer.X, self.state.accelerometer.Y, self.state.accelerometer.Z)
|
||||
|
||||
if self.state.gyro.Pitch != self.last_states.gyro.Pitch or \
|
||||
self.state.gyro.Yaw != self.last_states.gyro.Yaw or \
|
||||
self.state.gyro.Roll != self.last_states.gyro.Roll:
|
||||
self.gyro_changed(self.state.gyro.Pitch, self.state.gyro.Yaw, self.state.gyro.Roll)
|
||||
|
||||
# copy current state into ltemp object to check next cycle if a change occuret
|
||||
# and event trigger is needed
|
||||
|
||||
self.last_states = deepcopy(self.state) # copy current state into object to check next time
|
||||
|
||||
# TODO: implement gyrometer and accelerometer
|
||||
# TODO: control mouse with touchpad for fun as DS4Windows
|
||||
@@ -313,8 +446,11 @@ class DSState:
|
||||
self.L1, self.L2, self.L3, self.R1, self.R2, self.R3, self.R2Btn, self.L2Btn = False, False, False, False, False, False, False, False
|
||||
self.share, self.options, self.ps, self.touch1, self.touch2, self.touchBtn, self.touchRight, self.touchLeft = False, False, False, False, False, False, False, False
|
||||
self.touchFinger1, self.touchFinger2 = False, False
|
||||
self.micBtn = False
|
||||
self.RX, self.RY, self.LX, self.LY = 128, 128, 128, 128
|
||||
self.trackPadTouch0, self.trackPadTouch1 = DSTouchpad(), DSTouchpad()
|
||||
self.gyro = DSGyro()
|
||||
self.accelerometer = DSAccelerometer()
|
||||
|
||||
def setDPadState(self, dpad_state):
|
||||
if dpad_state == 0:
|
||||
@@ -370,7 +506,7 @@ class DSLight:
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
self.brightness: Brightness = Brightness.low # sets
|
||||
self.playerNumber: PlayerID = PlayerID.player1
|
||||
self.playerNumber: PlayerID = PlayerID.PLAYER_1
|
||||
self.ledOption: LedOptions = LedOptions.Both
|
||||
self.pulseOptions: PulseOptions = PulseOptions.Off
|
||||
self.TouchpadColor = (0, 0, 255)
|
||||
@@ -544,3 +680,23 @@ class DSTrigger:
|
||||
raise TypeError('Trigger mode parameter needs to be of type `TriggerModes`')
|
||||
|
||||
self.mode = mode
|
||||
|
||||
|
||||
class DSGyro:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Class represents the Gyro of the controller
|
||||
"""
|
||||
self.Pitch = 0
|
||||
self.Yaw = 0
|
||||
self.Roll = 0
|
||||
|
||||
|
||||
class DSAccelerometer:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Class represents the Accelerometer of the controller
|
||||
"""
|
||||
self.X = 0
|
||||
self.Y = 0
|
||||
self.Z = 0
|
Reference in New Issue
Block a user