awtrix_control 0.0.1
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.
- 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
|