awtrix_control 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/awtrix_control/app.rb +471 -0
- data/lib/awtrix_control/device.rb +195 -0
- data/lib/awtrix_control/request.rb +33 -0
- data/lib/awtrix_control.rb +35 -0
- data/lib/spec/awtrix_control/app_spec.rb +661 -0
- data/lib/spec/awtrix_control/device_spec.rb +246 -0
- data/lib/spec/awtrix_control_spec.rb +6 -0
- data/lib/spec/spec_helper.rb +33 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f3ab9bbb11f50ef16f44802afcddacd4ed0735a3183c232e44629ebf180ed580
|
4
|
+
data.tar.gz: 37127243a83f3057591f1cf6c74054c25329a50badf1a4f166f0084c474f6c1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c96a251e9df3890cfa8bfb95448b246cd61f19f2ad0d9abb66af7eb893086cbd4d15363651f0697f7785b4d4c28ce02d4ad2112602178a073cc4164a5ef32c5
|
7
|
+
data.tar.gz: 78f63a8565721667c6b038948e1f081aa1da58b29af5b4b018f7025e586ac105fb478b9f5da75b094eb4ed444e4538e28734fc962f03022499f30af053a337bd
|
@@ -0,0 +1,471 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'request'
|
4
|
+
|
5
|
+
module AwtrixControl
|
6
|
+
class App
|
7
|
+
include AwtrixControl
|
8
|
+
attr_accessor :name, :payload
|
9
|
+
|
10
|
+
# Payload attributes with their types and camelCase keys:
|
11
|
+
ATTRIBUTES = {
|
12
|
+
text: { key: 'text' },
|
13
|
+
text_case: { key: 'textCase', default: 0 },
|
14
|
+
top_text: { key: 'topText', default: false },
|
15
|
+
text_offset: { key: 'textOffset', default: 0 },
|
16
|
+
center_text: { key: 'center', default: true },
|
17
|
+
text_color: { key: 'color' },
|
18
|
+
text_gradient: { key: 'gradient' },
|
19
|
+
blink_text: { key: 'blinkText' },
|
20
|
+
fade_text: { key: 'fadeText' },
|
21
|
+
background_color: { key: 'background' },
|
22
|
+
rainbow_effect: { key: 'rainbow' },
|
23
|
+
icon: { key: 'icon' },
|
24
|
+
push_icon: { key: 'pushIcon', default: 0 },
|
25
|
+
repeat_text: { key: 'repeat', default: -1 },
|
26
|
+
display_duration: { key: 'duration', default: 5 },
|
27
|
+
hold_notification: { key: 'hold', default: false },
|
28
|
+
sound: { key: 'sound' },
|
29
|
+
rtttl_sound: { key: 'rtttl' },
|
30
|
+
loop_sound: { key: 'loopSound', default: false },
|
31
|
+
bar_chart: { key: 'bar' },
|
32
|
+
line_chart: { key: 'line' },
|
33
|
+
auto_scale: { key: 'autoScale', default: true },
|
34
|
+
progress_bar: { key: 'progress', default: -1 },
|
35
|
+
progress_bar_color: { key: 'progressC', default: -1 },
|
36
|
+
progress_bar_background_color: { key: 'progressBC', default: -1 },
|
37
|
+
position: { key: 'pos' },
|
38
|
+
draw_commands: { key: 'draw' },
|
39
|
+
lifetime: { key: 'lifetime', default: 0 },
|
40
|
+
lifetime_mode: { key: 'lifetimeMode', default: 0 },
|
41
|
+
stack_notifications: { key: 'stack', default: true },
|
42
|
+
wakeup_display: { key: 'wakeup', default: false },
|
43
|
+
disable_scroll: { key: 'noScroll', default: false },
|
44
|
+
forward_clients: { key: 'clients' },
|
45
|
+
scroll_speed: { key: 'scrollSpeed', default: 100 },
|
46
|
+
background_effect: { key: 'effect' },
|
47
|
+
background_effect_settings: { key: 'effectSettings' },
|
48
|
+
save_app: { key: 'save' },
|
49
|
+
overlay_effect: { key: 'overlay' }
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
LIFETIME_MODE_OPTIONS = {
|
53
|
+
destroy: 0,
|
54
|
+
stale: 1
|
55
|
+
}.freeze
|
56
|
+
|
57
|
+
PUSH_ICON_OPTIONS = {
|
58
|
+
fixed: 0,
|
59
|
+
scroll_once: 1,
|
60
|
+
loop: 2,
|
61
|
+
}.freeze
|
62
|
+
|
63
|
+
TEXT_CASES = {
|
64
|
+
default: 0,
|
65
|
+
upcase: 1,
|
66
|
+
as_is: 2
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
# Initializes a new App instance.
|
70
|
+
#
|
71
|
+
# @param name [Symbol, String] the name of the app. Must be unique within the device, or the app will be overwritten
|
72
|
+
# @param payload [Hash, nil] the payload for the app
|
73
|
+
# @param device [Device, nil] the device to associate the app with
|
74
|
+
def initialize(name, payload: nil, device: nil)
|
75
|
+
@name = name
|
76
|
+
device << self if device
|
77
|
+
@payload = payload || default_payload
|
78
|
+
end
|
79
|
+
|
80
|
+
# Pushes the app to the associated device.
|
81
|
+
def push
|
82
|
+
return if @device.nil?
|
83
|
+
@device.push_app(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Define getters for each attribute in ATTRIBUTES.
|
87
|
+
ATTRIBUTES.each do |method_name, options|
|
88
|
+
awtrix_attribute = options[:key]
|
89
|
+
define_method(method_name) do
|
90
|
+
payload[awtrix_attribute]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Sets the autoscale attribute.
|
95
|
+
#
|
96
|
+
# @param value [Boolean] whether to autoscale the bar or line chart.
|
97
|
+
def autoscale=(value)
|
98
|
+
@payload[method_to_awtrix_key(:auto_scale)] = !!value
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sets the background color.
|
102
|
+
#
|
103
|
+
# @param value [String, Symbol, Array<Integer>] the color to set the background to
|
104
|
+
# String: A hex color code
|
105
|
+
# Symbol: A color name. Must be a key in COLOR_MAPPINGS
|
106
|
+
# Array: An array of 3 integers representing the RGB values of the color
|
107
|
+
def background_color=(value)
|
108
|
+
@payload[method_to_awtrix_key(:background_color)] = normalize_color(value)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Sets the background effect.
|
112
|
+
#
|
113
|
+
# @param value [String] the effect to apply to the background
|
114
|
+
def background_effect=(value)
|
115
|
+
@payload[method_to_awtrix_key(:background_effect)] = value
|
116
|
+
end
|
117
|
+
|
118
|
+
# Sets the background effect settings.
|
119
|
+
#
|
120
|
+
# @param value [Hash] the settings for the background effect
|
121
|
+
def background_effect_settings=(value)
|
122
|
+
@payload[method_to_awtrix_key(:background_effect_settings)] = value
|
123
|
+
end
|
124
|
+
|
125
|
+
# Sets the bar chart values.
|
126
|
+
#
|
127
|
+
# @param value [Array<Integer>] an array of integers to represent each bar
|
128
|
+
def bar_chart=(value)
|
129
|
+
@payload[method_to_awtrix_key(:bar_chart)] = value
|
130
|
+
end
|
131
|
+
|
132
|
+
# Sets the blink text frequency.
|
133
|
+
#
|
134
|
+
# @param value [Integer] the frequency (in ms) at which the text should blink
|
135
|
+
def blink_text=(value)
|
136
|
+
@payload[method_to_awtrix_key(:blink_text)] = value.to_i
|
137
|
+
end
|
138
|
+
|
139
|
+
# Gets the blink text frequency.
|
140
|
+
#
|
141
|
+
# @return [Integer] the frequency (in ms) at which the text should blink
|
142
|
+
def blink_text
|
143
|
+
@payload[method_to_awtrix_key(:blink_text)].to_i
|
144
|
+
end
|
145
|
+
|
146
|
+
# Sets whether to center the text.
|
147
|
+
#
|
148
|
+
# @param value [Boolean] whether to center the text
|
149
|
+
def center_text=(value)
|
150
|
+
@payload[method_to_awtrix_key(:center_text)] = value
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns the default payload.
|
154
|
+
#
|
155
|
+
# @return [Hash] the default payload
|
156
|
+
def default_payload
|
157
|
+
@default_payload ||=
|
158
|
+
ATTRIBUTES.each_with_object({}) do |(method_name, options), hash|
|
159
|
+
hash[options[:key]] = options[:default] if options.key?(:default)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Deletes the app from the associated device.
|
164
|
+
def delete
|
165
|
+
@device&.delete_app(self)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Gets the associated device.
|
169
|
+
#
|
170
|
+
# @return [Device, nil] the associated device
|
171
|
+
def device
|
172
|
+
@device
|
173
|
+
end
|
174
|
+
|
175
|
+
# Sets the associated device.
|
176
|
+
#
|
177
|
+
# @param new_device [Device] the new device to associate the app with
|
178
|
+
def device=(new_device)
|
179
|
+
@device.delete_app(self.name) if @device
|
180
|
+
@device = new_device
|
181
|
+
|
182
|
+
new_device << self
|
183
|
+
end
|
184
|
+
|
185
|
+
# Sets whether to disable text scrolling.
|
186
|
+
#
|
187
|
+
# @param value [Boolean] whether to disable text scrolling
|
188
|
+
def disable_scroll=(value)
|
189
|
+
@payload[method_to_awtrix_key(:disable_scroll)] = !!value
|
190
|
+
end
|
191
|
+
|
192
|
+
# Sets the display duration.
|
193
|
+
#
|
194
|
+
# @param value [Integer] for how long (in seconds) the notification should be shown
|
195
|
+
def display_duration=(value)
|
196
|
+
@payload[method_to_awtrix_key(:display_duration)] = value.to_i
|
197
|
+
end
|
198
|
+
|
199
|
+
# Sets the drawing commands.
|
200
|
+
#
|
201
|
+
# @param value [Array] array of drawing instructions
|
202
|
+
def draw_commands=(value)
|
203
|
+
@payload[method_to_awtrix_key(:draw_commands)] = value
|
204
|
+
end
|
205
|
+
|
206
|
+
# Sets the fade text frequency.
|
207
|
+
#
|
208
|
+
# @param value [Integer] the frequency (in ms) at which the text should fade in and out
|
209
|
+
def fade_text=(value)
|
210
|
+
@payload[method_to_awtrix_key(:fade_text)] = value.to_i
|
211
|
+
end
|
212
|
+
|
213
|
+
# Gets the fade text frequency.
|
214
|
+
#
|
215
|
+
# @return [Integer] the frequency (in ms) at which the text should fade in and out
|
216
|
+
def fade_text
|
217
|
+
@payload[method_to_awtrix_key(:fade_text)].to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
# Sets the forward clients.
|
221
|
+
#
|
222
|
+
# @param value [Array<String>] an array of IP addresses of other devices to forward notifications to
|
223
|
+
def forward_clients=(value)
|
224
|
+
@payload[method_to_awtrix_key(:forward_clients)] = value
|
225
|
+
end
|
226
|
+
|
227
|
+
# Sets whether to hold the notification.
|
228
|
+
#
|
229
|
+
# @param value [Boolean] whether to hold the notification
|
230
|
+
def hold_notification=(value)
|
231
|
+
@payload[method_to_awtrix_key(:hold_notification)] = !!value
|
232
|
+
end
|
233
|
+
|
234
|
+
# Sets the icon.
|
235
|
+
#
|
236
|
+
# @param value [String] the icon ID or filename (without extension) to display on the app
|
237
|
+
def icon=(value)
|
238
|
+
@payload[method_to_awtrix_key(:icon)] = value
|
239
|
+
end
|
240
|
+
|
241
|
+
# Sets the lifetime.
|
242
|
+
#
|
243
|
+
# @param value [Integer] the time in seconds after which the app should be removed if no update is received
|
244
|
+
def lifetime=(value)
|
245
|
+
@payload[method_to_awtrix_key(:lifetime)] = value.to_i
|
246
|
+
end
|
247
|
+
|
248
|
+
# Sets the lifetime mode.
|
249
|
+
#
|
250
|
+
# @param value [Symbol, Integer] the lifetime mode to use
|
251
|
+
# Symbol options: :destroy, :stale
|
252
|
+
# Integer options: 0, 1
|
253
|
+
def lifetime_mode=(value)
|
254
|
+
@payload[method_to_awtrix_key(:lifetime_mode)] = LIFETIME_MODE_OPTIONS[value] || value
|
255
|
+
end
|
256
|
+
|
257
|
+
# Gets the lifetime mode.
|
258
|
+
#
|
259
|
+
# @return [Symbol, nil] the lifetime mode
|
260
|
+
def lifetime_mode
|
261
|
+
LIFETIME_MODE_OPTIONS.key(@payload[method_to_awtrix_key(:lifetime_mode)])
|
262
|
+
end
|
263
|
+
|
264
|
+
# Sets the line chart values.
|
265
|
+
#
|
266
|
+
# @param value [Array<Integer>] an array of integers to represent the line chart
|
267
|
+
def line_chart=(value)
|
268
|
+
@payload[method_to_awtrix_key(:line_chart)] = value
|
269
|
+
end
|
270
|
+
|
271
|
+
# Sets whether to loop the sound.
|
272
|
+
#
|
273
|
+
# @param value [Boolean] whether to loop the sound as long as the notification is shown
|
274
|
+
def loop_sound=(value)
|
275
|
+
@payload[method_to_awtrix_key(:loop_sound)] = !!value
|
276
|
+
end
|
277
|
+
|
278
|
+
# Sets the overlay effect.
|
279
|
+
#
|
280
|
+
# @param value [String] the effect to apply as an overlay
|
281
|
+
def overlay_effect=(value)
|
282
|
+
@payload[method_to_awtrix_key(:overlay_effect)] = value
|
283
|
+
end
|
284
|
+
|
285
|
+
# Sets the position of the app in the loop.
|
286
|
+
#
|
287
|
+
# @param value [Integer] the position of the app in the loop (starting at 0)
|
288
|
+
def position=(value)
|
289
|
+
@payload[method_to_awtrix_key(:position)] = value
|
290
|
+
end
|
291
|
+
|
292
|
+
# Sets the progress bar value.
|
293
|
+
#
|
294
|
+
# @param value [Integer] a value between 0 and 100 to set the progress bar to
|
295
|
+
def progress_bar=(value)
|
296
|
+
@payload[method_to_awtrix_key(:progress_bar)] = value.to_i
|
297
|
+
end
|
298
|
+
|
299
|
+
# Sets the progress bar background color.
|
300
|
+
#
|
301
|
+
# @param value [String, Symbol, Array<Integer>] the color to set the progress bar background to
|
302
|
+
def progress_bar_background_color=(value)
|
303
|
+
@payload[method_to_awtrix_key(:progress_bar_background_color)] = normalize_color(value)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Sets the progress bar color.
|
307
|
+
#
|
308
|
+
# @param value [String, Symbol, Array<Integer>] the color to set the progress bar to
|
309
|
+
def progress_bar_color=(value)
|
310
|
+
@payload[method_to_awtrix_key(:progress_bar_color)] = normalize_color(value)
|
311
|
+
end
|
312
|
+
|
313
|
+
# Sets the push icon behavior.
|
314
|
+
#
|
315
|
+
# @param value [Symbol, Integer] a symbol defining the push icon behavior:
|
316
|
+
# :fixed, :scroll_once, :loop
|
317
|
+
# Or an integer between 0 and 2 representing the above symbols
|
318
|
+
def push_icon=(value)
|
319
|
+
@payload[method_to_awtrix_key(:push_icon)] = PUSH_ICON_OPTIONS[value] || value
|
320
|
+
end
|
321
|
+
|
322
|
+
# Gets the push icon behavior.
|
323
|
+
#
|
324
|
+
# @return [Symbol, nil] the push icon behavior
|
325
|
+
def push_icon
|
326
|
+
PUSH_ICON_OPTIONS.key(@payload[method_to_awtrix_key(:push_icon)])
|
327
|
+
end
|
328
|
+
|
329
|
+
# Sets whether to enable the rainbow effect.
|
330
|
+
#
|
331
|
+
# @param value [Boolean] whether to enable the rainbow effect
|
332
|
+
def rainbow_effect=(value)
|
333
|
+
@payload[method_to_awtrix_key(:rainbow_effect)] = !!value
|
334
|
+
end
|
335
|
+
|
336
|
+
# Sets the repeat text value.
|
337
|
+
#
|
338
|
+
# @param value [Integer] the number of times to repeat the text
|
339
|
+
def repeat_text=(value)
|
340
|
+
@payload[method_to_awtrix_key(:repeat_text)] = value.to_i
|
341
|
+
end
|
342
|
+
|
343
|
+
# Sets the RTTTL sound.
|
344
|
+
#
|
345
|
+
# @param value [String] a RTTTL string to play as a sound
|
346
|
+
def rtttl_sound=(value)
|
347
|
+
@payload[method_to_awtrix_key(:rtttl_sound)] = value
|
348
|
+
end
|
349
|
+
|
350
|
+
# Sets whether to save the app.
|
351
|
+
#
|
352
|
+
# @param value [Boolean] whether to save the app into flash memory
|
353
|
+
def save_app=(value)
|
354
|
+
@payload[method_to_awtrix_key(:save_app)] = !!value
|
355
|
+
end
|
356
|
+
|
357
|
+
# Sets the scroll speed.
|
358
|
+
#
|
359
|
+
# @param value [Integer] the new scroll speed, as a percentage of the original scroll speed
|
360
|
+
def scroll_speed=(value)
|
361
|
+
@payload[method_to_awtrix_key(:scroll_speed)] = value.to_i
|
362
|
+
end
|
363
|
+
|
364
|
+
# Sets whether to stack notifications.
|
365
|
+
#
|
366
|
+
# @param value [Boolean] whether to stack the notification
|
367
|
+
def stack_notifications=(value)
|
368
|
+
@payload[method_to_awtrix_key(:stack_notifications)] = !!value
|
369
|
+
end
|
370
|
+
|
371
|
+
# Sets the sound.
|
372
|
+
#
|
373
|
+
# @param value [String] the filename of the RTTTL ringtone file or the 4-digit number of the MP3
|
374
|
+
def sound=(value)
|
375
|
+
@payload[method_to_awtrix_key(:sound)] = value
|
376
|
+
end
|
377
|
+
|
378
|
+
# Sets the text.
|
379
|
+
#
|
380
|
+
# @param value [String] the text to display
|
381
|
+
def text=(value)
|
382
|
+
@payload[method_to_awtrix_key(:text)] = value
|
383
|
+
end
|
384
|
+
|
385
|
+
# Sets the text case.
|
386
|
+
#
|
387
|
+
# @param value [Symbol, Integer] the text case to use
|
388
|
+
# Symbol options: :default, :upcase, :as_is
|
389
|
+
# Integer options: 0, 1, 2
|
390
|
+
def text_case=(value)
|
391
|
+
@payload[method_to_awtrix_key(:text_case)] = TEXT_CASES[value] || value
|
392
|
+
end
|
393
|
+
|
394
|
+
# Gets the text case.
|
395
|
+
#
|
396
|
+
# @return [Symbol, nil] the text case
|
397
|
+
def text_case
|
398
|
+
TEXT_CASES.key(@payload[method_to_awtrix_key(:text_case)])
|
399
|
+
end
|
400
|
+
|
401
|
+
# Sets the text color.
|
402
|
+
#
|
403
|
+
# @param value [String, Symbol, Array<Integer>] the color to set the text to
|
404
|
+
# String: A hex color code
|
405
|
+
# Symbol: A color name. Must be a key in COLOR_MAPPINGS
|
406
|
+
# Array: An array of 3 integers representing the RGB values of the color
|
407
|
+
def text_color=(value)
|
408
|
+
@payload[method_to_awtrix_key(:text_color)] = normalize_color(value)
|
409
|
+
end
|
410
|
+
|
411
|
+
# Sets the text gradient.
|
412
|
+
#
|
413
|
+
# @param gradient [Hash] a hash with keys :from and :to, each with a color value
|
414
|
+
# String: A hex color code
|
415
|
+
# Symbol: A color name. Must be a key in COLOR_MAPPINGS
|
416
|
+
# Array: An array of 3 integers representing the RGB values of the color
|
417
|
+
def text_gradient=(gradient)
|
418
|
+
from = normalize_color(gradient[:from])
|
419
|
+
to = normalize_color(gradient[:to])
|
420
|
+
@payload[method_to_awtrix_key(:text_gradient)] = [from, to]
|
421
|
+
end
|
422
|
+
|
423
|
+
# Gets the text gradient.
|
424
|
+
#
|
425
|
+
# @return [Hash] a hash with keys :from and :to, each with a color value
|
426
|
+
def text_gradient
|
427
|
+
raw_gradient = @payload[method_to_awtrix_key(:text_gradient)]
|
428
|
+
return { from: nil, to: nil } if raw_gradient.nil?
|
429
|
+
{
|
430
|
+
from: raw_gradient[0],
|
431
|
+
to: raw_gradient[1]
|
432
|
+
}
|
433
|
+
end
|
434
|
+
|
435
|
+
# Sets the text offset.
|
436
|
+
#
|
437
|
+
# @param value [Integer] the offset for the x position of a starting text
|
438
|
+
def text_offset=(value)
|
439
|
+
@payload[method_to_awtrix_key(:text_offset)] = value
|
440
|
+
end
|
441
|
+
|
442
|
+
# Sets whether to draw the text on top.
|
443
|
+
#
|
444
|
+
# @param value [Boolean] whether to draw the text on top
|
445
|
+
def top_text=(value)
|
446
|
+
@payload[method_to_awtrix_key(:top_text)] = value
|
447
|
+
end
|
448
|
+
|
449
|
+
# Sets the text case to uppercase.
|
450
|
+
def upcase
|
451
|
+
@payload[method_to_awtrix_key(:text_case)] = 1
|
452
|
+
end
|
453
|
+
|
454
|
+
# Sets whether to wake up the display for the notification.
|
455
|
+
#
|
456
|
+
# @param value [Boolean] whether to wake up the display for the notification
|
457
|
+
def wakeup_display=(value)
|
458
|
+
@payload[method_to_awtrix_key(:wakeup_display)] = !!value
|
459
|
+
end
|
460
|
+
|
461
|
+
private
|
462
|
+
|
463
|
+
# Converts a method name to its corresponding Awtrix key.
|
464
|
+
#
|
465
|
+
# @param method_name [Symbol] the method name
|
466
|
+
# @return [String] the corresponding Awtrix key
|
467
|
+
def method_to_awtrix_key(method_name)
|
468
|
+
ATTRIBUTES[method_name][:key]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'request'
|
4
|
+
|
5
|
+
module AwtrixControl
|
6
|
+
# The Device class is used to manage the apps on an Awtrix device.
|
7
|
+
# The apps are stored in a registry within the device, so you can refer to them by name.
|
8
|
+
# Note that there is no synchronization between devices or between device and device.
|
9
|
+
# E.g. If you instantiate 2 devices for the same device, they will not be aware of each other and apps could collide,
|
10
|
+
# or race conditions could occur.
|
11
|
+
class Device
|
12
|
+
include AwtrixControl
|
13
|
+
|
14
|
+
INDICATOR_INDEX = { top: 1, center: 2, bottom: 3 }.freeze
|
15
|
+
|
16
|
+
attr_reader :apps, :host
|
17
|
+
|
18
|
+
# Initializes a new Device instance.
|
19
|
+
#
|
20
|
+
# @param host [String] the IP address or hostname of the Awtrix device
|
21
|
+
def initialize(host)
|
22
|
+
@host = host
|
23
|
+
@apps = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds an app to the device.
|
27
|
+
#
|
28
|
+
# @param app [App] the app to add
|
29
|
+
# @return [App] the added app
|
30
|
+
def <<(app)
|
31
|
+
@apps[app.name] = app
|
32
|
+
app.device = self unless app.device == self
|
33
|
+
app
|
34
|
+
end
|
35
|
+
|
36
|
+
# Retrieves an app by name.
|
37
|
+
#
|
38
|
+
# @param name [String] the name of the app
|
39
|
+
# @return [App, nil] the app if found, otherwise nil
|
40
|
+
def [](name)
|
41
|
+
@apps[name]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieves the effects from the device.
|
45
|
+
#
|
46
|
+
# @return [Hash] the effects
|
47
|
+
def effects
|
48
|
+
get('effects')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the indicator on the device.
|
52
|
+
#
|
53
|
+
# @param location [Symbol] The location of the indicator to render.
|
54
|
+
# Must be a key in INDICATOR_INDEX (:top, :center, :bottom)
|
55
|
+
# @param color [String, Array, Symbol] the color to set the indicator to.
|
56
|
+
# String: A HEX string representing the color
|
57
|
+
# Array: An array of 3 integers representing the RGB values of the color
|
58
|
+
# Symbol: A symbol representing the color. Must be a key in COLOR_MAPPINGS
|
59
|
+
# @param effect [Symbol, nil] the effect to apply (e.g., :blink, :pulse)
|
60
|
+
# @param frequency [Integer, nil] the frequency of the effect
|
61
|
+
def indicator(location, color = :white, effect: nil, frequency: nil)
|
62
|
+
payload = {}
|
63
|
+
payload[:color] = AwtrixControl::COLOR_MAPPINGS[color] || color
|
64
|
+
payload[:blink] = frequency || 500 if effect == :blink
|
65
|
+
payload[:fade] = frequency || 2000 if effect == :pulse
|
66
|
+
index = INDICATOR_INDEX[location]
|
67
|
+
post("indicator#{index.to_i}", payload)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Retrieves the current loop state.
|
71
|
+
#
|
72
|
+
# @return [Hash] the current loop state, as a Hash of apps and their current index.
|
73
|
+
# @example
|
74
|
+
# device.loop
|
75
|
+
# { "Hello" => 1, "Time" => 0, "World" => 2 }
|
76
|
+
def loop
|
77
|
+
JSON.parse(get('loop'))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a new app and adds it to the device.
|
81
|
+
#
|
82
|
+
# @param name [String] the name of the app
|
83
|
+
# @param payload [Hash] additional payload for the app
|
84
|
+
# @return [App] the created app
|
85
|
+
def new_app(name, **payload)
|
86
|
+
app = payload.empty? ? App.new(name, device: self) : App.new(name, payload:, device: self)
|
87
|
+
self << app
|
88
|
+
app
|
89
|
+
end
|
90
|
+
|
91
|
+
# Sends a notification to the device.
|
92
|
+
#
|
93
|
+
# @param text [String] the notification text
|
94
|
+
# @param payload [Hash] additional payload for the notification
|
95
|
+
def notify(text, **payload)
|
96
|
+
payload[:color] = normalize_color(payload[:color] || :white)
|
97
|
+
post('notify', { text: }.merge(payload))
|
98
|
+
end
|
99
|
+
|
100
|
+
# Sets the power state of the device.
|
101
|
+
#
|
102
|
+
# @param switch [Boolean] the power state to set
|
103
|
+
# Note that this only turns off the display, not the device itself
|
104
|
+
def power(switch)
|
105
|
+
post('power', { power: switch })
|
106
|
+
end
|
107
|
+
|
108
|
+
# Removes an indicator from the device.
|
109
|
+
#
|
110
|
+
# @param location [Symbol] the location of the indicator to remove
|
111
|
+
def remove_indicator(location)
|
112
|
+
indicator(location, :black)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Removes all indicators from the device.
|
116
|
+
def remove_indicators
|
117
|
+
INDICATOR_INDEX.each_key { |location| remove_indicator(location) }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Resets the device.
|
121
|
+
def reset
|
122
|
+
sleep(1)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Plays an RTTTL string on the device.
|
126
|
+
#
|
127
|
+
# @param rtttl_string [String] the RTTTL string to play
|
128
|
+
def rtttl(rtttl_string)
|
129
|
+
post('rtttl', rtttl_string)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Retrieves the screen state from the device.
|
133
|
+
#
|
134
|
+
# @return [Hash] the screen state
|
135
|
+
def screen
|
136
|
+
get('screen')
|
137
|
+
end
|
138
|
+
|
139
|
+
# Puts the device to sleep.
|
140
|
+
#
|
141
|
+
# @param seconds [Integer] the number of seconds to sleep
|
142
|
+
# If no seconds are provided, the device will sleep indefinitely
|
143
|
+
def sleep(seconds = 0)
|
144
|
+
payload = (seconds.to_i > 0 ? { sleep: seconds.to_i } : {})
|
145
|
+
post('sleep', **payload)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Plays a sound on the device.
|
149
|
+
#
|
150
|
+
# @param name [String] the name of the sound to play
|
151
|
+
def sound(name)
|
152
|
+
post('sound', { sound: name })
|
153
|
+
end
|
154
|
+
|
155
|
+
# Retrieves the device statistics.
|
156
|
+
#
|
157
|
+
# @return [Hash] the device statistics
|
158
|
+
def stats
|
159
|
+
get('stats')
|
160
|
+
end
|
161
|
+
|
162
|
+
# Retrieves the transitions from the device.
|
163
|
+
#
|
164
|
+
# @return [Hash] the transitions
|
165
|
+
def transitions
|
166
|
+
get('transitions')
|
167
|
+
end
|
168
|
+
|
169
|
+
# Deletes an app from the device.
|
170
|
+
#
|
171
|
+
# @param app_name [String, Symbol] the app name to delete
|
172
|
+
def delete_app(app_name)
|
173
|
+
post('custom', {}, { name: app_name })
|
174
|
+
@apps.delete(app_name)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Deletes all apps from the device.
|
178
|
+
def delete_all_apps
|
179
|
+
loop.each_key { |app_name| delete_app(app_name) }
|
180
|
+
@apps = {}
|
181
|
+
end
|
182
|
+
|
183
|
+
# Pushes an app to the device, or updates an existing one.
|
184
|
+
#
|
185
|
+
# @param app_name [Symbol, String] the app name to push
|
186
|
+
def push_app(app_name)
|
187
|
+
registered_app = @apps[app_name]
|
188
|
+
return unless registered_app
|
189
|
+
|
190
|
+
post('custom',
|
191
|
+
{ name: registered_app.name }.merge(registered_app.payload),
|
192
|
+
{ name: registered_app.name })
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module AwtrixControl
|
7
|
+
module Request
|
8
|
+
private
|
9
|
+
|
10
|
+
def get(path)
|
11
|
+
uri = uri(path)
|
12
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
13
|
+
request = Net::HTTP::Get.new(uri)
|
14
|
+
response = http.request(request)
|
15
|
+
response.body
|
16
|
+
end
|
17
|
+
|
18
|
+
def post(path, payload = {}, query_params = {})
|
19
|
+
uri = uri(path, query_params)
|
20
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
21
|
+
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
22
|
+
request.body = payload.to_json
|
23
|
+
response = http.request(request)
|
24
|
+
response.body
|
25
|
+
end
|
26
|
+
|
27
|
+
def uri(path, query_params = {})
|
28
|
+
query_params = query_params.map { |k, v| "#{k}=#{v}" }.join('&')
|
29
|
+
query_params = "?#{query_params}" unless query_params.empty?
|
30
|
+
URI("http://#{host}/api/#{path}#{query_params}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|