streamdeck-gui-ng 4.1.3__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 (62) hide show
  1. streamdeck_gui_ng-4.1.3.dist-info/METADATA +141 -0
  2. streamdeck_gui_ng-4.1.3.dist-info/RECORD +62 -0
  3. streamdeck_gui_ng-4.1.3.dist-info/WHEEL +4 -0
  4. streamdeck_gui_ng-4.1.3.dist-info/entry_points.txt +4 -0
  5. streamdeck_gui_ng-4.1.3.dist-info/licenses/LICENSE +21 -0
  6. streamdeck_ui/__init__.py +6 -0
  7. streamdeck_ui/api.py +712 -0
  8. streamdeck_ui/button.ui +1214 -0
  9. streamdeck_ui/cli/__init__.py +0 -0
  10. streamdeck_ui/cli/commands.py +191 -0
  11. streamdeck_ui/cli/server.py +292 -0
  12. streamdeck_ui/config.py +244 -0
  13. streamdeck_ui/dimmer.py +93 -0
  14. streamdeck_ui/display/__init__.py +0 -0
  15. streamdeck_ui/display/background_color_filter.py +41 -0
  16. streamdeck_ui/display/display_grid.py +265 -0
  17. streamdeck_ui/display/empty_filter.py +43 -0
  18. streamdeck_ui/display/filter.py +65 -0
  19. streamdeck_ui/display/image_filter.py +144 -0
  20. streamdeck_ui/display/keypress_filter.py +63 -0
  21. streamdeck_ui/display/pipeline.py +74 -0
  22. streamdeck_ui/display/pulse_filter.py +54 -0
  23. streamdeck_ui/display/text_filter.py +142 -0
  24. streamdeck_ui/fonts/roboto/LICENSE.txt +202 -0
  25. streamdeck_ui/fonts/roboto/Roboto-Black.ttf +0 -0
  26. streamdeck_ui/fonts/roboto/Roboto-BlackItalic.ttf +0 -0
  27. streamdeck_ui/fonts/roboto/Roboto-Bold.ttf +0 -0
  28. streamdeck_ui/fonts/roboto/Roboto-BoldItalic.ttf +0 -0
  29. streamdeck_ui/fonts/roboto/Roboto-Italic.ttf +0 -0
  30. streamdeck_ui/fonts/roboto/Roboto-Light.ttf +0 -0
  31. streamdeck_ui/fonts/roboto/Roboto-LightItalic.ttf +0 -0
  32. streamdeck_ui/fonts/roboto/Roboto-Medium.ttf +0 -0
  33. streamdeck_ui/fonts/roboto/Roboto-MediumItalic.ttf +0 -0
  34. streamdeck_ui/fonts/roboto/Roboto-Regular.ttf +0 -0
  35. streamdeck_ui/fonts/roboto/Roboto-Thin.ttf +0 -0
  36. streamdeck_ui/fonts/roboto/Roboto-ThinItalic.ttf +0 -0
  37. streamdeck_ui/gui.py +1423 -0
  38. streamdeck_ui/icons/add_page.png +0 -0
  39. streamdeck_ui/icons/cross.png +0 -0
  40. streamdeck_ui/icons/gear.png +0 -0
  41. streamdeck_ui/icons/horizontal-align.png +0 -0
  42. streamdeck_ui/icons/remove_page.png +0 -0
  43. streamdeck_ui/icons/vertical-align.png +0 -0
  44. streamdeck_ui/icons/warning_icon_button.png +0 -0
  45. streamdeck_ui/logger.py +11 -0
  46. streamdeck_ui/logo.png +0 -0
  47. streamdeck_ui/main.ui +407 -0
  48. streamdeck_ui/mock_streamdeck.py +204 -0
  49. streamdeck_ui/model.py +78 -0
  50. streamdeck_ui/modules/__init__.py +0 -0
  51. streamdeck_ui/modules/fonts.py +150 -0
  52. streamdeck_ui/modules/keyboard.py +447 -0
  53. streamdeck_ui/modules/utils/__init__.py +0 -0
  54. streamdeck_ui/modules/utils/timers.py +35 -0
  55. streamdeck_ui/resources.qrc +10 -0
  56. streamdeck_ui/resources_rc.py +324 -0
  57. streamdeck_ui/semaphore.py +38 -0
  58. streamdeck_ui/settings.ui +155 -0
  59. streamdeck_ui/stream_deck_monitor.py +157 -0
  60. streamdeck_ui/ui_button.py +421 -0
  61. streamdeck_ui/ui_main.py +267 -0
  62. streamdeck_ui/ui_settings.py +119 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ import logging
2
+ import sys
3
+
4
+ from streamdeck_ui.config import LOG_FILE
5
+
6
+ logger = logging.getLogger("streamdeck_ui")
7
+ stderr_handler = logging.StreamHandler(sys.stderr)
8
+ file_handler = logging.FileHandler(LOG_FILE)
9
+ logger.addHandler(stderr_handler)
10
+ logger.addHandler(file_handler)
11
+ logger.setLevel(logging.INFO)
streamdeck_ui/logo.png ADDED
Binary file
streamdeck_ui/main.ui ADDED
@@ -0,0 +1,407 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>MainWindow</class>
4
+ <widget class="QMainWindow" name="MainWindow">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>940</width>
10
+ <height>766</height>
11
+ </rect>
12
+ </property>
13
+ <property name="windowTitle">
14
+ <string>Stream Deck UI</string>
15
+ </property>
16
+ <widget class="QWidget" name="centralwidget">
17
+ <property name="autoFillBackground">
18
+ <bool>false</bool>
19
+ </property>
20
+ <layout class="QHBoxLayout" name="horizontalLayout">
21
+ <property name="spacing">
22
+ <number>6</number>
23
+ </property>
24
+ <property name="leftMargin">
25
+ <number>9</number>
26
+ </property>
27
+ <property name="bottomMargin">
28
+ <number>3</number>
29
+ </property>
30
+ <item>
31
+ <layout class="QHBoxLayout" name="main_horizontalLayout">
32
+ <property name="spacing">
33
+ <number>12</number>
34
+ </property>
35
+ <property name="leftMargin">
36
+ <number>0</number>
37
+ </property>
38
+ <property name="topMargin">
39
+ <number>0</number>
40
+ </property>
41
+ <property name="rightMargin">
42
+ <number>0</number>
43
+ </property>
44
+ <property name="bottomMargin">
45
+ <number>0</number>
46
+ </property>
47
+ <item>
48
+ <layout class="QVBoxLayout" name="left_verticalLayout" stretch="0,0,1">
49
+ <property name="spacing">
50
+ <number>6</number>
51
+ </property>
52
+ <property name="sizeConstraint">
53
+ <enum>QLayout::SetDefaultConstraint</enum>
54
+ </property>
55
+ <property name="topMargin">
56
+ <number>0</number>
57
+ </property>
58
+ <item>
59
+ <layout class="QHBoxLayout" name="deviceSettings_horizontalLayout">
60
+ <property name="leftMargin">
61
+ <number>0</number>
62
+ </property>
63
+ <property name="topMargin">
64
+ <number>0</number>
65
+ </property>
66
+ <property name="rightMargin">
67
+ <number>0</number>
68
+ </property>
69
+ <property name="bottomMargin">
70
+ <number>0</number>
71
+ </property>
72
+ <item>
73
+ <widget class="QComboBox" name="device_list">
74
+ <property name="minimumSize">
75
+ <size>
76
+ <width>400</width>
77
+ <height>0</height>
78
+ </size>
79
+ </property>
80
+ </widget>
81
+ </item>
82
+ <item>
83
+ <widget class="QPushButton" name="settingsButton">
84
+ <property name="sizePolicy">
85
+ <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
86
+ <horstretch>0</horstretch>
87
+ <verstretch>0</verstretch>
88
+ </sizepolicy>
89
+ </property>
90
+ <property name="minimumSize">
91
+ <size>
92
+ <width>0</width>
93
+ <height>0</height>
94
+ </size>
95
+ </property>
96
+ <property name="maximumSize">
97
+ <size>
98
+ <width>30</width>
99
+ <height>16777215</height>
100
+ </size>
101
+ </property>
102
+ <property name="text">
103
+ <string/>
104
+ </property>
105
+ <property name="icon">
106
+ <iconset resource="resources.qrc">
107
+ <normaloff>:/icons/icons/gear.png</normaloff>:/icons/icons/gear.png</iconset>
108
+ </property>
109
+ </widget>
110
+ </item>
111
+ <item>
112
+ <widget class="QProgressBar" name="cpu_usage">
113
+ <property name="sizePolicy">
114
+ <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
115
+ <horstretch>0</horstretch>
116
+ <verstretch>0</verstretch>
117
+ </sizepolicy>
118
+ </property>
119
+ <property name="maximumSize">
120
+ <size>
121
+ <width>25</width>
122
+ <height>25</height>
123
+ </size>
124
+ </property>
125
+ <property name="maximum">
126
+ <number>130</number>
127
+ </property>
128
+ <property name="value">
129
+ <number>0</number>
130
+ </property>
131
+ <property name="orientation">
132
+ <enum>Qt::Vertical</enum>
133
+ </property>
134
+ <property name="format">
135
+ <string/>
136
+ </property>
137
+ </widget>
138
+ </item>
139
+ </layout>
140
+ </item>
141
+ <item>
142
+ <layout class="QHBoxLayout" name="pageActions">
143
+ <property name="leftMargin">
144
+ <number>0</number>
145
+ </property>
146
+ <property name="topMargin">
147
+ <number>0</number>
148
+ </property>
149
+ <property name="rightMargin">
150
+ <number>0</number>
151
+ </property>
152
+ <item>
153
+ <spacer name="horizontalSpacer">
154
+ <property name="orientation">
155
+ <enum>Qt::Horizontal</enum>
156
+ </property>
157
+ <property name="sizeHint" stdset="0">
158
+ <size>
159
+ <width>40</width>
160
+ <height>20</height>
161
+ </size>
162
+ </property>
163
+ </spacer>
164
+ </item>
165
+ <item>
166
+ <widget class="QPushButton" name="add_page">
167
+ <property name="sizePolicy">
168
+ <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
169
+ <horstretch>0</horstretch>
170
+ <verstretch>0</verstretch>
171
+ </sizepolicy>
172
+ </property>
173
+ <property name="maximumSize">
174
+ <size>
175
+ <width>16777215</width>
176
+ <height>16777215</height>
177
+ </size>
178
+ </property>
179
+ <property name="toolTip">
180
+ <string>Add new page</string>
181
+ </property>
182
+ <property name="text">
183
+ <string/>
184
+ </property>
185
+ <property name="icon">
186
+ <iconset resource="resources.qrc">
187
+ <normaloff>:/icons/icons/add_page.png</normaloff>:/icons/icons/add_page.png</iconset>
188
+ </property>
189
+ </widget>
190
+ </item>
191
+ <item>
192
+ <widget class="QPushButton" name="remove_page">
193
+ <property name="toolTip">
194
+ <string>Delete current page</string>
195
+ </property>
196
+ <property name="text">
197
+ <string/>
198
+ </property>
199
+ <property name="icon">
200
+ <iconset resource="resources.qrc">
201
+ <normaloff>:/icons/icons/remove_page.png</normaloff>:/icons/icons/remove_page.png</iconset>
202
+ </property>
203
+ </widget>
204
+ </item>
205
+ </layout>
206
+ </item>
207
+ <item>
208
+ <widget class="QTabWidget" name="pages">
209
+ <property name="sizePolicy">
210
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
211
+ <horstretch>0</horstretch>
212
+ <verstretch>0</verstretch>
213
+ </sizepolicy>
214
+ </property>
215
+ <property name="autoFillBackground">
216
+ <bool>false</bool>
217
+ </property>
218
+ <property name="styleSheet">
219
+ <string notr="true">b</string>
220
+ </property>
221
+ <property name="currentIndex">
222
+ <number>0</number>
223
+ </property>
224
+ <widget class="QWidget" name="page_1">
225
+ <attribute name="title">
226
+ <string/>
227
+ </attribute>
228
+ <layout class="QGridLayout" name="gridLayout_2"/>
229
+ </widget>
230
+ </widget>
231
+ </item>
232
+ </layout>
233
+ </item>
234
+ <item>
235
+ <layout class="QHBoxLayout" name="right_horizontalLayout">
236
+ <item>
237
+ <widget class="QGroupBox" name="groupBox">
238
+ <property name="minimumSize">
239
+ <size>
240
+ <width>250</width>
241
+ <height>0</height>
242
+ </size>
243
+ </property>
244
+ <property name="title">
245
+ <string>Configure Button</string>
246
+ </property>
247
+ <layout class="QVBoxLayout" name="verticalLayout_3">
248
+ <property name="spacing">
249
+ <number>10</number>
250
+ </property>
251
+ <property name="leftMargin">
252
+ <number>0</number>
253
+ </property>
254
+ <property name="topMargin">
255
+ <number>0</number>
256
+ </property>
257
+ <property name="rightMargin">
258
+ <number>0</number>
259
+ </property>
260
+ <property name="bottomMargin">
261
+ <number>0</number>
262
+ </property>
263
+ <item>
264
+ <layout class="QHBoxLayout" name="button_actions">
265
+ <property name="topMargin">
266
+ <number>10</number>
267
+ </property>
268
+ <property name="bottomMargin">
269
+ <number>0</number>
270
+ </property>
271
+ <item>
272
+ <spacer name="horizontalSpacer_2">
273
+ <property name="orientation">
274
+ <enum>Qt::Horizontal</enum>
275
+ </property>
276
+ <property name="sizeHint" stdset="0">
277
+ <size>
278
+ <width>40</width>
279
+ <height>20</height>
280
+ </size>
281
+ </property>
282
+ </spacer>
283
+ </item>
284
+ <item>
285
+ <widget class="QPushButton" name="add_button_state">
286
+ <property name="toolTip">
287
+ <string>Add a new button state</string>
288
+ </property>
289
+ <property name="text">
290
+ <string/>
291
+ </property>
292
+ <property name="icon">
293
+ <iconset resource="resources.qrc">
294
+ <normaloff>:/icons/icons/add_page.png</normaloff>:/icons/icons/add_page.png</iconset>
295
+ </property>
296
+ </widget>
297
+ </item>
298
+ <item>
299
+ <widget class="QPushButton" name="remove_button_state">
300
+ <property name="toolTip">
301
+ <string>Remove current selected button state</string>
302
+ </property>
303
+ <property name="text">
304
+ <string/>
305
+ </property>
306
+ <property name="icon">
307
+ <iconset resource="resources.qrc">
308
+ <normaloff>:/icons/icons/remove_page.png</normaloff>:/icons/icons/remove_page.png</iconset>
309
+ </property>
310
+ </widget>
311
+ </item>
312
+ </layout>
313
+ </item>
314
+ <item>
315
+ <widget class="QTabWidget" name="button_states">
316
+ <property name="currentIndex">
317
+ <number>0</number>
318
+ </property>
319
+ <widget class="QWidget" name="tab">
320
+ <attribute name="title">
321
+ <string/>
322
+ </attribute>
323
+ <layout class="QVBoxLayout" name="verticalLayout"/>
324
+ </widget>
325
+ </widget>
326
+ </item>
327
+ </layout>
328
+ </widget>
329
+ </item>
330
+ </layout>
331
+ </item>
332
+ </layout>
333
+ </item>
334
+ </layout>
335
+ </widget>
336
+ <widget class="QMenuBar" name="menubar">
337
+ <property name="geometry">
338
+ <rect>
339
+ <x>0</x>
340
+ <y>0</y>
341
+ <width>940</width>
342
+ <height>33</height>
343
+ </rect>
344
+ </property>
345
+ <widget class="QMenu" name="menuFile">
346
+ <property name="title">
347
+ <string>File</string>
348
+ </property>
349
+ <addaction name="actionImport"/>
350
+ <addaction name="actionExport"/>
351
+ <addaction name="separator"/>
352
+ <addaction name="actionExit"/>
353
+ </widget>
354
+ <widget class="QMenu" name="menuHelp">
355
+ <property name="title">
356
+ <string>Help</string>
357
+ </property>
358
+ <addaction name="actionDocs"/>
359
+ <addaction name="actionGithub"/>
360
+ <addaction name="separator"/>
361
+ <addaction name="actionAbout"/>
362
+ </widget>
363
+ <addaction name="menuFile"/>
364
+ <addaction name="menuHelp"/>
365
+ </widget>
366
+ <widget class="QStatusBar" name="statusbar"/>
367
+ <action name="actionImport">
368
+ <property name="text">
369
+ <string>Import</string>
370
+ </property>
371
+ </action>
372
+ <action name="actionExport">
373
+ <property name="text">
374
+ <string>Export</string>
375
+ </property>
376
+ </action>
377
+ <action name="actionExit">
378
+ <property name="text">
379
+ <string>Exit</string>
380
+ </property>
381
+ </action>
382
+ <action name="actionDocs">
383
+ <property name="text">
384
+ <string>Documentation</string>
385
+ </property>
386
+ </action>
387
+ <action name="actionGithub">
388
+ <property name="text">
389
+ <string>Github</string>
390
+ </property>
391
+ </action>
392
+ <action name="actionAbout">
393
+ <property name="text">
394
+ <string>About...</string>
395
+ </property>
396
+ </action>
397
+ </widget>
398
+ <tabstops>
399
+ <tabstop>device_list</tabstop>
400
+ <tabstop>settingsButton</tabstop>
401
+ <tabstop>pages</tabstop>
402
+ </tabstops>
403
+ <resources>
404
+ <include location="resources.qrc"/>
405
+ </resources>
406
+ <connections/>
407
+ </ui>
@@ -0,0 +1,204 @@
1
+ from StreamDeck.Devices import StreamDeck
2
+
3
+
4
+ class StreamDeckMock(StreamDeck.StreamDeck):
5
+ """
6
+ Represents a physically attached StreamDeck Original device.
7
+ """
8
+
9
+ KEY_COUNT = 24
10
+ KEY_COLS = 6
11
+ KEY_ROWS = 4
12
+
13
+ KEY_PIXEL_WIDTH = 72
14
+ KEY_PIXEL_HEIGHT = 72
15
+ KEY_IMAGE_FORMAT = "BMP"
16
+ KEY_FLIP = (True, True)
17
+ KEY_ROTATION = 0
18
+
19
+ DECK_TYPE = "Stream Deck Original"
20
+
21
+ IMAGE_REPORT_LENGTH = 8191
22
+ IMAGE_REPORT_HEADER_LENGTH = 16
23
+
24
+ # fmt: off
25
+ # 72 x 72 black BMP
26
+ BLANK_KEY_IMAGE = [
27
+ 0x42, 0x4d, 0xf6, 0x3c, 0x00, 0x00, 0x00, 0x00,
28
+ 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
29
+ 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00,
30
+ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
31
+ 0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc4, 0x0e,
32
+ 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
33
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
34
+ ] + [0] * (KEY_PIXEL_WIDTH * KEY_PIXEL_HEIGHT * 3)
35
+ # fmt: on
36
+
37
+ def _convert_key_id_origin(self, key):
38
+ """
39
+ Converts a key index from or to a origin at the physical top-left of
40
+ the StreamDeck device.
41
+
42
+ :param int key: Index of the button with either a device or top-left origin.
43
+
44
+ :rtype: int
45
+ :return: Key index converted to the opposite key origin (device or top-left).
46
+ """
47
+
48
+ key_col = key % self.KEY_COLS
49
+ return (key - key_col) + ((self.KEY_COLS - 1) - key_col)
50
+
51
+ def _read_key_states(self):
52
+ """
53
+ Reads the key states of the StreamDeck. This is used internally by
54
+ :func:`~StreamDeck._read` to talk to the actual device.
55
+
56
+ :rtype: list(bool)
57
+ :return: Button states, with the origin at the top-left of the deck.
58
+ """
59
+
60
+ return None
61
+
62
+ def __del__(self):
63
+ """
64
+ Delete handler for the StreamDeck, automatically closing the transport
65
+ if it is currently open and terminating the transport reader thread.
66
+ """
67
+ pass
68
+
69
+ # states = self.device.read(1 + self.KEY_COUNT)
70
+ # if states is None:
71
+ # return None
72
+
73
+ # states = states[1:]
74
+ # return [bool(states[s]) for s in map(self._convert_key_id_origin, range(self.KEY_COUNT))]
75
+
76
+ def open(self):
77
+ """
78
+ Opens the device for input/output. This must be called prior to setting
79
+ or retrieving any device state.
80
+
81
+ .. seealso:: See :func:`~StreamDeck.close` for the corresponding close method.
82
+ """
83
+ # self.device.open()
84
+ # self._reset_key_stream()
85
+ # self._setup_reader(self._read)
86
+
87
+ def close(self):
88
+ """
89
+ Closes the device for input/output.
90
+
91
+ .. seealso:: See :func:`~StreamDeck.open` for the corresponding open method.
92
+ """
93
+ pass
94
+
95
+ def is_open(self):
96
+ """
97
+ Indicattes if the StreamDeck device is currently open and ready for use..
98
+
99
+ :rtype: bool
100
+ :return: `True` if the deck is open, `False` otherwise.
101
+ """
102
+ return True
103
+
104
+ def connected(self):
105
+ """
106
+ Indicates if the physical StreamDeck device this instance is attached to
107
+ is still connected to the host.
108
+
109
+ :rtype: bool
110
+ :return: `True` if the deck is still connected, `False` otherwise.
111
+ """
112
+ return True
113
+
114
+ def id(self):
115
+ """
116
+ Retrieves the physical ID of the attached StreamDeck. This can be used
117
+ to differentiate one StreamDeck from another.
118
+
119
+ :rtype: str
120
+ :return: Identifier for the attached device.
121
+ """
122
+ return "/dev/dummy"
123
+
124
+ def _reset_key_stream(self):
125
+ """
126
+ Sends a blank key report to the StreamDeck, resetting the key image
127
+ streamer in the device. This prevents previously started partial key
128
+ writes that were not completed from corrupting images sent from this
129
+ application.
130
+ """
131
+
132
+ payload = bytearray(self.IMAGE_REPORT_LENGTH)
133
+ payload[0] = 0x02
134
+ # self.device.write(payload)
135
+
136
+ def reset(self):
137
+ """
138
+ Resets the StreamDeck, clearing all button images and showing the
139
+ standby image.
140
+ """
141
+
142
+ payload = bytearray(17)
143
+ payload[0:2] = [0x0B, 0x63]
144
+ # self.device.write_feature(payload)
145
+
146
+ def set_brightness(self, percent):
147
+ """
148
+ Sets the global screen brightness of the StreamDeck, across all the
149
+ physical buttons.
150
+
151
+ :param int/float percent: brightness percent, from [0-100] as an `int`,
152
+ or normalized to [0.0-1.0] as a `float`.
153
+ """
154
+
155
+ print(f"Dummy brightness changed to: {percent}")
156
+ if isinstance(percent, float):
157
+ percent = int(100.0 * percent)
158
+
159
+ percent = min(max(percent, 0), 100)
160
+
161
+ payload = bytearray(17)
162
+ payload[0:6] = [0x05, 0x55, 0xAA, 0xD1, 0x01, percent]
163
+ # self.device.write_feature(payload)
164
+
165
+ def get_serial_number(self):
166
+ """
167
+ Gets the serial number of the attached StreamDeck.
168
+
169
+ :rtype: str
170
+ :return: String containing the serial number of the attached device.
171
+ """
172
+
173
+ # serial = self.device.read_feature(0x03, 17)
174
+ # return self._extract_string(serial[5:])
175
+ return "FAKE"
176
+
177
+ def get_firmware_version(self):
178
+ """
179
+ Gets the firmware version of the attached StreamDeck.
180
+
181
+ :rtype: str
182
+ :return: String containing the firmware version of the attached device.
183
+ """
184
+
185
+ # version = self.device.read_feature(0x04, 17)
186
+ # return self._extract_string(version[5:])
187
+ return "1.0"
188
+
189
+ def set_key_image(self, key, image):
190
+ """
191
+ Sets the image of a button on the StreamDeck to the given image. The
192
+ image being set should be in the correct format for the device, as an
193
+ enumerable collection of bytes.
194
+
195
+ .. seealso:: See :func:`~StreamDeck.get_key_image_format` method for
196
+ information on the image format accepted by the device.
197
+
198
+ :param int key: Index of the button whose image is to be updated.
199
+ :param enumerable image: Raw data of the image to set on the button.
200
+ If `None`, the key will be cleared to a black
201
+ color.
202
+ """
203
+
204
+ pass