diff --git a/examples/effects.py b/examples/effects.py index 4036d09..c0fd9c2 100644 --- a/examples/effects.py +++ b/examples/effects.py @@ -16,7 +16,8 @@ dualsense.triggerR.setForce(0, 200) dualsense.triggerR.setForce(1, 255) dualsense.triggerR.setForce(2, 175) -import time; time.sleep(3) - +# loop until r1 is pressed to feel effect +while not dualsense.state.R1: + ... # 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 56324a3..b05d42d 100644 --- a/examples/leds.py +++ b/examples/leds.py @@ -6,7 +6,7 @@ dualsense.init() # set color around touchpad to red dualsense.light.setColorI(255,0,0) # mute microphone -dualsense.audio.setMicrophoneMute(True) +dualsense.audio.setMicrophoneState(True) # set all player 1 indicator on dualsense.light.setPlayerID(PlayerID.player1) # sleep a little to see the result on the controller diff --git a/pydualsense/pydualsense.py b/pydualsense/pydualsense.py index da8f862..fe1ba3e 100644 --- a/pydualsense/pydualsense.py +++ b/pydualsense/pydualsense.py @@ -1,3 +1,4 @@ +import logging import os import sys from sys import platform @@ -12,12 +13,26 @@ from .event_system import Event from copy import deepcopy +logger = logging.getLogger() +FORMAT = '%(asctime)s %(message)s' +logging.basicConfig(format=FORMAT) +logger.setLevel(logging.INFO) + class pydualsense: - def __init__(self, verbose: bool = False) -> None:# + def __init__(self, verbose: bool = False) -> None: + """ + initialise the library but dont connect to the controller. call :func:`init() ` to connect to the controller + + Args: + verbose (bool, optional): display verbose out (debug prints of input and output). Defaults to False. + """ # TODO: maybe add a init function to not automatically allocate controller when class is declared self.verbose = verbose + if self.verbose: + logger.setLevel(logging.DEBUG) + self.leftMotor = 0 self.rightMotor = 0 @@ -25,7 +40,10 @@ class pydualsense: self.register_available_events() - def register_available_events(self): + def register_available_events(self) -> None: + """ + register all available events that can be used for the controller + """ # button events self.triangle_pressed = Event() @@ -70,8 +88,9 @@ class pydualsense: self.accelerometer_changed = Event() - def init(self): - """initialize module and device states + def init(self) -> None: + """ + initialize module and device states. Starts the sendReport background thread at the end """ self.device: hidapi.Device = self.__find_device() self.light = DSLight() # control led light of ds @@ -92,6 +111,14 @@ class pydualsense: self.report_thread.start() def determineConnectionType(self) -> ConnectionType: + """ + Determine the connection type of the controller. eg USB or BT. + + Currently only USB is supported. + + Returns: + ConnectionType: Detected connection type of the controller. + """ if self.device._device.input_report_length == 64: self.input_report_length = 64 @@ -102,17 +129,19 @@ class pydualsense: self.output_report_length = 78 return ConnectionType.BT - def close(self): + def close(self) -> None: """ Stops the report thread and closes the HID device """ + # TODO: reset trigger effect to default + self.ds_thread = False self.report_thread.join() self.device.close() def __find_device(self) -> hidapi.Device: """ - find HID device and open it + find HID dualsense device and open it Raises: Exception: HIDGuardian detected @@ -139,7 +168,7 @@ class pydualsense: dual_sense = hidapi.Device(vendor_id=detected_device.vendor_id, product_id=detected_device.product_id) return dual_sense - def setLeftMotor(self, intensity: int): + def setLeftMotor(self, intensity: int) -> None: """ set left motor rumble @@ -157,7 +186,7 @@ class pydualsense: raise Exception('maximum intensity is 255') self.leftMotor = intensity - def setRightMotor(self, intensity: int): + def setRightMotor(self, intensity: int) -> None: """ set right motor rumble @@ -175,14 +204,14 @@ class pydualsense: raise Exception('maximum intensity is 255') self.rightMotor = intensity - def sendReport(self): + def sendReport(self) -> None: """background thread handling the reading of the device and updating its states """ while self.ds_thread: # read data from the input report of the controller inReport = self.device.read(self.input_report_length) if self.verbose: - print(inReport) + logger.debug(inReport) # decrypt the packet and bind the inputs self.readInput(inReport) @@ -192,7 +221,7 @@ class pydualsense: # write the report to the device self.writeReport(outReport) - def readInput(self, inReport): + def readInput(self, inReport) -> None: """ read the input from the controller and assign the states @@ -262,6 +291,7 @@ class pydualsense: self.last_states = deepcopy(self.state) return + # send all events if neede if self.state.circle != self.last_states.circle: self.circle_pressed(self.state.circle) @@ -335,15 +365,15 @@ class pydualsense: 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 - + """ + copy current state into temp 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 - def writeReport(self, outReport): + def writeReport(self, outReport) -> None: """ write the report to the device @@ -352,7 +382,7 @@ class pydualsense: """ self.device.write(bytes(outReport)) - def prepareReport(self): + def prepareReport(self) -> None: """ prepare the output to be send to the controller @@ -421,12 +451,17 @@ class pydualsense: outReport[45] = self.light.TouchpadColor[0] outReport[46] = self.light.TouchpadColor[1] outReport[47] = self.light.TouchpadColor[2] + if self.verbose: - print(outReport) + logger.debug(outReport) + return outReport class DSTouchpad: + """ + Dualsense Touchpad class. Contains X and Y position of touch and if the touch isActive + """ def __init__(self) -> None: """ Class represents the Touchpad of the controller @@ -440,7 +475,9 @@ class DSTouchpad: class DSState: def __init__(self) -> None: - self.packerC = 0 + """ + All dualsense states (inputs) that can be read. Second method to check if a input is pressed. + """ self.square, self.triangle, self.circle, self.cross = False, False, False, False self.DpadUp, self.DpadDown, self.DpadLeft, self.DpadRight = False, False, False, False self.L1, self.L2, self.L3, self.R1, self.R2, self.R3, self.R2Btn, self.L2Btn = False, False, False, False, False, False, False, False @@ -452,7 +489,13 @@ class DSState: self.gyro = DSGyro() self.accelerometer = DSAccelerometer() - def setDPadState(self, dpad_state): + def setDPadState(self, dpad_state: int): + """ + Sets the dpad state variables according to the integers that was read from the controller + + Args: + dpad_state (int): integer number representing the dpad state + """ if dpad_state == 0: self.DpadUp = True self.DpadDown = False @@ -611,6 +654,9 @@ class DSLight: class DSAudio: def __init__(self) -> None: + """ + initialize the limited Audio features of the controller + """ self.microphone_mute = 0 self.microphone_led = 0 @@ -629,7 +675,16 @@ class DSAudio: raise TypeError('MicrophoneLED can only be a bool') self.microphone_led = value - def setMicrophoneMute(self, state): + def setMicrophoneState(self, state: bool): + """ + Set the microphone state and also sets the microphone led accordingle + + Args: + state (bool): desired state of the microphone + + Raises: + TypeError: state was not a bool + """ if not isinstance(state, bool): raise TypeError('state needs to be bool') @@ -639,6 +694,11 @@ class DSAudio: class DSTrigger: + """ + Dualsense trigger class. Allowes for multiple :class:`TriggerModes ` and multiple forces + + # TODO: make this interface more userfriendly so a developer knows what he is doing + """ def __init__(self) -> None: # trigger modes self.mode: TriggerModes = TriggerModes.Off @@ -683,20 +743,20 @@ class DSTrigger: class DSGyro: + """ + Class representing the Gyro2 of the controller + """ def __init__(self) -> None: - """ - Class represents the Gyro of the controller - """ self.Pitch = 0 self.Yaw = 0 self.Roll = 0 class DSAccelerometer: + """ + Class representing the Accelerometer of the controller + """ def __init__(self) -> None: - """ - Class represents the Accelerometer of the controller - """ self.X = 0 self.Y = 0 self.Z = 0 \ No newline at end of file