streamdeck 0.9.6__tar.gz → 0.9.7__tar.gz
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.
- {streamdeck-0.9.6 → streamdeck-0.9.7}/CHANGELOG +6 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/PKG-INFO +17 -3
- {streamdeck-0.9.6 → streamdeck-0.9.7}/README.md +5 -0
- streamdeck-0.9.7/VERSION +1 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/setup.py +1 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/DeviceManager.py +11 -14
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeck.py +58 -50
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/ImageHelpers/PILHelper.py +19 -17
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/ProductIDs.py +11 -6
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Transport/LibUSBHIDAPI.py +10 -13
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Transport/Transport.py +13 -13
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_animated.py +1 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_basic.py +2 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_neo.py +2 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_pedal.py +2 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_plus.py +2 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_tileimage.py +2 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/streamdeck.egg-info/PKG-INFO +17 -3
- streamdeck-0.9.6/VERSION +0 -1
- {streamdeck-0.9.6 → streamdeck-0.9.7}/ExampleDeck.jpg +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/LICENSE +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/MANIFEST.in +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/setup.cfg +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Apache License 2.0.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Attribution.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/CC BY-SA 3.0 License.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/CC0 License.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Elephant_Walking_animated.gif +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Exit.png +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Harold.jpg +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Pressed.png +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/RGB_color_space_animated_view.gif +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Released.png +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Roboto-Regular.ttf +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/Assets/Simple_CV_Joint_animated.gif +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckMini.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckNeo.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckOriginal.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckOriginalV2.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckPedal.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckPlus.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/StreamDeckXL.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Devices/__init__.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/ImageHelpers/__init__.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Transport/Dummy.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/Transport/__init__.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/StreamDeck/__init__.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/example_deckinfo.py +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/streamdeck.egg-info/SOURCES.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/streamdeck.egg-info/dependency_links.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/src/streamdeck.egg-info/top_level.txt +0 -0
- {streamdeck-0.9.6 → streamdeck-0.9.7}/test/test.py +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
Version 0.9.7:
|
|
2
|
+
- Added type hints to public APIs.
|
|
3
|
+
- Fixed leading whitespace in StreamDeck Plus serial number strings.
|
|
4
|
+
- Added support for new PID variant of the StreamDeck MK2.
|
|
5
|
+
- Added support for new StreamDeck 16-key, 32-key and scissor modules.
|
|
6
|
+
|
|
1
7
|
Version 0.9.6:
|
|
2
8
|
- Fixed StreamDeck+ returning 10 key states instead of the expected 8.
|
|
3
9
|
- Fixed StreamDeck+ dial push event not being detected correctly.
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: streamdeck
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.7
|
|
4
4
|
Summary: Library to control Elgato StreamDeck devices.
|
|
5
5
|
Home-page: https://github.com/abcminiuser/python-elgato-streamdeck
|
|
6
6
|
Author: Dean Camera
|
|
7
7
|
Author-email: dean@fourwalledcubicle.com
|
|
8
8
|
License: MIT
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: author-email
|
|
14
|
+
Dynamic: description
|
|
15
|
+
Dynamic: description-content-type
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: license
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
12
21
|
|
|
13
22
|
# Python Elgato Stream Deck Library
|
|
14
23
|
|
|
@@ -32,6 +41,7 @@ the images shown on each button, and read the current button states.
|
|
|
32
41
|
Currently the following StreamDeck products are supported in multiple hardware
|
|
33
42
|
variants:
|
|
34
43
|
|
|
44
|
+
* StreamDeck 16 Key, 32 Key and Scissor Modules
|
|
35
45
|
* StreamDeck Mini
|
|
36
46
|
* StreamDeck Neo
|
|
37
47
|
* StreamDeck Original
|
|
@@ -79,16 +89,20 @@ development and maintenance of this library:
|
|
|
79
89
|
- [dubstech](https://github.com/dubstech)
|
|
80
90
|
- [Giraut](https://github.com/Giraut)
|
|
81
91
|
- [impala454](https://github.com/impala454)
|
|
92
|
+
- [iPhoneAddict](https://github.com/iPhoneAddict)
|
|
82
93
|
- [itsusony](https://github.com/itsusony)
|
|
83
94
|
- [jakobbuis](https://github.com/jakobbuis)
|
|
84
95
|
- [jmudge14](https://github.com/jmudge14)
|
|
96
|
+
- [jonbloom](https://github.com/jonbloom)
|
|
85
97
|
- [Kalle-Wirsch](https://github.com/Kalle-Wirsch)
|
|
86
98
|
- [karstlok](https://github.com/karstlok)
|
|
87
99
|
- [Lewiscowles1986](https://github.com/Lewiscowles1986)
|
|
88
100
|
- [m-weigand](https://github.com/m-weigand)
|
|
101
|
+
- [mathben](https://github.com/mathben)
|
|
89
102
|
- [matrixinius](https://github.com/matrixinius)
|
|
90
103
|
- [phillco](https://github.com/phillco)
|
|
91
104
|
- [pointshader](https://github.com/pointshader)
|
|
105
|
+
- [pylogger-html](https://github.com/pylogger-html)
|
|
92
106
|
- [shanna](https://github.com/shanna)
|
|
93
107
|
- [spidererrol](https://github.com/Spidererrol)
|
|
94
108
|
- [spyoungtech](https://github.com/spyoungtech)
|
|
@@ -20,6 +20,7 @@ the images shown on each button, and read the current button states.
|
|
|
20
20
|
Currently the following StreamDeck products are supported in multiple hardware
|
|
21
21
|
variants:
|
|
22
22
|
|
|
23
|
+
* StreamDeck 16 Key, 32 Key and Scissor Modules
|
|
23
24
|
* StreamDeck Mini
|
|
24
25
|
* StreamDeck Neo
|
|
25
26
|
* StreamDeck Original
|
|
@@ -67,16 +68,20 @@ development and maintenance of this library:
|
|
|
67
68
|
- [dubstech](https://github.com/dubstech)
|
|
68
69
|
- [Giraut](https://github.com/Giraut)
|
|
69
70
|
- [impala454](https://github.com/impala454)
|
|
71
|
+
- [iPhoneAddict](https://github.com/iPhoneAddict)
|
|
70
72
|
- [itsusony](https://github.com/itsusony)
|
|
71
73
|
- [jakobbuis](https://github.com/jakobbuis)
|
|
72
74
|
- [jmudge14](https://github.com/jmudge14)
|
|
75
|
+
- [jonbloom](https://github.com/jonbloom)
|
|
73
76
|
- [Kalle-Wirsch](https://github.com/Kalle-Wirsch)
|
|
74
77
|
- [karstlok](https://github.com/karstlok)
|
|
75
78
|
- [Lewiscowles1986](https://github.com/Lewiscowles1986)
|
|
76
79
|
- [m-weigand](https://github.com/m-weigand)
|
|
80
|
+
- [mathben](https://github.com/mathben)
|
|
77
81
|
- [matrixinius](https://github.com/matrixinius)
|
|
78
82
|
- [phillco](https://github.com/phillco)
|
|
79
83
|
- [pointshader](https://github.com/pointshader)
|
|
84
|
+
- [pylogger-html](https://github.com/pylogger-html)
|
|
80
85
|
- [shanna](https://github.com/shanna)
|
|
81
86
|
- [spidererrol](https://github.com/Spidererrol)
|
|
82
87
|
- [spyoungtech](https://github.com/spyoungtech)
|
streamdeck-0.9.7/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.9.7
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# www.fourwalledcubicle.com
|
|
6
6
|
#
|
|
7
7
|
|
|
8
|
+
from .Devices.StreamDeck import StreamDeck
|
|
8
9
|
from .Devices.StreamDeckMini import StreamDeckMini
|
|
9
10
|
from .Devices.StreamDeckNeo import StreamDeckNeo
|
|
10
11
|
from .Devices.StreamDeckOriginal import StreamDeckOriginal
|
|
@@ -12,6 +13,7 @@ from .Devices.StreamDeckOriginalV2 import StreamDeckOriginalV2
|
|
|
12
13
|
from .Devices.StreamDeckXL import StreamDeckXL
|
|
13
14
|
from .Devices.StreamDeckPedal import StreamDeckPedal
|
|
14
15
|
from .Devices.StreamDeckPlus import StreamDeckPlus
|
|
16
|
+
from .Transport import Transport
|
|
15
17
|
from .Transport.Dummy import Dummy
|
|
16
18
|
from .Transport.LibUSBHIDAPI import LibUSBHIDAPI
|
|
17
19
|
from .ProductIDs import USBVendorIDs, USBProductIDs
|
|
@@ -33,18 +35,8 @@ class DeviceManager:
|
|
|
33
35
|
StreamDeck devices.
|
|
34
36
|
"""
|
|
35
37
|
|
|
36
|
-
USB_VID_ELGATO = 0x0fd9
|
|
37
|
-
USB_PID_STREAMDECK_ORIGINAL = 0x0060
|
|
38
|
-
USB_PID_STREAMDECK_ORIGINAL_V2 = 0x006d
|
|
39
|
-
USB_PID_STREAMDECK_MINI = 0x0063
|
|
40
|
-
USB_PID_STREAMDECK_NEO = 0x009a
|
|
41
|
-
USB_PID_STREAMDECK_XL = 0x006c
|
|
42
|
-
USB_PID_STREAMDECK_MK2 = 0x0080
|
|
43
|
-
USB_PID_STREAMDECK_PEDAL = 0x0086
|
|
44
|
-
USB_PID_STREAMDECK_PLUS = 0x0084
|
|
45
|
-
|
|
46
38
|
@staticmethod
|
|
47
|
-
def _get_transport(transport):
|
|
39
|
+
def _get_transport(transport: str | None):
|
|
48
40
|
"""
|
|
49
41
|
Creates a new HID transport instance from the given transport back-end
|
|
50
42
|
name. If no specific transport is supplied, an attempt to find an
|
|
@@ -87,15 +79,15 @@ class DeviceManager:
|
|
|
87
79
|
|
|
88
80
|
raise ProbeError("Probe failed to find any functional HID backend.", probe_errors)
|
|
89
81
|
|
|
90
|
-
def __init__(self, transport=None):
|
|
82
|
+
def __init__(self, transport: str | None = None):
|
|
91
83
|
"""
|
|
92
84
|
Creates a new StreamDeck DeviceManager, used to detect attached StreamDeck devices.
|
|
93
85
|
|
|
94
86
|
:param str transport: name of the the specific HID transport back-end to use, None to auto-probe.
|
|
95
87
|
"""
|
|
96
|
-
self.transport = self._get_transport(transport)
|
|
88
|
+
self.transport: Transport.Transport = self._get_transport(transport)
|
|
97
89
|
|
|
98
|
-
def enumerate(self):
|
|
90
|
+
def enumerate(self) -> list[StreamDeck]:
|
|
99
91
|
"""
|
|
100
92
|
Detect attached StreamDeck devices.
|
|
101
93
|
|
|
@@ -106,13 +98,18 @@ class DeviceManager:
|
|
|
106
98
|
products = [
|
|
107
99
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_ORIGINAL, StreamDeckOriginal),
|
|
108
100
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_ORIGINAL_V2, StreamDeckOriginalV2),
|
|
101
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MK2_SCISSOR, StreamDeckOriginalV2),
|
|
102
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MK2_MODULE, StreamDeckOriginalV2),
|
|
109
103
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MINI, StreamDeckMini),
|
|
110
104
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_NEO, StreamDeckNeo),
|
|
111
105
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_XL, StreamDeckXL),
|
|
112
106
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MK2, StreamDeckOriginalV2),
|
|
107
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MK2_V2, StreamDeckOriginalV2),
|
|
113
108
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_PEDAL, StreamDeckPedal),
|
|
114
109
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MINI_MK2, StreamDeckMini),
|
|
110
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MINI_MK2_MODULE, StreamDeckMini),
|
|
115
111
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_XL_V2, StreamDeckXL),
|
|
112
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_XL_V2_MODULE, StreamDeckXL),
|
|
116
113
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_PLUS, StreamDeckPlus),
|
|
117
114
|
]
|
|
118
115
|
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
import threading
|
|
9
9
|
import time
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
|
+
from collections.abc import Callable
|
|
11
12
|
from enum import Enum
|
|
13
|
+
from typing import Any, Iterable, TypeVar
|
|
12
14
|
|
|
13
|
-
from ..Transport.Transport import TransportError
|
|
15
|
+
from ..Transport.Transport import Transport, TransportError
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class TouchscreenEventType(Enum):
|
|
@@ -78,19 +80,24 @@ class StreamDeck(ABC):
|
|
|
78
80
|
DECK_VISUAL = False
|
|
79
81
|
DECK_TOUCH = False
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
self.read_thread = None
|
|
86
|
-
self.run_read_thread = False
|
|
87
|
-
self.read_poll_hz = 20
|
|
83
|
+
_Self = TypeVar('_Self', bound='StreamDeck')
|
|
84
|
+
KeyCallback = Callable[[_Self, int, bool], None] | None
|
|
85
|
+
DialCallback = Callable[[_Self, int, DialEventType, bool], None] | None
|
|
86
|
+
TouchScreenCallback = Callable[[_Self, TouchscreenEventType, Any], None] | None
|
|
88
87
|
|
|
89
|
-
|
|
90
|
-
self.
|
|
91
|
-
self.
|
|
88
|
+
def __init__(self, device: Transport.Device):
|
|
89
|
+
self.device: Transport.Device = device
|
|
90
|
+
self.last_key_states: list[bool] = [False] * (self.KEY_COUNT + self.TOUCH_KEY_COUNT)
|
|
91
|
+
self.last_dial_states: list[bool] = [False] * self.DIAL_COUNT
|
|
92
|
+
self.read_thread: threading.Thread | None = None
|
|
93
|
+
self.run_read_thread: bool = False
|
|
94
|
+
self.read_poll_hz: int = 20
|
|
92
95
|
|
|
93
|
-
self.
|
|
96
|
+
self.key_callback: StreamDeck.KeyCallback = None
|
|
97
|
+
self.dial_callback: StreamDeck.DialCallback = None
|
|
98
|
+
self.touchscreen_callback: StreamDeck.TouchScreenCallback = None
|
|
99
|
+
|
|
100
|
+
self.update_lock: threading.RLock = threading.RLock()
|
|
94
101
|
|
|
95
102
|
def __del__(self):
|
|
96
103
|
"""
|
|
@@ -124,7 +131,7 @@ class StreamDeck(ABC):
|
|
|
124
131
|
self.update_lock.release()
|
|
125
132
|
|
|
126
133
|
@abstractmethod
|
|
127
|
-
def _read_control_states(self):
|
|
134
|
+
def _read_control_states(self) -> None:
|
|
128
135
|
"""
|
|
129
136
|
Reads the raw key states from an attached StreamDeck.
|
|
130
137
|
|
|
@@ -133,7 +140,7 @@ class StreamDeck(ABC):
|
|
|
133
140
|
pass
|
|
134
141
|
|
|
135
142
|
@abstractmethod
|
|
136
|
-
def _reset_key_stream(self):
|
|
143
|
+
def _reset_key_stream(self) -> None:
|
|
137
144
|
"""
|
|
138
145
|
Sends a blank key report to the StreamDeck, resetting the key image
|
|
139
146
|
streamer in the device. This prevents previously started partial key
|
|
@@ -142,15 +149,16 @@ class StreamDeck(ABC):
|
|
|
142
149
|
"""
|
|
143
150
|
pass
|
|
144
151
|
|
|
145
|
-
def _extract_string(self, data):
|
|
152
|
+
def _extract_string(self, data: Iterable[int]) -> str:
|
|
146
153
|
"""
|
|
147
154
|
Extracts out a human-readable string from a collection of raw bytes,
|
|
148
|
-
removing any trailing whitespace or data after the first NUL
|
|
155
|
+
removing any trailing whitespace or data after and before the first NUL
|
|
156
|
+
byte.
|
|
149
157
|
"""
|
|
150
158
|
|
|
151
|
-
return str(bytes(data), 'ascii', 'replace').partition('\0')[0].
|
|
159
|
+
return str(bytes(data), 'ascii', 'replace').partition('\0')[0].strip()
|
|
152
160
|
|
|
153
|
-
def _read(self):
|
|
161
|
+
def _read(self) -> None:
|
|
154
162
|
"""
|
|
155
163
|
Read handler for the underlying transport, listening for control state
|
|
156
164
|
changes on the underlying device, caching the new states and firing off
|
|
@@ -200,7 +208,7 @@ class StreamDeck(ABC):
|
|
|
200
208
|
self.run_read_thread = False
|
|
201
209
|
self.close()
|
|
202
210
|
|
|
203
|
-
def _setup_reader(self, callback):
|
|
211
|
+
def _setup_reader(self, callback: Callable) -> None:
|
|
204
212
|
"""
|
|
205
213
|
Sets up the internal transport reader thread with the given callback,
|
|
206
214
|
for asynchronous processing of HID events from the device. If the thread
|
|
@@ -223,7 +231,7 @@ class StreamDeck(ABC):
|
|
|
223
231
|
self.read_thread.daemon = True
|
|
224
232
|
self.read_thread.start()
|
|
225
233
|
|
|
226
|
-
def open(self):
|
|
234
|
+
def open(self) -> None:
|
|
227
235
|
"""
|
|
228
236
|
Opens the device for input/output. This must be called prior to setting
|
|
229
237
|
or retrieving any device state.
|
|
@@ -235,7 +243,7 @@ class StreamDeck(ABC):
|
|
|
235
243
|
self._reset_key_stream()
|
|
236
244
|
self._setup_reader(self._read)
|
|
237
245
|
|
|
238
|
-
def close(self):
|
|
246
|
+
def close(self) -> None:
|
|
239
247
|
"""
|
|
240
248
|
Closes the device for input/output.
|
|
241
249
|
|
|
@@ -243,7 +251,7 @@ class StreamDeck(ABC):
|
|
|
243
251
|
"""
|
|
244
252
|
self.device.close()
|
|
245
253
|
|
|
246
|
-
def is_open(self):
|
|
254
|
+
def is_open(self) -> bool:
|
|
247
255
|
"""
|
|
248
256
|
Indicates if the StreamDeck device is currently open and ready for use.
|
|
249
257
|
|
|
@@ -252,7 +260,7 @@ class StreamDeck(ABC):
|
|
|
252
260
|
"""
|
|
253
261
|
return self.device.is_open()
|
|
254
262
|
|
|
255
|
-
def connected(self):
|
|
263
|
+
def connected(self) -> bool:
|
|
256
264
|
"""
|
|
257
265
|
Indicates if the physical StreamDeck device this instance is attached to
|
|
258
266
|
is still connected to the host.
|
|
@@ -262,7 +270,7 @@ class StreamDeck(ABC):
|
|
|
262
270
|
"""
|
|
263
271
|
return self.device.connected()
|
|
264
272
|
|
|
265
|
-
def vendor_id(self):
|
|
273
|
+
def vendor_id(self) -> int:
|
|
266
274
|
"""
|
|
267
275
|
Retrieves the vendor ID attached StreamDeck. This can be used
|
|
268
276
|
to determine the exact type of attached StreamDeck.
|
|
@@ -272,7 +280,7 @@ class StreamDeck(ABC):
|
|
|
272
280
|
"""
|
|
273
281
|
return self.device.vendor_id()
|
|
274
282
|
|
|
275
|
-
def product_id(self):
|
|
283
|
+
def product_id(self) -> int:
|
|
276
284
|
"""
|
|
277
285
|
Retrieves the product ID attached StreamDeck. This can be used
|
|
278
286
|
to determine the exact type of attached StreamDeck.
|
|
@@ -282,7 +290,7 @@ class StreamDeck(ABC):
|
|
|
282
290
|
"""
|
|
283
291
|
return self.device.product_id()
|
|
284
292
|
|
|
285
|
-
def id(self):
|
|
293
|
+
def id(self) -> str:
|
|
286
294
|
"""
|
|
287
295
|
Retrieves the physical ID of the attached StreamDeck. This can be used
|
|
288
296
|
to differentiate one StreamDeck from another.
|
|
@@ -292,7 +300,7 @@ class StreamDeck(ABC):
|
|
|
292
300
|
"""
|
|
293
301
|
return self.device.path()
|
|
294
302
|
|
|
295
|
-
def key_count(self):
|
|
303
|
+
def key_count(self) -> int:
|
|
296
304
|
"""
|
|
297
305
|
Retrieves number of physical buttons on the attached StreamDeck device.
|
|
298
306
|
|
|
@@ -301,7 +309,7 @@ class StreamDeck(ABC):
|
|
|
301
309
|
"""
|
|
302
310
|
return self.KEY_COUNT
|
|
303
311
|
|
|
304
|
-
def touch_key_count(self):
|
|
312
|
+
def touch_key_count(self) -> int:
|
|
305
313
|
"""
|
|
306
314
|
Retrieves number of touch buttons on the attached StreamDeck device.
|
|
307
315
|
|
|
@@ -310,7 +318,7 @@ class StreamDeck(ABC):
|
|
|
310
318
|
"""
|
|
311
319
|
return self.TOUCH_KEY_COUNT
|
|
312
320
|
|
|
313
|
-
def dial_count(self):
|
|
321
|
+
def dial_count(self) -> int:
|
|
314
322
|
"""
|
|
315
323
|
Retrieves number of physical dials on the attached StreamDeck device.
|
|
316
324
|
|
|
@@ -319,7 +327,7 @@ class StreamDeck(ABC):
|
|
|
319
327
|
"""
|
|
320
328
|
return self.DIAL_COUNT
|
|
321
329
|
|
|
322
|
-
def deck_type(self):
|
|
330
|
+
def deck_type(self) -> str:
|
|
323
331
|
"""
|
|
324
332
|
Retrieves the model of Stream Deck.
|
|
325
333
|
|
|
@@ -328,7 +336,7 @@ class StreamDeck(ABC):
|
|
|
328
336
|
"""
|
|
329
337
|
return self.DECK_TYPE
|
|
330
338
|
|
|
331
|
-
def is_visual(self):
|
|
339
|
+
def is_visual(self) -> bool:
|
|
332
340
|
"""
|
|
333
341
|
Returns whether the Stream Deck has a visual display output.
|
|
334
342
|
|
|
@@ -337,7 +345,7 @@ class StreamDeck(ABC):
|
|
|
337
345
|
"""
|
|
338
346
|
return self.DECK_VISUAL
|
|
339
347
|
|
|
340
|
-
def is_touch(self):
|
|
348
|
+
def is_touch(self) -> bool:
|
|
341
349
|
"""
|
|
342
350
|
Returns whether the Stream Deck can receive touch events
|
|
343
351
|
|
|
@@ -346,7 +354,7 @@ class StreamDeck(ABC):
|
|
|
346
354
|
"""
|
|
347
355
|
return self.DECK_TOUCH
|
|
348
356
|
|
|
349
|
-
def key_layout(self):
|
|
357
|
+
def key_layout(self) -> tuple[int, int]:
|
|
350
358
|
"""
|
|
351
359
|
Retrieves the physical button layout on the attached StreamDeck device.
|
|
352
360
|
|
|
@@ -414,7 +422,7 @@ class StreamDeck(ABC):
|
|
|
414
422
|
'rotation': self.SCREEN_ROTATION,
|
|
415
423
|
}
|
|
416
424
|
|
|
417
|
-
def set_poll_frequency(self, hz):
|
|
425
|
+
def set_poll_frequency(self, hz: int) -> None:
|
|
418
426
|
"""
|
|
419
427
|
Sets the frequency of the button polling reader thread, determining how
|
|
420
428
|
often the StreamDeck will be polled for button changes.
|
|
@@ -426,7 +434,7 @@ class StreamDeck(ABC):
|
|
|
426
434
|
"""
|
|
427
435
|
self.read_poll_hz = min(max(hz, 1), 1000)
|
|
428
436
|
|
|
429
|
-
def set_key_callback(self, callback):
|
|
437
|
+
def set_key_callback(self, callback: KeyCallback) -> None:
|
|
430
438
|
"""
|
|
431
439
|
Sets the callback function called each time a button on the StreamDeck
|
|
432
440
|
changes state (either pressed, or released).
|
|
@@ -445,7 +453,7 @@ class StreamDeck(ABC):
|
|
|
445
453
|
"""
|
|
446
454
|
self.key_callback = callback
|
|
447
455
|
|
|
448
|
-
def set_key_callback_async(self, async_callback, loop=None):
|
|
456
|
+
def set_key_callback_async(self, async_callback: KeyCallback, loop=None):
|
|
449
457
|
"""
|
|
450
458
|
Sets the asynchronous callback function called each time a button on the
|
|
451
459
|
StreamDeck changes state (either pressed, or released). The given
|
|
@@ -470,7 +478,7 @@ class StreamDeck(ABC):
|
|
|
470
478
|
|
|
471
479
|
self.set_key_callback(callback)
|
|
472
480
|
|
|
473
|
-
def set_dial_callback(self, callback):
|
|
481
|
+
def set_dial_callback(self, callback: DialCallback) -> None:
|
|
474
482
|
"""
|
|
475
483
|
Sets the callback function called each time there is an interaction
|
|
476
484
|
with a dial on the StreamDeck.
|
|
@@ -489,7 +497,7 @@ class StreamDeck(ABC):
|
|
|
489
497
|
"""
|
|
490
498
|
self.dial_callback = callback
|
|
491
499
|
|
|
492
|
-
def set_dial_callback_async(self, async_callback, loop=None):
|
|
500
|
+
def set_dial_callback_async(self, async_callback: DialCallback, loop=None) -> None:
|
|
493
501
|
"""
|
|
494
502
|
Sets the asynchronous callback function called each time there is an
|
|
495
503
|
interaction with a dial on the StreamDeck. The given callback should
|
|
@@ -514,7 +522,7 @@ class StreamDeck(ABC):
|
|
|
514
522
|
|
|
515
523
|
self.set_dial_callback(callback)
|
|
516
524
|
|
|
517
|
-
def set_touchscreen_callback(self, callback):
|
|
525
|
+
def set_touchscreen_callback(self, callback: TouchScreenCallback) -> None:
|
|
518
526
|
"""
|
|
519
527
|
Sets the callback function called each time there is an interaction
|
|
520
528
|
with a touchscreen on the StreamDeck.
|
|
@@ -533,7 +541,7 @@ class StreamDeck(ABC):
|
|
|
533
541
|
"""
|
|
534
542
|
self.touchscreen_callback = callback
|
|
535
543
|
|
|
536
|
-
def set_touchscreen_callback_async(self, async_callback, loop=None):
|
|
544
|
+
def set_touchscreen_callback_async(self, async_callback: TouchScreenCallback, loop=None) -> None:
|
|
537
545
|
"""
|
|
538
546
|
Sets the asynchronous callback function called each time there is an
|
|
539
547
|
interaction with the touchscreen on the StreamDeck. The given callback
|
|
@@ -558,7 +566,7 @@ class StreamDeck(ABC):
|
|
|
558
566
|
|
|
559
567
|
self.set_touchscreen_callback(callback)
|
|
560
568
|
|
|
561
|
-
def key_states(self):
|
|
569
|
+
def key_states(self) -> list[bool]:
|
|
562
570
|
"""
|
|
563
571
|
Retrieves the current states of the buttons on the StreamDeck.
|
|
564
572
|
|
|
@@ -569,7 +577,7 @@ class StreamDeck(ABC):
|
|
|
569
577
|
"""
|
|
570
578
|
return self.last_key_states
|
|
571
579
|
|
|
572
|
-
def dial_states(self):
|
|
580
|
+
def dial_states(self) -> list[bool]:
|
|
573
581
|
"""
|
|
574
582
|
Retrieves the current states of the dials (pressed or not) on the
|
|
575
583
|
Stream Deck
|
|
@@ -582,7 +590,7 @@ class StreamDeck(ABC):
|
|
|
582
590
|
return self.last_dial_states
|
|
583
591
|
|
|
584
592
|
@abstractmethod
|
|
585
|
-
def reset(self):
|
|
593
|
+
def reset(self) -> None:
|
|
586
594
|
"""
|
|
587
595
|
Resets the StreamDeck, clearing all button images and showing the
|
|
588
596
|
standby image.
|
|
@@ -590,7 +598,7 @@ class StreamDeck(ABC):
|
|
|
590
598
|
pass
|
|
591
599
|
|
|
592
600
|
@abstractmethod
|
|
593
|
-
def set_brightness(self, percent):
|
|
601
|
+
def set_brightness(self, percent: int | float) -> None:
|
|
594
602
|
"""
|
|
595
603
|
Sets the global screen brightness of the StreamDeck, across all the
|
|
596
604
|
physical buttons.
|
|
@@ -601,7 +609,7 @@ class StreamDeck(ABC):
|
|
|
601
609
|
pass
|
|
602
610
|
|
|
603
611
|
@abstractmethod
|
|
604
|
-
def get_serial_number(self):
|
|
612
|
+
def get_serial_number(self) -> str:
|
|
605
613
|
"""
|
|
606
614
|
Gets the serial number of the attached StreamDeck.
|
|
607
615
|
|
|
@@ -611,7 +619,7 @@ class StreamDeck(ABC):
|
|
|
611
619
|
pass
|
|
612
620
|
|
|
613
621
|
@abstractmethod
|
|
614
|
-
def get_firmware_version(self):
|
|
622
|
+
def get_firmware_version(self) -> str:
|
|
615
623
|
"""
|
|
616
624
|
Gets the firmware version of the attached StreamDeck.
|
|
617
625
|
|
|
@@ -621,7 +629,7 @@ class StreamDeck(ABC):
|
|
|
621
629
|
pass
|
|
622
630
|
|
|
623
631
|
@abstractmethod
|
|
624
|
-
def set_key_image(self, key, image):
|
|
632
|
+
def set_key_image(self, key: int, image: bytes) -> None:
|
|
625
633
|
"""
|
|
626
634
|
Sets the image of a button on the StreamDeck to the given image. The
|
|
627
635
|
image being set should be in the correct format for the device, as an
|
|
@@ -638,7 +646,7 @@ class StreamDeck(ABC):
|
|
|
638
646
|
pass
|
|
639
647
|
|
|
640
648
|
@abstractmethod
|
|
641
|
-
def set_touchscreen_image(self, image, x_pos=0, y_pos=0, width=0, height=0):
|
|
649
|
+
def set_touchscreen_image(self, image: bytes, x_pos: int = 0, y_pos: int = 0, width: int = 0, height: int = 0):
|
|
642
650
|
"""
|
|
643
651
|
Draws an image on the touchscreen in a certain position. The image
|
|
644
652
|
should be in the correct format for the devices, as an enumerable
|
|
@@ -658,7 +666,7 @@ class StreamDeck(ABC):
|
|
|
658
666
|
pass
|
|
659
667
|
|
|
660
668
|
@abstractmethod
|
|
661
|
-
def set_key_color(self, key, r, g, b):
|
|
669
|
+
def set_key_color(self, key: int, r: int, g: int, b: int) -> None:
|
|
662
670
|
"""
|
|
663
671
|
Sets the color of the touch buttons. These buttons are indexed
|
|
664
672
|
in order after the standard keys.
|
|
@@ -672,7 +680,7 @@ class StreamDeck(ABC):
|
|
|
672
680
|
pass
|
|
673
681
|
|
|
674
682
|
@abstractmethod
|
|
675
|
-
def set_screen_image(self, image):
|
|
683
|
+
def set_screen_image(self, image: bytes) -> None:
|
|
676
684
|
"""
|
|
677
685
|
Draws an image on the touchless screen of the StreamDeck.
|
|
678
686
|
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
import io
|
|
9
9
|
from PIL import Image
|
|
10
10
|
|
|
11
|
+
from ..Devices.StreamDeck import StreamDeck
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
def _create_image(image_format, background):
|
|
13
15
|
return Image.new("RGB", image_format['size'], background)
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
def _scale_image(image, image_format, margins=
|
|
18
|
+
def _scale_image(image, image_format, margins=(0, 0, 0, 0), background='black'):
|
|
17
19
|
if len(margins) != 4:
|
|
18
20
|
raise ValueError("Margins should be given as an array of four integers.")
|
|
19
21
|
|
|
@@ -47,12 +49,12 @@ def _to_native_format(image, image_format):
|
|
|
47
49
|
image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
|
48
50
|
|
|
49
51
|
# We want a compressed image in a given codec, convert.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
with io.BytesIO() as compressed_image:
|
|
53
|
+
image.save(compressed_image, image_format['format'], quality=100)
|
|
54
|
+
return compressed_image.getvalue()
|
|
53
55
|
|
|
54
56
|
|
|
55
|
-
def create_image(deck, background='black'):
|
|
57
|
+
def create_image(deck: StreamDeck, background: str = 'black') -> Image.Image:
|
|
56
58
|
"""
|
|
57
59
|
.. deprecated:: 0.9.5
|
|
58
60
|
Use :func:`~PILHelper.create_key_image` method instead.
|
|
@@ -60,7 +62,7 @@ def create_image(deck, background='black'):
|
|
|
60
62
|
return create_key_image(deck, background)
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
def create_key_image(deck, background='black'):
|
|
65
|
+
def create_key_image(deck: StreamDeck, background: str = 'black') -> Image.Image:
|
|
64
66
|
"""
|
|
65
67
|
Creates a new PIL Image with the correct image dimensions for the given
|
|
66
68
|
StreamDeck device's keys.
|
|
@@ -78,7 +80,7 @@ def create_key_image(deck, background='black'):
|
|
|
78
80
|
return _create_image(deck.key_image_format(), background)
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def create_touchscreen_image(deck, background='black'):
|
|
83
|
+
def create_touchscreen_image(deck: StreamDeck, background: str = 'black') -> Image.Image:
|
|
82
84
|
"""
|
|
83
85
|
Creates a new PIL Image with the correct image dimensions for the given
|
|
84
86
|
StreamDeck device's touchscreen.
|
|
@@ -96,10 +98,10 @@ def create_touchscreen_image(deck, background='black'):
|
|
|
96
98
|
return _create_image(deck.touchscreen_image_format(), background)
|
|
97
99
|
|
|
98
100
|
|
|
99
|
-
def create_screen_image(deck, background='black'):
|
|
101
|
+
def create_screen_image(deck: StreamDeck, background: str = 'black') -> Image.Image:
|
|
100
102
|
"""
|
|
101
103
|
Creates a new PIL Image with the correct image dimensions for the given
|
|
102
|
-
StreamDeck device's
|
|
104
|
+
StreamDeck device's screen.
|
|
103
105
|
|
|
104
106
|
.. seealso:: See :func:`~PILHelper.to_native_screen_format` method for converting a
|
|
105
107
|
PIL image instance to the native screen image format of a given
|
|
@@ -114,7 +116,7 @@ def create_screen_image(deck, background='black'):
|
|
|
114
116
|
return _create_image(deck.screen_image_format(), background)
|
|
115
117
|
|
|
116
118
|
|
|
117
|
-
def create_scaled_image(deck, image, margins=
|
|
119
|
+
def create_scaled_image(deck: StreamDeck, image: Image.Image, margins: tuple[int, int, int, int] = (0, 0, 0, 0), background: str = 'black') -> Image.Image:
|
|
118
120
|
"""
|
|
119
121
|
.. deprecated:: 0.9.5
|
|
120
122
|
Use :func:`~PILHelper.create_scaled_key_image` method instead.
|
|
@@ -122,7 +124,7 @@ def create_scaled_image(deck, image, margins=[0, 0, 0, 0], background='black'):
|
|
|
122
124
|
return create_scaled_key_image(deck, image, margins, background)
|
|
123
125
|
|
|
124
126
|
|
|
125
|
-
def create_scaled_key_image(deck, image, margins=
|
|
127
|
+
def create_scaled_key_image(deck: StreamDeck, image: Image.Image, margins: tuple[int, int, int, int] = (0, 0, 0, 0), background: str = 'black') -> Image.Image:
|
|
126
128
|
"""
|
|
127
129
|
Creates a new key image that contains a scaled version of a given image,
|
|
128
130
|
resized to best fit the given StreamDeck device's keys with the given
|
|
@@ -146,7 +148,7 @@ def create_scaled_key_image(deck, image, margins=[0, 0, 0, 0], background='black
|
|
|
146
148
|
return _scale_image(image, deck.key_image_format(), margins, background)
|
|
147
149
|
|
|
148
150
|
|
|
149
|
-
def create_scaled_touchscreen_image(deck, image, margins=
|
|
151
|
+
def create_scaled_touchscreen_image(deck: StreamDeck, image: Image.Image, margins: tuple[int, int, int, int] = (0, 0, 0, 0), background: str = 'black') -> Image.Image:
|
|
150
152
|
"""
|
|
151
153
|
Creates a new touchscreen image that contains a scaled version of a given image,
|
|
152
154
|
resized to best fit the given StreamDeck device's touchscreen with the given
|
|
@@ -170,7 +172,7 @@ def create_scaled_touchscreen_image(deck, image, margins=[0, 0, 0, 0], backgroun
|
|
|
170
172
|
return _scale_image(image, deck.touchscreen_image_format(), margins, background)
|
|
171
173
|
|
|
172
174
|
|
|
173
|
-
def create_scaled_screen_image(deck, image, margins=
|
|
175
|
+
def create_scaled_screen_image(deck: StreamDeck, image: Image.Image, margins: tuple[int, int, int, int] = (0, 0, 0, 0), background: str = 'black') -> Image.Image:
|
|
174
176
|
"""
|
|
175
177
|
Creates a new screen image that contains a scaled version of a given image,
|
|
176
178
|
resized to best fit the given StreamDeck device's screen with the given
|
|
@@ -194,7 +196,7 @@ def create_scaled_screen_image(deck, image, margins=[0, 0, 0, 0], background='bl
|
|
|
194
196
|
return _scale_image(image, deck.screen_image_format(), margins, background)
|
|
195
197
|
|
|
196
198
|
|
|
197
|
-
def to_native_format(deck, image):
|
|
199
|
+
def to_native_format(deck: StreamDeck, image: Image.Image) -> bytes:
|
|
198
200
|
"""
|
|
199
201
|
.. deprecated:: 0.9.5
|
|
200
202
|
Use :func:`~PILHelper.to_native_key_format` method instead.
|
|
@@ -202,7 +204,7 @@ def to_native_format(deck, image):
|
|
|
202
204
|
return to_native_key_format(deck, image)
|
|
203
205
|
|
|
204
206
|
|
|
205
|
-
def to_native_key_format(deck, image):
|
|
207
|
+
def to_native_key_format(deck: StreamDeck, image: Image.Image) -> bytes:
|
|
206
208
|
"""
|
|
207
209
|
Converts a given PIL image to the native key image format for a StreamDeck,
|
|
208
210
|
suitable for passing to :func:`~StreamDeck.set_key_image`.
|
|
@@ -219,7 +221,7 @@ def to_native_key_format(deck, image):
|
|
|
219
221
|
return _to_native_format(image, deck.key_image_format())
|
|
220
222
|
|
|
221
223
|
|
|
222
|
-
def to_native_touchscreen_format(deck, image):
|
|
224
|
+
def to_native_touchscreen_format(deck: StreamDeck, image: Image.Image) -> bytes:
|
|
223
225
|
"""
|
|
224
226
|
Converts a given PIL image to the native touchscreen image format for a StreamDeck,
|
|
225
227
|
suitable for passing to :func:`~StreamDeck.set_touchscreen_image`.
|
|
@@ -236,7 +238,7 @@ def to_native_touchscreen_format(deck, image):
|
|
|
236
238
|
return _to_native_format(image, deck.touchscreen_image_format())
|
|
237
239
|
|
|
238
240
|
|
|
239
|
-
def to_native_screen_format(deck, image):
|
|
241
|
+
def to_native_screen_format(deck: StreamDeck, image: Image.Image) -> bytes:
|
|
240
242
|
"""
|
|
241
243
|
Converts a given PIL image to the native screen image format for a StreamDeck,
|
|
242
244
|
suitable for passing to :func:`~StreamDeck.set_screen_image`.
|
|
@@ -19,13 +19,18 @@ class USBProductIDs:
|
|
|
19
19
|
USB Product IDs for known StreamDeck devices.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
USB_PID_STREAMDECK_ORIGINAL = 0x0060
|
|
23
|
-
USB_PID_STREAMDECK_ORIGINAL_V2 = 0x006d
|
|
24
22
|
USB_PID_STREAMDECK_MINI = 0x0063
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
USB_PID_STREAMDECK_XL_V2 = 0x008f
|
|
23
|
+
USB_PID_STREAMDECK_MINI_MK2 = 0x0090
|
|
24
|
+
USB_PID_STREAMDECK_MINI_MK2_MODULE = 0x00b8
|
|
28
25
|
USB_PID_STREAMDECK_MK2 = 0x0080
|
|
26
|
+
USB_PID_STREAMDECK_MK2_MODULE = 0x00b9
|
|
27
|
+
USB_PID_STREAMDECK_MK2_SCISSOR = 0x00a5
|
|
28
|
+
USB_PID_STREAMDECK_MK2_V2 = 0x00B9
|
|
29
|
+
USB_PID_STREAMDECK_NEO = 0x009a
|
|
30
|
+
USB_PID_STREAMDECK_ORIGINAL = 0x0060
|
|
31
|
+
USB_PID_STREAMDECK_ORIGINAL_V2 = 0x006d
|
|
29
32
|
USB_PID_STREAMDECK_PEDAL = 0x0086
|
|
30
|
-
USB_PID_STREAMDECK_MINI_MK2 = 0x0090
|
|
31
33
|
USB_PID_STREAMDECK_PLUS = 0x0084
|
|
34
|
+
USB_PID_STREAMDECK_XL = 0x006c
|
|
35
|
+
USB_PID_STREAMDECK_XL_V2 = 0x008f
|
|
36
|
+
USB_PID_STREAMDECK_XL_V2_MODULE = 0x00ba
|
|
@@ -213,7 +213,6 @@ class LibUSBHIDAPI(Transport):
|
|
|
213
213
|
:rtype: Handle
|
|
214
214
|
:return: Device handle if opened successfully, None if open failed.
|
|
215
215
|
"""
|
|
216
|
-
|
|
217
216
|
with self.mutex:
|
|
218
217
|
if type(path) is not bytes:
|
|
219
218
|
path = bytes(path, 'utf-8')
|
|
@@ -250,10 +249,10 @@ class LibUSBHIDAPI(Transport):
|
|
|
250
249
|
:rtype: int
|
|
251
250
|
:return: Number of bytes successfully sent to the device.
|
|
252
251
|
"""
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
raise TransportError("No HID device.")
|
|
252
|
+
if not handle:
|
|
253
|
+
raise TransportError("No HID device.")
|
|
256
254
|
|
|
255
|
+
with self.mutex:
|
|
257
256
|
result = self.hidapi.hid_send_feature_report(handle, bytes(data), len(data))
|
|
258
257
|
|
|
259
258
|
if result < 0:
|
|
@@ -274,6 +273,8 @@ class LibUSBHIDAPI(Transport):
|
|
|
274
273
|
first byte of the report will be the Report ID of the
|
|
275
274
|
report that was read.
|
|
276
275
|
"""
|
|
276
|
+
if not handle:
|
|
277
|
+
raise TransportError("No HID device.")
|
|
277
278
|
|
|
278
279
|
# We may need to oversize our read due a bug in some versions of
|
|
279
280
|
# HIDAPI. Only applied on Mac systems, as this will cause other
|
|
@@ -284,9 +285,6 @@ class LibUSBHIDAPI(Transport):
|
|
|
284
285
|
data[0] = report_id
|
|
285
286
|
|
|
286
287
|
with self.mutex:
|
|
287
|
-
if not handle:
|
|
288
|
-
raise TransportError("No HID device.")
|
|
289
|
-
|
|
290
288
|
result = self.hidapi.hid_get_feature_report(handle, data, len(data))
|
|
291
289
|
|
|
292
290
|
if result < 0:
|
|
@@ -313,10 +311,10 @@ class LibUSBHIDAPI(Transport):
|
|
|
313
311
|
:rtype: int
|
|
314
312
|
:return: Number of bytes successfully sent to the device.
|
|
315
313
|
"""
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
raise TransportError("No HID device.")
|
|
314
|
+
if not handle:
|
|
315
|
+
raise TransportError("No HID device.")
|
|
319
316
|
|
|
317
|
+
with self.mutex:
|
|
320
318
|
result = self.hidapi.hid_write(handle, bytes(data), len(data))
|
|
321
319
|
|
|
322
320
|
if result < 0:
|
|
@@ -336,13 +334,12 @@ class LibUSBHIDAPI(Transport):
|
|
|
336
334
|
first byte of the report will be the Report ID of the
|
|
337
335
|
report that was read.
|
|
338
336
|
"""
|
|
337
|
+
if not handle:
|
|
338
|
+
raise TransportError("No HID device.")
|
|
339
339
|
|
|
340
340
|
data = ctypes.create_string_buffer(length)
|
|
341
341
|
|
|
342
342
|
with self.mutex:
|
|
343
|
-
if not handle:
|
|
344
|
-
raise TransportError("No HID device.")
|
|
345
|
-
|
|
346
343
|
result = self.hidapi.hid_read(handle, data, len(data))
|
|
347
344
|
|
|
348
345
|
if result < 0:
|
|
@@ -31,7 +31,7 @@ class Transport(ABC):
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
@abstractmethod
|
|
34
|
-
def open(self):
|
|
34
|
+
def open(self) -> None:
|
|
35
35
|
"""
|
|
36
36
|
Opens the device for input/output. This must be called prior to
|
|
37
37
|
sending or receiving any reports.
|
|
@@ -42,7 +42,7 @@ class Transport(ABC):
|
|
|
42
42
|
pass
|
|
43
43
|
|
|
44
44
|
@abstractmethod
|
|
45
|
-
def close(self):
|
|
45
|
+
def close(self) -> None:
|
|
46
46
|
"""
|
|
47
47
|
Closes the device for input/output.
|
|
48
48
|
|
|
@@ -52,7 +52,7 @@ class Transport(ABC):
|
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
54
|
@abstractmethod
|
|
55
|
-
def is_open(self):
|
|
55
|
+
def is_open(self) -> bool:
|
|
56
56
|
"""
|
|
57
57
|
Indicates if the physical device object this instance is attached
|
|
58
58
|
to has been opened by the host.
|
|
@@ -63,7 +63,7 @@ class Transport(ABC):
|
|
|
63
63
|
pass
|
|
64
64
|
|
|
65
65
|
@abstractmethod
|
|
66
|
-
def connected(self):
|
|
66
|
+
def connected(self) -> bool:
|
|
67
67
|
"""
|
|
68
68
|
Indicates if the physical device object this instance is attached
|
|
69
69
|
to is still connected to the host.
|
|
@@ -74,7 +74,7 @@ class Transport(ABC):
|
|
|
74
74
|
pass
|
|
75
75
|
|
|
76
76
|
@abstractmethod
|
|
77
|
-
def path(self):
|
|
77
|
+
def path(self) -> str:
|
|
78
78
|
"""
|
|
79
79
|
Retrieves the logical path of the attached device within the
|
|
80
80
|
current system. This can be used to uniquely differentiate one
|
|
@@ -86,7 +86,7 @@ class Transport(ABC):
|
|
|
86
86
|
pass
|
|
87
87
|
|
|
88
88
|
@abstractmethod
|
|
89
|
-
def vendor_id(self):
|
|
89
|
+
def vendor_id(self) -> int:
|
|
90
90
|
"""
|
|
91
91
|
Retrieves the vendor ID value of the attached device.
|
|
92
92
|
|
|
@@ -96,7 +96,7 @@ class Transport(ABC):
|
|
|
96
96
|
pass
|
|
97
97
|
|
|
98
98
|
@abstractmethod
|
|
99
|
-
def product_id(self):
|
|
99
|
+
def product_id(self) -> int:
|
|
100
100
|
"""
|
|
101
101
|
Retrieves the product ID value of the attached device.
|
|
102
102
|
|
|
@@ -106,7 +106,7 @@ class Transport(ABC):
|
|
|
106
106
|
pass
|
|
107
107
|
|
|
108
108
|
@abstractmethod
|
|
109
|
-
def write_feature(self, payload):
|
|
109
|
+
def write_feature(self, payload: bytes) -> int:
|
|
110
110
|
"""
|
|
111
111
|
Sends a HID Feature report to the open HID device.
|
|
112
112
|
|
|
@@ -121,7 +121,7 @@ class Transport(ABC):
|
|
|
121
121
|
pass
|
|
122
122
|
|
|
123
123
|
@abstractmethod
|
|
124
|
-
def read_feature(self, report_id, length):
|
|
124
|
+
def read_feature(self, report_id: int, length: int) -> bytes:
|
|
125
125
|
"""
|
|
126
126
|
Reads a HID Feature report from the open HID device.
|
|
127
127
|
|
|
@@ -136,7 +136,7 @@ class Transport(ABC):
|
|
|
136
136
|
pass
|
|
137
137
|
|
|
138
138
|
@abstractmethod
|
|
139
|
-
def write(self, payload):
|
|
139
|
+
def write(self, payload: bytes) -> int:
|
|
140
140
|
"""
|
|
141
141
|
Sends a HID Out report to the open HID device.
|
|
142
142
|
|
|
@@ -151,7 +151,7 @@ class Transport(ABC):
|
|
|
151
151
|
pass
|
|
152
152
|
|
|
153
153
|
@abstractmethod
|
|
154
|
-
def read(self, length):
|
|
154
|
+
def read(self, length: int) -> bytes:
|
|
155
155
|
"""
|
|
156
156
|
Performs a blocking read of a HID In report from the open HID device.
|
|
157
157
|
|
|
@@ -166,7 +166,7 @@ class Transport(ABC):
|
|
|
166
166
|
|
|
167
167
|
@staticmethod
|
|
168
168
|
@abstractmethod
|
|
169
|
-
def probe():
|
|
169
|
+
def probe() -> None:
|
|
170
170
|
"""
|
|
171
171
|
Attempts to determine if the back-end is installed and usable. It is
|
|
172
172
|
expected that probe failures throw exceptions detailing their exact
|
|
@@ -175,7 +175,7 @@ class Transport(ABC):
|
|
|
175
175
|
pass
|
|
176
176
|
|
|
177
177
|
@abstractmethod
|
|
178
|
-
def enumerate(self, vid, pid):
|
|
178
|
+
def enumerate(self, vid: int, pid: int) -> list[Device]:
|
|
179
179
|
"""
|
|
180
180
|
Enumerates all available devices on the system using the current
|
|
181
181
|
transport back-end.
|
|
@@ -16,6 +16,7 @@ import threading
|
|
|
16
16
|
from PIL import Image, ImageDraw, ImageFont
|
|
17
17
|
from StreamDeck.DeviceManager import DeviceManager
|
|
18
18
|
from StreamDeck.ImageHelpers import PILHelper
|
|
19
|
+
from StreamDeck.Transport.Transport import TransportError
|
|
19
20
|
|
|
20
21
|
# Folder location of image assets used by this example.
|
|
21
22
|
ASSETS_PATH = os.path.join(os.path.dirname(__file__), "Assets")
|
|
@@ -140,5 +141,5 @@ if __name__ == "__main__":
|
|
|
140
141
|
for t in threading.enumerate():
|
|
141
142
|
try:
|
|
142
143
|
t.join()
|
|
143
|
-
except RuntimeError:
|
|
144
|
+
except (TransportError, RuntimeError):
|
|
144
145
|
pass
|
|
@@ -17,6 +17,7 @@ import random
|
|
|
17
17
|
from PIL import Image, ImageDraw, ImageFont
|
|
18
18
|
from StreamDeck.DeviceManager import DeviceManager
|
|
19
19
|
from StreamDeck.ImageHelpers import PILHelper
|
|
20
|
+
from StreamDeck.Transport.Transport import TransportError
|
|
20
21
|
|
|
21
22
|
# Folder location of image assets used by this example.
|
|
22
23
|
ASSETS_PATH = os.path.join(os.path.dirname(__file__), "Assets")
|
|
@@ -166,5 +167,5 @@ if __name__ == "__main__":
|
|
|
166
167
|
for t in threading.enumerate():
|
|
167
168
|
try:
|
|
168
169
|
t.join()
|
|
169
|
-
except RuntimeError:
|
|
170
|
+
except (TransportError, RuntimeError):
|
|
170
171
|
pass
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import threading
|
|
15
15
|
|
|
16
16
|
from StreamDeck.DeviceManager import DeviceManager
|
|
17
|
+
from StreamDeck.Transport.Transport import TransportError
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def key_change_callback(deck, key, state):
|
|
@@ -40,5 +41,5 @@ if __name__ == "__main__":
|
|
|
40
41
|
for t in threading.enumerate():
|
|
41
42
|
try:
|
|
42
43
|
t.join()
|
|
43
|
-
except RuntimeError:
|
|
44
|
+
except (TransportError, RuntimeError):
|
|
44
45
|
pass
|
|
@@ -14,6 +14,7 @@ import io
|
|
|
14
14
|
from PIL import Image
|
|
15
15
|
from StreamDeck.DeviceManager import DeviceManager
|
|
16
16
|
from StreamDeck.Devices.StreamDeck import DialEventType, TouchscreenEventType
|
|
17
|
+
from StreamDeck.Transport.Transport import TransportError
|
|
17
18
|
|
|
18
19
|
# Folder location of image assets used by this example.
|
|
19
20
|
ASSETS_PATH = os.path.join(os.path.dirname(__file__), "Assets")
|
|
@@ -131,5 +132,5 @@ if __name__ == "__main__":
|
|
|
131
132
|
for t in threading.enumerate():
|
|
132
133
|
try:
|
|
133
134
|
t.join()
|
|
134
|
-
except RuntimeError:
|
|
135
|
+
except (TransportError, RuntimeError):
|
|
135
136
|
pass
|
|
@@ -17,6 +17,7 @@ import threading
|
|
|
17
17
|
from PIL import Image, ImageOps
|
|
18
18
|
from StreamDeck.DeviceManager import DeviceManager
|
|
19
19
|
from StreamDeck.ImageHelpers import PILHelper
|
|
20
|
+
from StreamDeck.Transport.Transport import TransportError
|
|
20
21
|
|
|
21
22
|
# Folder location of image assets used by this example.
|
|
22
23
|
ASSETS_PATH = os.path.join(os.path.dirname(__file__), "Assets")
|
|
@@ -144,5 +145,5 @@ if __name__ == "__main__":
|
|
|
144
145
|
for t in threading.enumerate():
|
|
145
146
|
try:
|
|
146
147
|
t.join()
|
|
147
|
-
except RuntimeError:
|
|
148
|
+
except (TransportError, RuntimeError):
|
|
148
149
|
pass
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: streamdeck
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.7
|
|
4
4
|
Summary: Library to control Elgato StreamDeck devices.
|
|
5
5
|
Home-page: https://github.com/abcminiuser/python-elgato-streamdeck
|
|
6
6
|
Author: Dean Camera
|
|
7
7
|
Author-email: dean@fourwalledcubicle.com
|
|
8
8
|
License: MIT
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: author-email
|
|
14
|
+
Dynamic: description
|
|
15
|
+
Dynamic: description-content-type
|
|
16
|
+
Dynamic: home-page
|
|
17
|
+
Dynamic: license
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
Dynamic: requires-python
|
|
20
|
+
Dynamic: summary
|
|
12
21
|
|
|
13
22
|
# Python Elgato Stream Deck Library
|
|
14
23
|
|
|
@@ -32,6 +41,7 @@ the images shown on each button, and read the current button states.
|
|
|
32
41
|
Currently the following StreamDeck products are supported in multiple hardware
|
|
33
42
|
variants:
|
|
34
43
|
|
|
44
|
+
* StreamDeck 16 Key, 32 Key and Scissor Modules
|
|
35
45
|
* StreamDeck Mini
|
|
36
46
|
* StreamDeck Neo
|
|
37
47
|
* StreamDeck Original
|
|
@@ -79,16 +89,20 @@ development and maintenance of this library:
|
|
|
79
89
|
- [dubstech](https://github.com/dubstech)
|
|
80
90
|
- [Giraut](https://github.com/Giraut)
|
|
81
91
|
- [impala454](https://github.com/impala454)
|
|
92
|
+
- [iPhoneAddict](https://github.com/iPhoneAddict)
|
|
82
93
|
- [itsusony](https://github.com/itsusony)
|
|
83
94
|
- [jakobbuis](https://github.com/jakobbuis)
|
|
84
95
|
- [jmudge14](https://github.com/jmudge14)
|
|
96
|
+
- [jonbloom](https://github.com/jonbloom)
|
|
85
97
|
- [Kalle-Wirsch](https://github.com/Kalle-Wirsch)
|
|
86
98
|
- [karstlok](https://github.com/karstlok)
|
|
87
99
|
- [Lewiscowles1986](https://github.com/Lewiscowles1986)
|
|
88
100
|
- [m-weigand](https://github.com/m-weigand)
|
|
101
|
+
- [mathben](https://github.com/mathben)
|
|
89
102
|
- [matrixinius](https://github.com/matrixinius)
|
|
90
103
|
- [phillco](https://github.com/phillco)
|
|
91
104
|
- [pointshader](https://github.com/pointshader)
|
|
105
|
+
- [pylogger-html](https://github.com/pylogger-html)
|
|
92
106
|
- [shanna](https://github.com/shanna)
|
|
93
107
|
- [spidererrol](https://github.com/Spidererrol)
|
|
94
108
|
- [spyoungtech](https://github.com/spyoungtech)
|
streamdeck-0.9.6/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.9.6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|