streamdeck 0.9.4__tar.gz → 0.9.6__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.4 → streamdeck-0.9.6}/CHANGELOG +10 -0
- streamdeck-0.9.6/PKG-INFO +108 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/README.md +15 -4
- streamdeck-0.9.6/VERSION +1 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/DeviceManager.py +6 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeck.py +306 -14
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeckMini.py +14 -3
- streamdeck-0.9.6/src/StreamDeck/Devices/StreamDeckNeo.py +212 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeckOriginal.py +14 -3
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeckOriginalV2.py +14 -3
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeckPedal.py +14 -3
- streamdeck-0.9.6/src/StreamDeck/Devices/StreamDeckPlus.py +498 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/StreamDeckXL.py +14 -3
- streamdeck-0.9.6/src/StreamDeck/ImageHelpers/PILHelper.py +253 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/ProductIDs.py +4 -2
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Transport/LibUSBHIDAPI.py +23 -135
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Transport/Transport.py +1 -1
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/example_animated.py +2 -2
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/example_basic.py +6 -2
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/example_deckinfo.py +15 -6
- streamdeck-0.9.6/src/example_neo.py +170 -0
- streamdeck-0.9.6/src/example_plus.py +135 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/example_tileimage.py +2 -2
- streamdeck-0.9.6/src/streamdeck.egg-info/PKG-INFO +108 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/streamdeck.egg-info/SOURCES.txt +6 -1
- streamdeck-0.9.6/test/test.py +128 -0
- streamdeck-0.9.4/PKG-INFO +0 -97
- streamdeck-0.9.4/VERSION +0 -1
- streamdeck-0.9.4/src/StreamDeck/ImageHelpers/PILHelper.py +0 -108
- streamdeck-0.9.4/src/streamdeck.egg-info/PKG-INFO +0 -97
- {streamdeck-0.9.4 → streamdeck-0.9.6}/ExampleDeck.jpg +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/LICENSE +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/MANIFEST.in +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/setup.cfg +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/setup.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Apache License 2.0.txt +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Attribution.txt +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/CC BY-SA 3.0 License.txt +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/CC0 License.txt +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Elephant_Walking_animated.gif +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Exit.png +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Harold.jpg +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Pressed.png +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/RGB_color_space_animated_view.gif +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Released.png +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Roboto-Regular.ttf +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/Assets/Simple_CV_Joint_animated.gif +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Devices/__init__.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/ImageHelpers/__init__.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Transport/Dummy.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/Transport/__init__.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/StreamDeck/__init__.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/example_pedal.py +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/streamdeck.egg-info/dependency_links.txt +0 -0
- {streamdeck-0.9.4 → streamdeck-0.9.6}/src/streamdeck.egg-info/top_level.txt +0 -0
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
Version 0.9.6:
|
|
2
|
+
- Fixed StreamDeck+ returning 10 key states instead of the expected 8.
|
|
3
|
+
- Fixed StreamDeck+ dial push event not being detected correctly.
|
|
4
|
+
- Fixed old key states and dial states returned if requested from inside a key or dial callback function.
|
|
5
|
+
- Added support for FreeBSD platforms.
|
|
6
|
+
- Added support for the StreamDeck Neo.
|
|
7
|
+
|
|
8
|
+
Version 0.9.5:
|
|
9
|
+
- Added support for the StreamDeck Plus.
|
|
10
|
+
|
|
1
11
|
Version 0.9.4:
|
|
2
12
|
- Updated Windows HIDAPI backend to attempt to load from the local working directory.
|
|
3
13
|
- Added detection for MacOS Homebrew installations of the libhidapi back-end library.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: streamdeck
|
|
3
|
+
Version: 0.9.6
|
|
4
|
+
Summary: Library to control Elgato StreamDeck devices.
|
|
5
|
+
Home-page: https://github.com/abcminiuser/python-elgato-streamdeck
|
|
6
|
+
Author: Dean Camera
|
|
7
|
+
Author-email: dean@fourwalledcubicle.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
|
|
13
|
+
# Python Elgato Stream Deck Library
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
This is an open source Python 3 library to control an
|
|
18
|
+
[Elgato Stream Deck](https://www.elgato.com/en/gaming/stream-deck) directly,
|
|
19
|
+
without the official software. This can allow you to create your own custom
|
|
20
|
+
front-ends, such as a custom control front-end for home automation software.
|
|
21
|
+
|
|
22
|
+
_________________
|
|
23
|
+
|
|
24
|
+
[PyPi Project Entry](https://pypi.org/project/streamdeck/) - [Online Documentation](https://python-elgato-streamdeck.readthedocs.io) - [Source Code](https://github.com/abcminiuser/python-elgato-streamdeck)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Project Status:
|
|
28
|
+
|
|
29
|
+
Working - you can enumerate devices, set the brightness of the panel(s), set
|
|
30
|
+
the images shown on each button, and read the current button states.
|
|
31
|
+
|
|
32
|
+
Currently the following StreamDeck products are supported in multiple hardware
|
|
33
|
+
variants:
|
|
34
|
+
|
|
35
|
+
* StreamDeck Mini
|
|
36
|
+
* StreamDeck Neo
|
|
37
|
+
* StreamDeck Original
|
|
38
|
+
* StreamDeck Pedal
|
|
39
|
+
* StreamDeck Plus
|
|
40
|
+
* StreamDeck XL
|
|
41
|
+
|
|
42
|
+
## Package Installation:
|
|
43
|
+
|
|
44
|
+
Install the library via pip:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
pip install streamdeck
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Alternatively, manually clone the project repository:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
git clone https://github.com/abcminiuser/python-elgato-streamdeck.git
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For detailed installation instructions, refer to the prebuilt
|
|
57
|
+
[online documentation](https://python-elgato-streamdeck.readthedocs.io), or
|
|
58
|
+
build the documentation yourself locally by running `make html` from the `docs`
|
|
59
|
+
directory.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Credits:
|
|
63
|
+
|
|
64
|
+
I've used the reverse engineering notes from
|
|
65
|
+
[this GitHub](https://github.com/alvancamp/node-elgato-stream-deck/blob/master/NOTES.md)
|
|
66
|
+
repository to implement this library. Thanks Alex Van Camp!
|
|
67
|
+
|
|
68
|
+
Thank you to the following contributors, large and small, for helping with the
|
|
69
|
+
development and maintenance of this library:
|
|
70
|
+
|
|
71
|
+
- [admiral0](https://github.com/admiral0)
|
|
72
|
+
- [Aetherdyne](https://github.com/Aetherdyne)
|
|
73
|
+
- [benedikt-bartscher](https://github.com/benedikt-bartscher)
|
|
74
|
+
- [brimston3](https://github.com/brimston3)
|
|
75
|
+
- [BS-Tek](https://github.com/BS-Tek)
|
|
76
|
+
- [Core447](https://github.com/Core447)
|
|
77
|
+
- [dirkk0](https://github.com/dirkk0)
|
|
78
|
+
- [dodgyrabbit](https://github.com/dodgyrabbit)
|
|
79
|
+
- [dubstech](https://github.com/dubstech)
|
|
80
|
+
- [Giraut](https://github.com/Giraut)
|
|
81
|
+
- [impala454](https://github.com/impala454)
|
|
82
|
+
- [itsusony](https://github.com/itsusony)
|
|
83
|
+
- [jakobbuis](https://github.com/jakobbuis)
|
|
84
|
+
- [jmudge14](https://github.com/jmudge14)
|
|
85
|
+
- [Kalle-Wirsch](https://github.com/Kalle-Wirsch)
|
|
86
|
+
- [karstlok](https://github.com/karstlok)
|
|
87
|
+
- [Lewiscowles1986](https://github.com/Lewiscowles1986)
|
|
88
|
+
- [m-weigand](https://github.com/m-weigand)
|
|
89
|
+
- [matrixinius](https://github.com/matrixinius)
|
|
90
|
+
- [phillco](https://github.com/phillco)
|
|
91
|
+
- [pointshader](https://github.com/pointshader)
|
|
92
|
+
- [shanna](https://github.com/shanna)
|
|
93
|
+
- [spidererrol](https://github.com/Spidererrol)
|
|
94
|
+
- [spyoungtech](https://github.com/spyoungtech)
|
|
95
|
+
- [Subsentient](https://github.com/Subsentient)
|
|
96
|
+
- [swedishmike](https://github.com/swedishmike)
|
|
97
|
+
- [TheSchmidt](https://github.com/TheSchmidt)
|
|
98
|
+
- [theslimshaney](https://github.com/theslimshaney)
|
|
99
|
+
- [tjemg](https://github.com/tjemg)
|
|
100
|
+
- [VladFlorinIlie](https://github.com/VladFlorinIlie)
|
|
101
|
+
|
|
102
|
+
If you've contributed in some manner, but I've accidentally missed you in the
|
|
103
|
+
list above, please let me know.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## License:
|
|
107
|
+
|
|
108
|
+
Released under the [MIT license](LICENSE).
|
|
@@ -20,11 +20,12 @@ 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 Original
|
|
24
23
|
* StreamDeck Mini
|
|
25
|
-
* StreamDeck
|
|
24
|
+
* StreamDeck Neo
|
|
25
|
+
* StreamDeck Original
|
|
26
26
|
* StreamDeck Pedal
|
|
27
|
-
|
|
27
|
+
* StreamDeck Plus
|
|
28
|
+
* StreamDeck XL
|
|
28
29
|
|
|
29
30
|
## Package Installation:
|
|
30
31
|
|
|
@@ -49,21 +50,28 @@ directory.
|
|
|
49
50
|
## Credits:
|
|
50
51
|
|
|
51
52
|
I've used the reverse engineering notes from
|
|
52
|
-
[this GitHub](https://github.com/
|
|
53
|
+
[this GitHub](https://github.com/alvancamp/node-elgato-stream-deck/blob/master/NOTES.md)
|
|
53
54
|
repository to implement this library. Thanks Alex Van Camp!
|
|
54
55
|
|
|
55
56
|
Thank you to the following contributors, large and small, for helping with the
|
|
56
57
|
development and maintenance of this library:
|
|
57
58
|
|
|
59
|
+
- [admiral0](https://github.com/admiral0)
|
|
58
60
|
- [Aetherdyne](https://github.com/Aetherdyne)
|
|
61
|
+
- [benedikt-bartscher](https://github.com/benedikt-bartscher)
|
|
62
|
+
- [brimston3](https://github.com/brimston3)
|
|
59
63
|
- [BS-Tek](https://github.com/BS-Tek)
|
|
64
|
+
- [Core447](https://github.com/Core447)
|
|
60
65
|
- [dirkk0](https://github.com/dirkk0)
|
|
61
66
|
- [dodgyrabbit](https://github.com/dodgyrabbit)
|
|
62
67
|
- [dubstech](https://github.com/dubstech)
|
|
68
|
+
- [Giraut](https://github.com/Giraut)
|
|
63
69
|
- [impala454](https://github.com/impala454)
|
|
70
|
+
- [itsusony](https://github.com/itsusony)
|
|
64
71
|
- [jakobbuis](https://github.com/jakobbuis)
|
|
65
72
|
- [jmudge14](https://github.com/jmudge14)
|
|
66
73
|
- [Kalle-Wirsch](https://github.com/Kalle-Wirsch)
|
|
74
|
+
- [karstlok](https://github.com/karstlok)
|
|
67
75
|
- [Lewiscowles1986](https://github.com/Lewiscowles1986)
|
|
68
76
|
- [m-weigand](https://github.com/m-weigand)
|
|
69
77
|
- [matrixinius](https://github.com/matrixinius)
|
|
@@ -74,7 +82,10 @@ development and maintenance of this library:
|
|
|
74
82
|
- [spyoungtech](https://github.com/spyoungtech)
|
|
75
83
|
- [Subsentient](https://github.com/Subsentient)
|
|
76
84
|
- [swedishmike](https://github.com/swedishmike)
|
|
85
|
+
- [TheSchmidt](https://github.com/TheSchmidt)
|
|
77
86
|
- [theslimshaney](https://github.com/theslimshaney)
|
|
87
|
+
- [tjemg](https://github.com/tjemg)
|
|
88
|
+
- [VladFlorinIlie](https://github.com/VladFlorinIlie)
|
|
78
89
|
|
|
79
90
|
If you've contributed in some manner, but I've accidentally missed you in the
|
|
80
91
|
list above, please let me know.
|
streamdeck-0.9.6/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.9.6
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
from .Devices.StreamDeckMini import StreamDeckMini
|
|
9
|
+
from .Devices.StreamDeckNeo import StreamDeckNeo
|
|
9
10
|
from .Devices.StreamDeckOriginal import StreamDeckOriginal
|
|
10
11
|
from .Devices.StreamDeckOriginalV2 import StreamDeckOriginalV2
|
|
11
12
|
from .Devices.StreamDeckXL import StreamDeckXL
|
|
12
13
|
from .Devices.StreamDeckPedal import StreamDeckPedal
|
|
14
|
+
from .Devices.StreamDeckPlus import StreamDeckPlus
|
|
13
15
|
from .Transport.Dummy import Dummy
|
|
14
16
|
from .Transport.LibUSBHIDAPI import LibUSBHIDAPI
|
|
15
17
|
from .ProductIDs import USBVendorIDs, USBProductIDs
|
|
@@ -35,9 +37,11 @@ class DeviceManager:
|
|
|
35
37
|
USB_PID_STREAMDECK_ORIGINAL = 0x0060
|
|
36
38
|
USB_PID_STREAMDECK_ORIGINAL_V2 = 0x006d
|
|
37
39
|
USB_PID_STREAMDECK_MINI = 0x0063
|
|
40
|
+
USB_PID_STREAMDECK_NEO = 0x009a
|
|
38
41
|
USB_PID_STREAMDECK_XL = 0x006c
|
|
39
42
|
USB_PID_STREAMDECK_MK2 = 0x0080
|
|
40
43
|
USB_PID_STREAMDECK_PEDAL = 0x0086
|
|
44
|
+
USB_PID_STREAMDECK_PLUS = 0x0084
|
|
41
45
|
|
|
42
46
|
@staticmethod
|
|
43
47
|
def _get_transport(transport):
|
|
@@ -103,11 +107,13 @@ class DeviceManager:
|
|
|
103
107
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_ORIGINAL, StreamDeckOriginal),
|
|
104
108
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_ORIGINAL_V2, StreamDeckOriginalV2),
|
|
105
109
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MINI, StreamDeckMini),
|
|
110
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_NEO, StreamDeckNeo),
|
|
106
111
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_XL, StreamDeckXL),
|
|
107
112
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MK2, StreamDeckOriginalV2),
|
|
108
113
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_PEDAL, StreamDeckPedal),
|
|
109
114
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_MINI_MK2, StreamDeckMini),
|
|
110
115
|
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_XL_V2, StreamDeckXL),
|
|
116
|
+
(USBVendorIDs.USB_VID_ELGATO, USBProductIDs.USB_PID_STREAMDECK_PLUS, StreamDeckPlus),
|
|
111
117
|
]
|
|
112
118
|
|
|
113
119
|
streamdecks = list()
|
|
@@ -8,10 +8,41 @@
|
|
|
8
8
|
import threading
|
|
9
9
|
import time
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
|
+
from enum import Enum
|
|
11
12
|
|
|
12
13
|
from ..Transport.Transport import TransportError
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class TouchscreenEventType(Enum):
|
|
17
|
+
"""
|
|
18
|
+
Type of event that has occurred for a Touchscreen.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
SHORT = 1
|
|
22
|
+
LONG = 2
|
|
23
|
+
DRAG = 3
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DialEventType(Enum):
|
|
27
|
+
"""
|
|
28
|
+
Type of event that has occurred for a Dial.
|
|
29
|
+
"""
|
|
30
|
+
TURN = 1
|
|
31
|
+
PUSH = 2
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ControlType(Enum):
|
|
35
|
+
"""
|
|
36
|
+
Type of control. This is used when returning internal events from a
|
|
37
|
+
StreamDeck subclass.
|
|
38
|
+
|
|
39
|
+
:meta private:
|
|
40
|
+
"""
|
|
41
|
+
KEY = 1
|
|
42
|
+
DIAL = 2
|
|
43
|
+
TOUCHSCREEN = 3
|
|
44
|
+
|
|
45
|
+
|
|
15
46
|
class StreamDeck(ABC):
|
|
16
47
|
"""
|
|
17
48
|
Represents a physically attached StreamDeck device.
|
|
@@ -21,22 +52,43 @@ class StreamDeck(ABC):
|
|
|
21
52
|
KEY_COLS = 0
|
|
22
53
|
KEY_ROWS = 0
|
|
23
54
|
|
|
55
|
+
TOUCH_KEY_COUNT = 0
|
|
56
|
+
|
|
24
57
|
KEY_PIXEL_WIDTH = 0
|
|
25
58
|
KEY_PIXEL_HEIGHT = 0
|
|
26
59
|
KEY_IMAGE_FORMAT = ""
|
|
27
60
|
KEY_FLIP = (False, False)
|
|
28
61
|
KEY_ROTATION = 0
|
|
29
62
|
|
|
63
|
+
TOUCHSCREEN_PIXEL_WIDTH = 0
|
|
64
|
+
TOUCHSCREEN_PIXEL_HEIGHT = 0
|
|
65
|
+
TOUCHSCREEN_IMAGE_FORMAT = ""
|
|
66
|
+
TOUCHSCREEN_FLIP = (False, False)
|
|
67
|
+
TOUCHSCREEN_ROTATION = 0
|
|
68
|
+
|
|
69
|
+
SCREEN_PIXEL_WIDTH = 0
|
|
70
|
+
SCREEN_PIXEL_HEIGHT = 0
|
|
71
|
+
SCREEN_IMAGE_FORMAT = ""
|
|
72
|
+
SCREEN_FLIP = (False, False)
|
|
73
|
+
SCREEN_ROTATION = 0
|
|
74
|
+
|
|
75
|
+
DIAL_COUNT = 0
|
|
76
|
+
|
|
30
77
|
DECK_TYPE = ""
|
|
31
78
|
DECK_VISUAL = False
|
|
79
|
+
DECK_TOUCH = False
|
|
32
80
|
|
|
33
81
|
def __init__(self, device):
|
|
34
82
|
self.device = device
|
|
35
|
-
self.last_key_states = [False] * self.KEY_COUNT
|
|
83
|
+
self.last_key_states = [False] * (self.KEY_COUNT + self.TOUCH_KEY_COUNT)
|
|
84
|
+
self.last_dial_states = [False] * self.DIAL_COUNT
|
|
36
85
|
self.read_thread = None
|
|
37
86
|
self.run_read_thread = False
|
|
38
87
|
self.read_poll_hz = 20
|
|
88
|
+
|
|
39
89
|
self.key_callback = None
|
|
90
|
+
self.dial_callback = None
|
|
91
|
+
self.touchscreen_callback = None
|
|
40
92
|
|
|
41
93
|
self.update_lock = threading.RLock()
|
|
42
94
|
|
|
@@ -72,12 +124,11 @@ class StreamDeck(ABC):
|
|
|
72
124
|
self.update_lock.release()
|
|
73
125
|
|
|
74
126
|
@abstractmethod
|
|
75
|
-
def
|
|
127
|
+
def _read_control_states(self):
|
|
76
128
|
"""
|
|
77
129
|
Reads the raw key states from an attached StreamDeck.
|
|
78
130
|
|
|
79
|
-
:
|
|
80
|
-
:return: List containing the raw key states.
|
|
131
|
+
:return: dictionary containing states for all controls
|
|
81
132
|
"""
|
|
82
133
|
pass
|
|
83
134
|
|
|
@@ -101,24 +152,51 @@ class StreamDeck(ABC):
|
|
|
101
152
|
|
|
102
153
|
def _read(self):
|
|
103
154
|
"""
|
|
104
|
-
Read handler for the underlying transport, listening for
|
|
155
|
+
Read handler for the underlying transport, listening for control state
|
|
105
156
|
changes on the underlying device, caching the new states and firing off
|
|
106
157
|
any registered callbacks.
|
|
107
158
|
"""
|
|
108
159
|
while self.run_read_thread:
|
|
109
160
|
try:
|
|
110
|
-
|
|
111
|
-
if
|
|
161
|
+
control_states = self._read_control_states()
|
|
162
|
+
if control_states is None:
|
|
112
163
|
time.sleep(1.0 / self.read_poll_hz)
|
|
113
164
|
continue
|
|
114
165
|
|
|
115
|
-
if
|
|
116
|
-
for k, (old, new) in enumerate(zip(self.last_key_states,
|
|
117
|
-
if old
|
|
166
|
+
if ControlType.KEY in control_states:
|
|
167
|
+
for k, (old, new) in enumerate(zip(self.last_key_states, control_states[ControlType.KEY])):
|
|
168
|
+
if old == new:
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
self.last_key_states[k] = new
|
|
172
|
+
|
|
173
|
+
if self.key_callback is not None:
|
|
118
174
|
self.key_callback(self, k, new)
|
|
119
175
|
|
|
120
|
-
|
|
121
|
-
|
|
176
|
+
elif ControlType.DIAL in control_states:
|
|
177
|
+
if DialEventType.PUSH in control_states[ControlType.DIAL]:
|
|
178
|
+
for k, (old, new) in enumerate(zip(self.last_dial_states, control_states[ControlType.DIAL][DialEventType.PUSH])):
|
|
179
|
+
if old == new:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
self.last_dial_states[k] = new
|
|
183
|
+
|
|
184
|
+
if self.dial_callback is not None:
|
|
185
|
+
self.dial_callback(self, k, DialEventType.PUSH, new)
|
|
186
|
+
|
|
187
|
+
if DialEventType.TURN in control_states[ControlType.DIAL]:
|
|
188
|
+
for k, amount in enumerate(control_states[ControlType.DIAL][DialEventType.TURN]):
|
|
189
|
+
if amount == 0:
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
if self.dial_callback is not None:
|
|
193
|
+
self.dial_callback(self, k, DialEventType.TURN, amount)
|
|
194
|
+
|
|
195
|
+
elif ControlType.TOUCHSCREEN in control_states:
|
|
196
|
+
if self.touchscreen_callback is not None:
|
|
197
|
+
self.touchscreen_callback(self, *control_states[ControlType.TOUCHSCREEN])
|
|
198
|
+
|
|
199
|
+
except TransportError:
|
|
122
200
|
self.run_read_thread = False
|
|
123
201
|
self.close()
|
|
124
202
|
|
|
@@ -223,12 +301,30 @@ class StreamDeck(ABC):
|
|
|
223
301
|
"""
|
|
224
302
|
return self.KEY_COUNT
|
|
225
303
|
|
|
304
|
+
def touch_key_count(self):
|
|
305
|
+
"""
|
|
306
|
+
Retrieves number of touch buttons on the attached StreamDeck device.
|
|
307
|
+
|
|
308
|
+
:rtype: int
|
|
309
|
+
:return: Number of touch buttons.
|
|
310
|
+
"""
|
|
311
|
+
return self.TOUCH_KEY_COUNT
|
|
312
|
+
|
|
313
|
+
def dial_count(self):
|
|
314
|
+
"""
|
|
315
|
+
Retrieves number of physical dials on the attached StreamDeck device.
|
|
316
|
+
|
|
317
|
+
:rtype: int
|
|
318
|
+
:return: Number of physical dials
|
|
319
|
+
"""
|
|
320
|
+
return self.DIAL_COUNT
|
|
321
|
+
|
|
226
322
|
def deck_type(self):
|
|
227
323
|
"""
|
|
228
324
|
Retrieves the model of Stream Deck.
|
|
229
325
|
|
|
230
326
|
:rtype: str
|
|
231
|
-
:return: String containing the model name of the StreamDeck device
|
|
327
|
+
:return: String containing the model name of the StreamDeck device.
|
|
232
328
|
"""
|
|
233
329
|
return self.DECK_TYPE
|
|
234
330
|
|
|
@@ -241,6 +337,15 @@ class StreamDeck(ABC):
|
|
|
241
337
|
"""
|
|
242
338
|
return self.DECK_VISUAL
|
|
243
339
|
|
|
340
|
+
def is_touch(self):
|
|
341
|
+
"""
|
|
342
|
+
Returns whether the Stream Deck can receive touch events
|
|
343
|
+
|
|
344
|
+
:rtype: bool
|
|
345
|
+
:return: `True` if the deck can receive touch events, `False` otherwise
|
|
346
|
+
"""
|
|
347
|
+
return self.DECK_TOUCH
|
|
348
|
+
|
|
244
349
|
def key_layout(self):
|
|
245
350
|
"""
|
|
246
351
|
Retrieves the physical button layout on the attached StreamDeck device.
|
|
@@ -269,6 +374,46 @@ class StreamDeck(ABC):
|
|
|
269
374
|
'rotation': self.KEY_ROTATION,
|
|
270
375
|
}
|
|
271
376
|
|
|
377
|
+
def touchscreen_image_format(self):
|
|
378
|
+
"""
|
|
379
|
+
Retrieves the image format accepted by the touchscreen of the Stream
|
|
380
|
+
Deck. Images should be given in this format when drawing on
|
|
381
|
+
touchscreen.
|
|
382
|
+
|
|
383
|
+
.. seealso:: See :func:`~StreamDeck.set_touchscreen_image` method to
|
|
384
|
+
draw an image on the StreamDeck touchscreen.
|
|
385
|
+
|
|
386
|
+
:rtype: dict()
|
|
387
|
+
:return: Dictionary describing the various image parameters
|
|
388
|
+
(size, image format).
|
|
389
|
+
"""
|
|
390
|
+
return {
|
|
391
|
+
'size': (self.TOUCHSCREEN_PIXEL_WIDTH, self.TOUCHSCREEN_PIXEL_HEIGHT),
|
|
392
|
+
'format': self.TOUCHSCREEN_IMAGE_FORMAT,
|
|
393
|
+
'flip': self.TOUCHSCREEN_FLIP,
|
|
394
|
+
'rotation': self.TOUCHSCREEN_ROTATION,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
def screen_image_format(self):
|
|
398
|
+
"""
|
|
399
|
+
Retrieves the image format accepted by the screen of the Stream
|
|
400
|
+
Deck. Images should be given in this format when drawing on
|
|
401
|
+
screen.
|
|
402
|
+
|
|
403
|
+
.. seealso:: See :func:`~StreamDeck.set_screen_image` method to
|
|
404
|
+
draw an image on the StreamDeck screen.
|
|
405
|
+
|
|
406
|
+
:rtype: dict()
|
|
407
|
+
:return: Dictionary describing the various image parameters
|
|
408
|
+
(size, image format).
|
|
409
|
+
"""
|
|
410
|
+
return {
|
|
411
|
+
'size': (self.SCREEN_PIXEL_WIDTH, self.SCREEN_PIXEL_HEIGHT),
|
|
412
|
+
'format': self.SCREEN_IMAGE_FORMAT,
|
|
413
|
+
'flip': self.SCREEN_FLIP,
|
|
414
|
+
'rotation': self.SCREEN_ROTATION,
|
|
415
|
+
}
|
|
416
|
+
|
|
272
417
|
def set_poll_frequency(self, hz):
|
|
273
418
|
"""
|
|
274
419
|
Sets the frequency of the button polling reader thread, determining how
|
|
@@ -325,6 +470,94 @@ class StreamDeck(ABC):
|
|
|
325
470
|
|
|
326
471
|
self.set_key_callback(callback)
|
|
327
472
|
|
|
473
|
+
def set_dial_callback(self, callback):
|
|
474
|
+
"""
|
|
475
|
+
Sets the callback function called each time there is an interaction
|
|
476
|
+
with a dial on the StreamDeck.
|
|
477
|
+
|
|
478
|
+
.. note:: This callback will be fired from an internal reader thread.
|
|
479
|
+
Ensure that the given callback function is thread-safe.
|
|
480
|
+
|
|
481
|
+
.. note:: Only one callback can be registered at one time.
|
|
482
|
+
|
|
483
|
+
.. seealso:: See :func:`~StreamDeck.set_dial_callback_async` method
|
|
484
|
+
for a version compatible with Python 3 `asyncio`
|
|
485
|
+
asynchronous functions.
|
|
486
|
+
|
|
487
|
+
:param function callback: Callback function to fire each time a button
|
|
488
|
+
state changes.
|
|
489
|
+
"""
|
|
490
|
+
self.dial_callback = callback
|
|
491
|
+
|
|
492
|
+
def set_dial_callback_async(self, async_callback, loop=None):
|
|
493
|
+
"""
|
|
494
|
+
Sets the asynchronous callback function called each time there is an
|
|
495
|
+
interaction with a dial on the StreamDeck. The given callback should
|
|
496
|
+
be compatible with Python 3's `asyncio` routines.
|
|
497
|
+
|
|
498
|
+
.. note:: The asynchronous callback will be fired in a thread-safe
|
|
499
|
+
manner.
|
|
500
|
+
|
|
501
|
+
.. note:: This will override the callback (if any) set by
|
|
502
|
+
:func:`~StreamDeck.set_dial_callback`.
|
|
503
|
+
|
|
504
|
+
:param function async_callback: Asynchronous callback function to fire
|
|
505
|
+
each time a button state changes.
|
|
506
|
+
:param asyncio.loop loop: Asyncio loop to dispatch the callback into
|
|
507
|
+
"""
|
|
508
|
+
import asyncio
|
|
509
|
+
|
|
510
|
+
loop = loop or asyncio.get_event_loop()
|
|
511
|
+
|
|
512
|
+
def callback(*args):
|
|
513
|
+
asyncio.run_coroutine_threadsafe(async_callback(*args), loop)
|
|
514
|
+
|
|
515
|
+
self.set_dial_callback(callback)
|
|
516
|
+
|
|
517
|
+
def set_touchscreen_callback(self, callback):
|
|
518
|
+
"""
|
|
519
|
+
Sets the callback function called each time there is an interaction
|
|
520
|
+
with a touchscreen on the StreamDeck.
|
|
521
|
+
|
|
522
|
+
.. note:: This callback will be fired from an internal reader thread.
|
|
523
|
+
Ensure that the given callback function is thread-safe.
|
|
524
|
+
|
|
525
|
+
.. note:: Only one callback can be registered at one time.
|
|
526
|
+
|
|
527
|
+
.. seealso:: See :func:`~StreamDeck.set_touchscreen_callback_async`
|
|
528
|
+
method for a version compatible with Python 3 `asyncio`
|
|
529
|
+
asynchronous functions.
|
|
530
|
+
|
|
531
|
+
:param function callback: Callback function to fire each time a button
|
|
532
|
+
state changes.
|
|
533
|
+
"""
|
|
534
|
+
self.touchscreen_callback = callback
|
|
535
|
+
|
|
536
|
+
def set_touchscreen_callback_async(self, async_callback, loop=None):
|
|
537
|
+
"""
|
|
538
|
+
Sets the asynchronous callback function called each time there is an
|
|
539
|
+
interaction with the touchscreen on the StreamDeck. The given callback
|
|
540
|
+
should be compatible with Python 3's `asyncio` routines.
|
|
541
|
+
|
|
542
|
+
.. note:: The asynchronous callback will be fired in a thread-safe
|
|
543
|
+
manner.
|
|
544
|
+
|
|
545
|
+
.. note:: This will override the callback (if any) set by
|
|
546
|
+
:func:`~StreamDeck.set_touchscreen_callback`.
|
|
547
|
+
|
|
548
|
+
:param function async_callback: Asynchronous callback function to fire
|
|
549
|
+
each time a button state changes.
|
|
550
|
+
:param asyncio.loop loop: Asyncio loop to dispatch the callback into
|
|
551
|
+
"""
|
|
552
|
+
import asyncio
|
|
553
|
+
|
|
554
|
+
loop = loop or asyncio.get_event_loop()
|
|
555
|
+
|
|
556
|
+
def callback(*args):
|
|
557
|
+
asyncio.run_coroutine_threadsafe(async_callback(*args), loop)
|
|
558
|
+
|
|
559
|
+
self.set_touchscreen_callback(callback)
|
|
560
|
+
|
|
328
561
|
def key_states(self):
|
|
329
562
|
"""
|
|
330
563
|
Retrieves the current states of the buttons on the StreamDeck.
|
|
@@ -336,6 +569,18 @@ class StreamDeck(ABC):
|
|
|
336
569
|
"""
|
|
337
570
|
return self.last_key_states
|
|
338
571
|
|
|
572
|
+
def dial_states(self):
|
|
573
|
+
"""
|
|
574
|
+
Retrieves the current states of the dials (pressed or not) on the
|
|
575
|
+
Stream Deck
|
|
576
|
+
|
|
577
|
+
:rtype: list(bool)
|
|
578
|
+
:return: List describing the current states of each of the dials on
|
|
579
|
+
the device (`True` if the dial is being pressed, `False`
|
|
580
|
+
otherwise).
|
|
581
|
+
"""
|
|
582
|
+
return self.last_dial_states
|
|
583
|
+
|
|
339
584
|
@abstractmethod
|
|
340
585
|
def reset(self):
|
|
341
586
|
"""
|
|
@@ -382,7 +627,7 @@ class StreamDeck(ABC):
|
|
|
382
627
|
image being set should be in the correct format for the device, as an
|
|
383
628
|
enumerable collection of bytes.
|
|
384
629
|
|
|
385
|
-
.. seealso:: See :func:`~StreamDeck.
|
|
630
|
+
.. seealso:: See :func:`~StreamDeck.key_image_format` method for
|
|
386
631
|
information on the image format accepted by the device.
|
|
387
632
|
|
|
388
633
|
:param int key: Index of the button whose image is to be updated.
|
|
@@ -391,3 +636,50 @@ class StreamDeck(ABC):
|
|
|
391
636
|
color.
|
|
392
637
|
"""
|
|
393
638
|
pass
|
|
639
|
+
|
|
640
|
+
@abstractmethod
|
|
641
|
+
def set_touchscreen_image(self, image, x_pos=0, y_pos=0, width=0, height=0):
|
|
642
|
+
"""
|
|
643
|
+
Draws an image on the touchscreen in a certain position. The image
|
|
644
|
+
should be in the correct format for the devices, as an enumerable
|
|
645
|
+
collection of bytes.
|
|
646
|
+
|
|
647
|
+
.. seealso:: See :func:`~StreamDeck.touchscreen_image_format` method for
|
|
648
|
+
information on the image format accepted by the device.
|
|
649
|
+
|
|
650
|
+
:param enumerable image: Raw data of the image to set on the button.
|
|
651
|
+
If `None`, the touchscreen will be cleared.
|
|
652
|
+
:param int x_pos: Position on x axis of the image to draw
|
|
653
|
+
:param int y_pos: Position on y axis of the image to draw
|
|
654
|
+
:param int width: width of the image
|
|
655
|
+
:param int height: height of the image
|
|
656
|
+
|
|
657
|
+
"""
|
|
658
|
+
pass
|
|
659
|
+
|
|
660
|
+
@abstractmethod
|
|
661
|
+
def set_key_color(self, key, r, g, b):
|
|
662
|
+
"""
|
|
663
|
+
Sets the color of the touch buttons. These buttons are indexed
|
|
664
|
+
in order after the standard keys.
|
|
665
|
+
|
|
666
|
+
:param int key: Index of the button
|
|
667
|
+
:param int r: Red value
|
|
668
|
+
:param int g: Green value
|
|
669
|
+
:param int b: Blue value
|
|
670
|
+
|
|
671
|
+
"""
|
|
672
|
+
pass
|
|
673
|
+
|
|
674
|
+
@abstractmethod
|
|
675
|
+
def set_screen_image(self, image):
|
|
676
|
+
"""
|
|
677
|
+
Draws an image on the touchless screen of the StreamDeck.
|
|
678
|
+
|
|
679
|
+
.. seealso:: See :func:`~StreamDeck.screen_image_format` method for
|
|
680
|
+
information on the image format accepted by the device.
|
|
681
|
+
|
|
682
|
+
:param enumerable image: Raw data of the image to set on the button.
|
|
683
|
+
If `None`, the screen will be cleared.
|
|
684
|
+
"""
|
|
685
|
+
pass
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# www.fourwalledcubicle.com
|
|
6
6
|
#
|
|
7
7
|
|
|
8
|
-
from .StreamDeck import StreamDeck
|
|
8
|
+
from .StreamDeck import StreamDeck, ControlType
|
|
9
9
|
from ..ProductIDs import USBProductIDs
|
|
10
10
|
|
|
11
11
|
|
|
@@ -42,13 +42,15 @@ class StreamDeckMini(StreamDeck):
|
|
|
42
42
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
43
43
|
] + [0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def _read_control_states(self):
|
|
46
46
|
states = self.device.read(1 + self.KEY_COUNT)
|
|
47
47
|
if states is None:
|
|
48
48
|
return None
|
|
49
49
|
|
|
50
50
|
states = states[1:]
|
|
51
|
-
return
|
|
51
|
+
return {
|
|
52
|
+
ControlType.KEY: [bool(s) for s in states],
|
|
53
|
+
}
|
|
52
54
|
|
|
53
55
|
def _reset_key_stream(self):
|
|
54
56
|
payload = bytearray(self.IMAGE_REPORT_LENGTH)
|
|
@@ -116,3 +118,12 @@ class StreamDeckMini(StreamDeck):
|
|
|
116
118
|
|
|
117
119
|
bytes_remaining = bytes_remaining - this_length
|
|
118
120
|
page_number = page_number + 1
|
|
121
|
+
|
|
122
|
+
def set_touchscreen_image(self, image, x_pos=0, y_pos=0, width=0, height=0):
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
def set_key_color(self, key, r, g, b):
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
def set_screen_image(self, image):
|
|
129
|
+
pass
|