dualsense-py 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. dualsense_py/__init__.py +7 -0
  2. dualsense_py/_version.py +24 -0
  3. dualsense_py/backends/__init__.py +3 -0
  4. dualsense_py/backends/backend.py +67 -0
  5. dualsense_py/backends/device_infos.py +270 -0
  6. dualsense_py/backends/hidapi/hidapi_backend.py +25 -0
  7. dualsense_py/backends/hidapi/hidapi_device_info.py +113 -0
  8. dualsense_py/backends/hidapi/in_report/__init__.py +6 -0
  9. dualsense_py/backends/hidapi/in_report/bt_01_in_report.py +11 -0
  10. dualsense_py/backends/hidapi/in_report/bt_31_in_report.py +25 -0
  11. dualsense_py/backends/hidapi/in_report/enums.py +8 -0
  12. dualsense_py/backends/hidapi/in_report/exceptions.py +21 -0
  13. dualsense_py/backends/hidapi/in_report/in_report.py +281 -0
  14. dualsense_py/backends/hidapi/in_report/usb_01_in_report.py +23 -0
  15. dualsense_py/backends/hidapi/out_report/__init__.py +4 -0
  16. dualsense_py/backends/hidapi/out_report/bt_01_out_report.py +8 -0
  17. dualsense_py/backends/hidapi/out_report/bt_31_out_report.py +70 -0
  18. dualsense_py/backends/hidapi/out_report/crc32.py +50 -0
  19. dualsense_py/backends/hidapi/out_report/enums.py +186 -0
  20. dualsense_py/backends/hidapi/out_report/out_report.py +65 -0
  21. dualsense_py/backends/hidapi/out_report/usb_01_out_report.py +96 -0
  22. dualsense_py/backends/hidapi/out_report/utils.py +6 -0
  23. dualsense_py/backends/sdl3/sdl3_backend.py +37 -0
  24. dualsense_py/backends/sdl3/sdl3_device_info.py +133 -0
  25. dualsense_py/change_detection.py +65 -0
  26. dualsense_py/dual_sense_controller.py +249 -0
  27. dualsense_py/mapping.py +139 -0
  28. dualsense_py/py.typed +0 -0
  29. dualsense_py/readable_value.py +114 -0
  30. dualsense_py/states/__init__.py +7 -0
  31. dualsense_py/states/accelerometer.py +7 -0
  32. dualsense_py/states/battery.py +7 -0
  33. dualsense_py/states/gyroscope.py +7 -0
  34. dualsense_py/states/joy_stick.py +6 -0
  35. dualsense_py/states/orientation.py +7 -0
  36. dualsense_py/states/touch_finger.py +8 -0
  37. dualsense_py/states/trigger_feedback.py +7 -0
  38. dualsense_py/utils.py +26 -0
  39. dualsense_py-0.3.0.dist-info/METADATA +245 -0
  40. dualsense_py-0.3.0.dist-info/RECORD +43 -0
  41. dualsense_py-0.3.0.dist-info/WHEEL +5 -0
  42. dualsense_py-0.3.0.dist-info/licenses/LICENSE.txt +21 -0
  43. dualsense_py-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ from .dual_sense_controller import DualSenseController
2
+ from .utils import get_all_controllers, get_all_dual_sense_controllers, get_all_xbox_360_controllers
3
+
4
+ try:
5
+ from ._version import __version__
6
+ except ImportError:
7
+ __version__ = "0.0.0.dev0"
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.3.0'
22
+ __version_tuple__ = version_tuple = (0, 3, 0)
23
+
24
+ __commit_id__ = commit_id = 'gfde1784d8'
@@ -0,0 +1,3 @@
1
+ from .backend import DeviceInfo
2
+ from .hidapi.hidapi_backend import HidAPIBackend
3
+ from .sdl3.sdl3_backend import SDL3Backend
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from typing import Generic, TypeVar, Any, Type
4
+
5
+
6
+ from .device_infos import DeviceInfo
7
+
8
+
9
+ _DeviceInfoType = TypeVar("_DeviceInfoType", bound=DeviceInfo[Any, Any])
10
+
11
+
12
+ class Backend(ABC, Generic[_DeviceInfoType]):
13
+ """
14
+ Abstract base class for backend implementations.
15
+ """
16
+
17
+ ActiveBackend: Type[Backend[Any]] | None = None
18
+
19
+ @staticmethod
20
+ @abstractmethod
21
+ def _get_available_devices() -> list[_DeviceInfoType]:
22
+ """
23
+ Open a connection to the device at the specified path.
24
+ """
25
+
26
+ @staticmethod
27
+ @abstractmethod
28
+ def _init() -> Type[Backend[_DeviceInfoType]]:
29
+ pass
30
+
31
+ @staticmethod
32
+ @abstractmethod
33
+ def _quit() -> None:
34
+ pass
35
+
36
+ __slots__ = ( )
37
+
38
+ @classmethod
39
+ def init(cls) -> None:
40
+ """
41
+ Initialize the backend.
42
+ """
43
+ if cls == Backend:
44
+ raise TypeError("Cannot initialize the base Backend class directly.")
45
+
46
+ Backend.ActiveBackend = cls._init()
47
+
48
+ @staticmethod
49
+ def get_available_devices() -> list[_DeviceInfoType]:
50
+ """
51
+ Open a connection to the device at the specified path.
52
+ """
53
+ if Backend.ActiveBackend is None:
54
+ raise TypeError("Cannot call get_available_devices() without initializing the backend.")
55
+
56
+ return Backend.ActiveBackend._get_available_devices()
57
+
58
+ @staticmethod
59
+ def quit() -> None:
60
+ """
61
+ Close the backend.
62
+ """
63
+ if Backend.ActiveBackend is None:
64
+ return
65
+
66
+ Backend.ActiveBackend._quit()
67
+ Backend.ActiveBackend = None
@@ -0,0 +1,270 @@
1
+ from __future__ import annotations
2
+ import time
3
+ from abc import abstractmethod
4
+ from typing import Generic, TypeVar, Final
5
+
6
+
7
+ from ..readable_value import ReadableValue, ButtonValue
8
+ from ..states import (
9
+ Accelerometer,
10
+ Battery,
11
+ Gyroscope,
12
+ JoyStick,
13
+ Orientation,
14
+ TouchFinger,
15
+ TriggerFeedback
16
+ )
17
+ ID = TypeVar("ID")
18
+ T = TypeVar("T")
19
+
20
+
21
+ class DeviceInfo(Generic[ID, T]):
22
+ """
23
+ Class to hold device information.
24
+ """
25
+
26
+ """def __init__(self, id: ID, orig_device_info: T):
27
+ self.id: ID = id
28
+ self.orig_device_info: T = orig_device_info"""
29
+
30
+ @abstractmethod
31
+ def open(self):
32
+ """
33
+ Open a connection to the device at the specified path.
34
+ """
35
+
36
+ @abstractmethod
37
+ def close(self):
38
+ """
39
+ Close the connection to the device.
40
+ """
41
+
42
+ @abstractmethod
43
+ def _read(self):
44
+ """
45
+ Read data from the device.
46
+ """
47
+
48
+ @abstractmethod
49
+ def write(self):
50
+ """
51
+ Write data to the device.
52
+ """
53
+
54
+ @abstractmethod
55
+ def set_led(self, r: int, g: int, b: int) -> None:
56
+ """
57
+ Set the LED color of the device.
58
+ """
59
+
60
+ __slots__ = (
61
+ "_orig_device_info",
62
+ "_read_time",
63
+ "_last_read_time",
64
+ "_square",
65
+ "_cross",
66
+ "_circle",
67
+ "_triangle",
68
+ "_dpad_up",
69
+ "_dpad_right",
70
+ "_dpad_down",
71
+ "_dpad_left",
72
+ "_l1",
73
+ "_r1",
74
+ "_l2",
75
+ "_r2",
76
+ "_share",
77
+ "_options",
78
+ "_l3",
79
+ "_r3",
80
+ "_ps",
81
+ "_touch",
82
+ "_mikrophone",
83
+ "_left_joy_stick",
84
+ "_right_joy_stick",
85
+ "_l2_trigger",
86
+ "_r2_trigger",
87
+ "_accelerometer",
88
+ "_gyroscope",
89
+ "_battery",
90
+ "_touch_finger_1",
91
+ "_touch_finger_2",
92
+ "_left_trigger_feedback",
93
+ "_right_trigger_feedback",
94
+ "_orientation",
95
+ )
96
+
97
+ def __init__(self, orig_device_info: T):
98
+ self._orig_device_info: Final[T] = orig_device_info
99
+ self._read_time: float = 0.0
100
+ self._last_read_time: float = 0.0
101
+
102
+ self._square: Final[ButtonValue] = ButtonValue()
103
+ self._cross: Final[ButtonValue] = ButtonValue()
104
+ self._circle: Final[ButtonValue] = ButtonValue()
105
+ self._triangle: Final[ButtonValue] = ButtonValue()
106
+ self._dpad_up: Final[ButtonValue] = ButtonValue()
107
+ self._dpad_right: Final[ButtonValue] = ButtonValue()
108
+ self._dpad_down: Final[ButtonValue] = ButtonValue()
109
+ self._dpad_left: Final[ButtonValue] = ButtonValue()
110
+ self._l1: Final[ButtonValue] = ButtonValue()
111
+ self._r1: Final[ButtonValue] = ButtonValue()
112
+ self._l2: Final[ButtonValue] = ButtonValue()
113
+ self._r2: Final[ButtonValue] = ButtonValue()
114
+ self._share: Final[ButtonValue] = ButtonValue()
115
+ self._options: Final[ButtonValue] = ButtonValue()
116
+ self._l3: Final[ButtonValue] = ButtonValue()
117
+ self._r3: Final[ButtonValue] = ButtonValue()
118
+ self._ps: Final[ButtonValue] = ButtonValue()
119
+ self._touch: Final[ButtonValue] = ButtonValue()
120
+ self._mikrophone: Final[ButtonValue] = ButtonValue()
121
+ self._left_joy_stick: Final[ReadableValue[JoyStick]] = ReadableValue[JoyStick](JoyStick())
122
+ self._right_joy_stick: Final[ReadableValue[JoyStick]] = ReadableValue[JoyStick](JoyStick())
123
+ self._l2_trigger: Final[ReadableValue[float]] = ReadableValue[float](0.0)
124
+ self._r2_trigger: Final[ReadableValue[float]] = ReadableValue[float](0.0)
125
+ self._accelerometer: Final[ReadableValue[Accelerometer]] = ReadableValue[Accelerometer](Accelerometer())
126
+ self._gyroscope: Final[ReadableValue[Gyroscope]] = ReadableValue[Gyroscope](Gyroscope())
127
+ self._battery: Final[ReadableValue[Battery]] = ReadableValue[Battery](Battery())
128
+ self._touch_finger_1: Final[ReadableValue[TouchFinger]] = ReadableValue[TouchFinger](TouchFinger())
129
+ self._touch_finger_2: Final[ReadableValue[TouchFinger]] = ReadableValue[TouchFinger](TouchFinger())
130
+ self._left_trigger_feedback: Final[ReadableValue[TriggerFeedback]] = ReadableValue[TriggerFeedback](TriggerFeedback())
131
+ self._right_trigger_feedback: Final[ReadableValue[TriggerFeedback]] = ReadableValue[TriggerFeedback](TriggerFeedback())
132
+ self._orientation: Final[ReadableValue[Orientation]] = ReadableValue[Orientation](Orientation())
133
+
134
+ def before_start(self):
135
+ """
136
+ Initialize the backend with the device information.
137
+ """
138
+ pass
139
+
140
+ def read(self):
141
+ """
142
+ Read data from the device.
143
+ """
144
+ self._read_time = time.perf_counter() * 1000.0
145
+ self._read()
146
+ self._last_read_time = self._read_time
147
+
148
+ @property
149
+ def square(self) -> ButtonValue:
150
+ return self._square
151
+
152
+ @property
153
+ def cross(self) -> ButtonValue:
154
+ return self._cross
155
+
156
+ @property
157
+ def circle(self) -> ButtonValue:
158
+ return self._circle
159
+
160
+ @property
161
+ def triangle(self) -> ButtonValue:
162
+ return self._triangle
163
+
164
+ @property
165
+ def dpad_up(self) -> ButtonValue:
166
+ return self._dpad_up
167
+
168
+ @property
169
+ def dpad_right(self) -> ButtonValue:
170
+ return self._dpad_right
171
+
172
+ @property
173
+ def dpad_down(self) -> ButtonValue:
174
+ return self._dpad_down
175
+
176
+ @property
177
+ def dpad_left(self) -> ButtonValue:
178
+ return self._dpad_left
179
+
180
+ @property
181
+ def l1(self) -> ButtonValue:
182
+ return self._l1
183
+
184
+ @property
185
+ def r1(self) -> ButtonValue:
186
+ return self._r1
187
+
188
+ @property
189
+ def l2(self) -> ButtonValue:
190
+ return self._l2
191
+
192
+ @property
193
+ def r2(self) -> ButtonValue:
194
+ return self._r2
195
+
196
+ @property
197
+ def share(self) -> ButtonValue:
198
+ return self._share
199
+
200
+ @property
201
+ def options(self) -> ButtonValue:
202
+ return self._options
203
+
204
+ @property
205
+ def l3(self) -> ButtonValue:
206
+ return self._l3
207
+
208
+ @property
209
+ def r3(self) -> ButtonValue:
210
+ return self._r3
211
+
212
+ @property
213
+ def ps(self) -> ButtonValue:
214
+ return self._ps
215
+
216
+ @property
217
+ def touch(self) -> ButtonValue:
218
+ return self._touch
219
+
220
+ @property
221
+ def mikrophone(self) -> ButtonValue:
222
+ return self._mikrophone
223
+
224
+ @property
225
+ def left_joy_stick(self) -> ReadableValue[JoyStick]:
226
+ return self._left_joy_stick
227
+
228
+ @property
229
+ def right_joy_stick(self) -> ReadableValue[JoyStick]:
230
+ return self._right_joy_stick
231
+
232
+ @property
233
+ def l2_trigger(self) -> ReadableValue[float]:
234
+ return self._l2_trigger
235
+
236
+ @property
237
+ def r2_trigger(self) -> ReadableValue[float]:
238
+ return self._r2_trigger
239
+
240
+ @property
241
+ def accelerometer(self) -> ReadableValue[Accelerometer]:
242
+ return self._accelerometer
243
+
244
+ @property
245
+ def gyroscope(self) -> ReadableValue[Gyroscope]:
246
+ return self._gyroscope
247
+
248
+ @property
249
+ def battery(self) -> ReadableValue[Battery]:
250
+ return self._battery
251
+
252
+ @property
253
+ def touch_finger_1(self) -> ReadableValue[TouchFinger]:
254
+ return self._touch_finger_1
255
+
256
+ @property
257
+ def touch_finger_2(self) -> ReadableValue[TouchFinger]:
258
+ return self._touch_finger_2
259
+
260
+ @property
261
+ def left_trigger_feedback(self) -> ReadableValue[TriggerFeedback]:
262
+ return self._left_trigger_feedback
263
+
264
+ @property
265
+ def right_trigger_feedback(self) -> ReadableValue[TriggerFeedback]:
266
+ return self._right_trigger_feedback
267
+
268
+ @property
269
+ def orientation(self) -> ReadableValue[Orientation]:
270
+ return self._orientation
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+ from typing import Type
3
+
4
+ from hidapi_py import get_all_device_infos
5
+
6
+ from .hidapi_device_info import HIDAPIDeviceInfo
7
+ from ..backend import Backend
8
+
9
+
10
+ SONY_VENDOR_ID: int = 0x054C
11
+ DS_PRODUCT_ID: int = 0x0CE6
12
+
13
+
14
+ class HidAPIBackend(Backend[HIDAPIDeviceInfo]):
15
+ @staticmethod
16
+ def _get_available_devices() -> list[HIDAPIDeviceInfo]:
17
+ return [HIDAPIDeviceInfo(hid_device_info) for hid_device_info in get_all_device_infos(SONY_VENDOR_ID, DS_PRODUCT_ID)]
18
+
19
+ @staticmethod
20
+ def _init() -> Type[HidAPIBackend]:
21
+ return HidAPIBackend
22
+
23
+ @staticmethod
24
+ def _quit() -> None:
25
+ pass
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+ from typing import Final
3
+
4
+ from hidapi_py import HidDeviceInfo, HidDevice
5
+
6
+ from ..backend import DeviceInfo
7
+ from .in_report import (
8
+ InReport,
9
+ Usb01InReport,
10
+ Bt01InReport,
11
+ Bt31InReport,
12
+ InReportLength,
13
+ InvalidInReportLengthException,
14
+ )
15
+ from .out_report import OutReport, Usb01OutReport, Bt01OutReport, Bt31OutReport
16
+ from ...mapping import (
17
+ uint8_value_mapping,
18
+ uint8_bit_to_bool,
19
+ create_battery,
20
+ create_touch_finger,
21
+ uint8_to_float,
22
+ create_accelerometer,
23
+ create_gyroscope,
24
+ create_trigger_feedback,
25
+ create_orientation,
26
+ create_joy_stick,
27
+ )
28
+
29
+
30
+ class HIDAPIDeviceInfo(DeviceInfo[str, HidDeviceInfo]):
31
+ __slots__ = (
32
+ "_hid_device",
33
+ "__in_report",
34
+ "__out_report",
35
+ )
36
+
37
+ def __init__(self, hid_device_info: HidDeviceInfo):
38
+ super().__init__(hid_device_info)
39
+ self._hid_device: Final[HidDevice] = HidDevice(hid_device_info.path)
40
+ self.__in_report: InReport | None = None
41
+ self.__out_report: OutReport | None = None
42
+
43
+ def before_start(self):
44
+ data = self._hid_device.read()
45
+
46
+ match len(data):
47
+ case InReportLength.USB_01:
48
+ self.__in_report = Usb01InReport()
49
+ self.__out_report = Usb01OutReport()
50
+ case InReportLength.BT_31:
51
+ self.__in_report = Bt31InReport()
52
+ self.__out_report = Bt31OutReport()
53
+ case InReportLength.BT_01:
54
+ self.__in_report = Bt01InReport()
55
+ self.__out_report = Bt01OutReport()
56
+ case _:
57
+ raise InvalidInReportLengthException
58
+
59
+ def open(self):
60
+ self._hid_device.open()
61
+
62
+ def close(self):
63
+ self._hid_device.close()
64
+
65
+ def _read(self):
66
+ if self.__in_report is None:
67
+ return
68
+
69
+ self._hid_device.read(self.__in_report.data)
70
+
71
+ self._square.set_value(uint8_bit_to_bool(self.__in_report.buttons_0, InReport.SQUARE_BIT))
72
+ self._cross.set_value(uint8_bit_to_bool(self.__in_report.buttons_0, InReport.CROSS_BIT))
73
+ self._circle.set_value(uint8_bit_to_bool(self.__in_report.buttons_0, InReport.CIRCLE_BIT))
74
+ self._triangle.set_value(uint8_bit_to_bool(self.__in_report.buttons_0, InReport.TRIANGLE_BIT))
75
+
76
+ for readable_value, value in zip(
77
+ [self._dpad_up, self._dpad_right, self._dpad_down, self._dpad_left],
78
+ uint8_value_mapping(self.__in_report.buttons_0 & 0b1111, InReport.DPAD_MAPPING)
79
+ ):
80
+ readable_value.set_value(value)
81
+
82
+ self._l1.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.L1_BIT))
83
+ self._r1.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.R1_BIT))
84
+ self._l2.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.L2_BIT))
85
+ self._r2.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.R2_BIT))
86
+ self._share.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.SHARE_BIT))
87
+ self._options.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.OPTIONS_BIT))
88
+ self._l3.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.L3_BIT))
89
+ self._r3.set_value(uint8_bit_to_bool(self.__in_report.buttons_1, InReport.R3_BIT))
90
+ self._ps.set_value(uint8_bit_to_bool(self.__in_report.buttons_2, InReport.PS_BIT))
91
+ self._touch.set_value(uint8_bit_to_bool(self.__in_report.buttons_2, InReport.TOUCH_BIT))
92
+ self._mikrophone.set_value(uint8_bit_to_bool(self.__in_report.buttons_2, InReport.MIKROPHONE_BIT))
93
+ self._left_joy_stick.set_value(create_joy_stick(self.__in_report.axes_0, self.__in_report.axes_1))
94
+ self._right_joy_stick.set_value(create_joy_stick(self.__in_report.axes_2, self.__in_report.axes_3))
95
+ self._l2_trigger.set_value(uint8_to_float(self.__in_report.axes_4, (0.0, 1.0)))
96
+ self._r2_trigger.set_value(uint8_to_float(self.__in_report.axes_5, (0.0, 1.0)))
97
+ self._accelerometer.set_value(create_accelerometer(x_0=self.__in_report.accel_x_0, x_1=self.__in_report.accel_x_1, y_0=self.__in_report.accel_y_0, y_1=self.__in_report.accel_y_1, z_0=self.__in_report.accel_z_0, z_1=self.__in_report.accel_z_1))
98
+ self._gyroscope.set_value(create_gyroscope(x_0=self.__in_report.gyro_x_0, x_1=self.__in_report.gyro_x_1, y_0=self.__in_report.gyro_y_0, y_1=self.__in_report.gyro_y_1, z_0=self.__in_report.gyro_z_0, z_1=self.__in_report.gyro_z_1))
99
+ self._battery.set_value(create_battery(self.__in_report.battery_0, self.__in_report.battery_1))
100
+ self._touch_finger_1.set_value(create_touch_finger(self.__in_report.touch_1_0, self.__in_report.touch_1_1, self.__in_report.touch_1_2, self.__in_report.touch_1_3))
101
+ self._touch_finger_2.set_value(create_touch_finger(self.__in_report.touch_2_0, self.__in_report.touch_2_1, self.__in_report.touch_2_2, self.__in_report.touch_2_3))
102
+ self._left_trigger_feedback.set_value(create_trigger_feedback(self.__in_report.left_trigger_feedback))
103
+ self._right_trigger_feedback.set_value(create_trigger_feedback(self.__in_report.right_trigger_feedback))
104
+ self._orientation.set_value(create_orientation(self._orientation.value, self._accelerometer.value, self._gyroscope.value, (self._read_time - self._last_read_time) if self._last_read_time else 0.0))
105
+
106
+ def write(self):
107
+ """
108
+ Write data to the device.
109
+ """
110
+ pass
111
+
112
+ def set_led(self, r: int, g: int, b: int) -> None:
113
+ pass
@@ -0,0 +1,6 @@
1
+ from .in_report import InReport, BytesLike
2
+ from .bt_01_in_report import Bt01InReport
3
+ from .bt_31_in_report import Bt31InReport
4
+ from .usb_01_in_report import Usb01InReport
5
+ from .enums import InReportLength
6
+ from .exceptions import AbstractBaseException, InvalidDeviceIndexException, InvalidInReportLengthException, NoDeviceDetectedException
@@ -0,0 +1,11 @@
1
+ from .in_report import InReport, BytesLike
2
+ from .enums import InReportLength
3
+
4
+
5
+ class Bt01InReport(InReport):
6
+ def __init__(self, data: BytesLike = bytearray(InReportLength.BT_01)):
7
+ super().__init__({
8
+ "axes_0": 0, "axes_1": 1, "axes_2": 2, "axes_3": 3,
9
+ "buttons_0": 4, "buttons_1": 5, "buttons_2": 6,
10
+ "axes_4": 7, "axes_5": 8
11
+ }, data=data)
@@ -0,0 +1,25 @@
1
+ from .in_report import InReport, BytesLike
2
+ from .enums import InReportLength
3
+
4
+
5
+ # ??? byte 0
6
+ # ??? byte 7?
7
+ # ??? byte 11
8
+ # ??? bytes 28-32
9
+ # ??? byte 41
10
+ # ??? bytes 44-52
11
+ # ??? bytes 55-76
12
+ class Bt31InReport(InReport):
13
+
14
+ def __init__(self, data: BytesLike = bytearray(InReportLength.BT_31)):
15
+ super().__init__({
16
+ "axes_0": 1, "axes_1": 2, "axes_2": 3, "axes_3": 4, "axes_4": 5, "axes_5": 6,
17
+ "buttons_0": 8, "buttons_1": 9, "buttons_2": 10,
18
+ "timestamp_0": 12, "timestamp_1": 13, "timestamp_2": 14, "timestamp_3": 15,
19
+ "gyro_x_0": 16, "gyro_x_1": 17, "gyro_y_0": 18, "gyro_y_1": 19, "gyro_z_0": 20, "gyro_z_1": 21,
20
+ "accel_x_0": 22, "accel_x_1": 23, "accel_y_0": 24, "accel_y_1": 25, "accel_z_0": 26, "accel_z_1": 27,
21
+ "touch_1_0": 33, "touch_1_1": 34, "touch_1_2": 35, "touch_1_3": 36,
22
+ "touch_2_0": 37, "touch_2_1": 38, "touch_2_2": 39, "touch_2_3": 40,
23
+ "right_trigger_feedback": 42, "left_trigger_feedback": 43,
24
+ "battery_0": 53, "battery_1": 54,
25
+ }, data=data)
@@ -0,0 +1,8 @@
1
+ from enum import Enum
2
+
3
+
4
+ class InReportLength(int, Enum):
5
+ DUMMY = 100
6
+ USB_01 = 64
7
+ BT_31 = 78
8
+ BT_01 = 10
@@ -0,0 +1,21 @@
1
+ from abc import ABC
2
+
3
+
4
+ class AbstractBaseException(ABC, Exception):
5
+ def __init__(self, msg: str):
6
+ super().__init__(msg)
7
+
8
+
9
+ class NoDeviceDetectedException(AbstractBaseException):
10
+ def __init__(self):
11
+ super().__init__('No DualSense device detected')
12
+
13
+
14
+ class InvalidDeviceIndexException(AbstractBaseException):
15
+ def __init__(self, idx: int):
16
+ super().__init__(f'Invalid DualSense device index given {idx}')
17
+
18
+
19
+ class InvalidInReportLengthException(AbstractBaseException):
20
+ def __init__(self):
21
+ super().__init__(f'Invalid connection type')