huebot 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/huebot/bot.rb +15 -3
- data/lib/huebot/cli/helpers.rb +1 -1
- data/lib/huebot/cli/runner.rb +14 -0
- data/lib/huebot/compiler/api_v1.rb +89 -11
- data/lib/huebot/compiler/compiler.rb +1 -1
- data/lib/huebot/program.rb +6 -4
- data/lib/huebot/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fd718fb625c5f417b31619579459bdfcaffac98276d9981882df7d9b1e192b9
|
4
|
+
data.tar.gz: 213fb99651e6826283d4c0be21c26e9442f146ea2df1734a2acb5c53f0e6eafa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: caa7f46fb1d7ac5954f7b1e8f8b48c81b69c3cea59c7ce0ad246eb6ecafda318bd161eec1aff2a41e7e8f6a84af4d2a18a555f56dab808e97b39ff3396b91d9c
|
7
|
+
data.tar.gz: 2221b64eb67ab257232358e52657446cb3b8c95d7f102220954ee71e9a85bc1447ec1566712b651bd1a0d5c4db8be2eff82097551302186cd092688ad0df5548
|
data/lib/huebot/bot.rb
CHANGED
@@ -41,7 +41,7 @@ module Huebot
|
|
41
41
|
# TODO error handling
|
42
42
|
_res = device.set_state i.state
|
43
43
|
@logger.log :set_state, {device: device.name, state: i.state, result: nil}
|
44
|
-
wait time if i.wait
|
44
|
+
wait Program::AST::Num.new(time) if i.wait
|
45
45
|
}
|
46
46
|
}.map(&:join)
|
47
47
|
wait i.pause.post if i.pause&.post
|
@@ -81,7 +81,7 @@ module Huebot
|
|
81
81
|
wait lp.pause.post if lp.pause&.post
|
82
82
|
}
|
83
83
|
when Program::AST::CountedLoop
|
84
|
-
lp.n.times {
|
84
|
+
number(lp.n).round.times {
|
85
85
|
wait lp.pause.pre if lp.pause&.pre
|
86
86
|
yield :counted
|
87
87
|
wait lp.pause.post if lp.pause&.post
|
@@ -124,11 +124,23 @@ module Huebot
|
|
124
124
|
}
|
125
125
|
end
|
126
126
|
|
127
|
-
def wait(
|
127
|
+
def wait(n)
|
128
|
+
seconds = number n
|
128
129
|
@logger.log :pause, {time: seconds}
|
129
130
|
@waiter.call seconds
|
130
131
|
end
|
131
132
|
|
133
|
+
def number(n)
|
134
|
+
case n
|
135
|
+
when Program::AST::Num
|
136
|
+
n.n
|
137
|
+
when Program::AST::RandomNum
|
138
|
+
rand(n.min..n.max)
|
139
|
+
else
|
140
|
+
raise Error, "Unknown numeric type. Expected Program::AST::Num, Program::AST::NRandomNum, found: #{n.class.name}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
132
144
|
module Waiter
|
133
145
|
def self.call(seconds)
|
134
146
|
sleep seconds
|
data/lib/huebot/cli/helpers.rb
CHANGED
data/lib/huebot/cli/runner.rb
CHANGED
@@ -8,6 +8,8 @@ module Huebot
|
|
8
8
|
rescue ::Huebot::Error => e
|
9
9
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
10
10
|
return 1
|
11
|
+
rescue Interrupt
|
12
|
+
return 130
|
11
13
|
end
|
12
14
|
|
13
15
|
def self.run(sources, lights, groups, opts)
|
@@ -25,6 +27,8 @@ module Huebot
|
|
25
27
|
rescue ::Huebot::Error => e
|
26
28
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
27
29
|
return 1
|
30
|
+
rescue Interrupt
|
31
|
+
return 130
|
28
32
|
end
|
29
33
|
|
30
34
|
def self.check(sources, lights, groups, opts)
|
@@ -47,6 +51,8 @@ module Huebot
|
|
47
51
|
rescue ::Huebot::Error => e
|
48
52
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
49
53
|
return 1
|
54
|
+
rescue Interrupt
|
55
|
+
return 130
|
50
56
|
end
|
51
57
|
|
52
58
|
def self.get_state(lights, groups, opts)
|
@@ -56,6 +62,8 @@ module Huebot
|
|
56
62
|
opts.stdout.puts " #{device.get_state}"
|
57
63
|
end
|
58
64
|
0
|
65
|
+
rescue Interrupt
|
66
|
+
return 130
|
59
67
|
end
|
60
68
|
|
61
69
|
def self.set_ip(config, ip, opts)
|
@@ -64,6 +72,8 @@ module Huebot
|
|
64
72
|
rescue ::Huebot::Error => e
|
65
73
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
66
74
|
return 1
|
75
|
+
rescue Interrupt
|
76
|
+
return 130
|
67
77
|
end
|
68
78
|
|
69
79
|
def self.clear_ip(config, opts)
|
@@ -72,6 +82,8 @@ module Huebot
|
|
72
82
|
rescue ::Huebot::Error => e
|
73
83
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
74
84
|
return 1
|
85
|
+
rescue Interrupt
|
86
|
+
return 130
|
75
87
|
end
|
76
88
|
|
77
89
|
def self.unregister(config, opts)
|
@@ -80,6 +92,8 @@ module Huebot
|
|
80
92
|
rescue ::Huebot::Error => e
|
81
93
|
opts.stderr.puts "#{e.class.name}: #{e.message}"
|
82
94
|
return 1
|
95
|
+
rescue Interrupt
|
96
|
+
return 130
|
83
97
|
end
|
84
98
|
end
|
85
99
|
end
|
@@ -10,6 +10,8 @@ module Huebot
|
|
10
10
|
PARALLEL_KEYS = ["parallel"].freeze
|
11
11
|
INFINITE_KEYS = ["infinite"].freeze
|
12
12
|
COUNT_KEYS = ["count"].freeze
|
13
|
+
RANDOM_KEYS = ["random"].freeze
|
14
|
+
MIN_MAX = ["min", "max"].freeze
|
13
15
|
TIMER_KEYS = ["timer"].freeze
|
14
16
|
DEADLINE_KEYS = ["until"].freeze
|
15
17
|
HHMM = /\A[0-9]{2}:[0-9]{2}\Z/.freeze
|
@@ -187,26 +189,54 @@ module Huebot
|
|
187
189
|
lp =
|
188
190
|
case loop_val.keys
|
189
191
|
when INFINITE_KEYS
|
190
|
-
loop_val
|
192
|
+
loop_val.fetch("infinite") == true ? Program::AST::InfiniteLoop.new : Program::AST::CountedLoop.new(Program::AST::Num.new(1))
|
191
193
|
when COUNT_KEYS
|
192
|
-
num = loop_val
|
194
|
+
num = loop_val.fetch("count")
|
193
195
|
errors << "'loop.count' must be an integer. Found '#{num.class.name}'" unless num.is_a? Integer
|
194
|
-
Program::AST::CountedLoop.new(num)
|
196
|
+
Program::AST::CountedLoop.new(Program::AST::Num.new(num))
|
197
|
+
when RANDOM_KEYS
|
198
|
+
n = build_random loop_val, errors, warnings
|
199
|
+
Program::AST::CountedLoop.new(n)
|
195
200
|
when TIMER_KEYS
|
196
|
-
build_timer_loop loop_val
|
201
|
+
build_timer_loop loop_val.fetch("timer"), errors, warnings
|
197
202
|
when DEADLINE_KEYS
|
198
|
-
build_deadline_loop loop_val
|
203
|
+
build_deadline_loop loop_val.fetch("until"), errors, warnings
|
199
204
|
else
|
200
205
|
errors << "'loop' must contain exactly one of: 'infinite', 'count', 'timer', or 'until', and optionally 'pause'. Found: #{loop_val.keys.join ", "}"
|
201
|
-
Program::AST::CountedLoop.new(1)
|
206
|
+
Program::AST::CountedLoop.new(Program::AST::Num.new(1))
|
202
207
|
end
|
203
208
|
lp.pause = pause
|
204
209
|
lp
|
205
210
|
when nil
|
206
|
-
Program::AST::CountedLoop.new(1)
|
211
|
+
Program::AST::CountedLoop.new(Program::AST::Num.new(1))
|
207
212
|
else
|
208
213
|
errors << "'loop' must be an object. Found '#{loop_val.class.name}'"
|
209
|
-
Program::AST::CountedLoop.new(1)
|
214
|
+
Program::AST::CountedLoop.new(Program::AST::Num.new(1))
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def build_random(t, errors, warnings)
|
219
|
+
random = t.delete("random") || {}
|
220
|
+
min = build_random_n(random, "min", errors, warnings)
|
221
|
+
max = build_random_n(random, "max", errors, warnings)
|
222
|
+
errors << "'random.max' must be greater than 'random.min'" unless max > min
|
223
|
+
errors << "Unknown keys in 'random': #{random.keys.join ", "}" if random.keys.any?
|
224
|
+
Program::AST::RandomNum.new(min, max)
|
225
|
+
end
|
226
|
+
|
227
|
+
def build_random_n(t, name, errors, warnings)
|
228
|
+
n = t.delete name
|
229
|
+
case n
|
230
|
+
when Integer, Float
|
231
|
+
if n >= 0
|
232
|
+
n
|
233
|
+
else
|
234
|
+
errors << "'random.#{name}' must be >= 0, found #{n}"
|
235
|
+
0
|
236
|
+
end
|
237
|
+
else
|
238
|
+
errors << "'random.#{name}' must be an integer or float > 0, found #{n.class.name}"
|
239
|
+
0
|
210
240
|
end
|
211
241
|
end
|
212
242
|
|
@@ -234,6 +264,7 @@ module Huebot
|
|
234
264
|
case @api_version
|
235
265
|
when 1.0 then build_pause_1_0(t, errors, warnings)
|
236
266
|
when 1.1 then build_pause_1_1(t, errors, warnings)
|
267
|
+
when 1.2 then build_pause_1_2(t, errors, warnings)
|
237
268
|
else raise Error, "Unknown api version '#{@api_version}'"
|
238
269
|
end
|
239
270
|
end
|
@@ -242,7 +273,7 @@ module Huebot
|
|
242
273
|
pause_val = t.delete "pause"
|
243
274
|
case pause_val
|
244
275
|
when Integer, Float
|
245
|
-
Program::AST::Pause.new(nil, pause_val)
|
276
|
+
Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
|
246
277
|
when nil
|
247
278
|
nil
|
248
279
|
else
|
@@ -255,22 +286,69 @@ module Huebot
|
|
255
286
|
pause_val = t.delete "pause"
|
256
287
|
case pause_val
|
257
288
|
when Integer, Float
|
258
|
-
Program::AST::Pause.new(nil, pause_val)
|
289
|
+
Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
|
259
290
|
when Hash
|
260
291
|
pre = pause_val.delete "before"
|
261
292
|
post = pause_val.delete "after"
|
262
293
|
errors << "'pause.before' must be an integer or float" unless pre.nil? or pre.is_a? Integer or pre.is_a? Float
|
263
294
|
errors << "'pause.after' must be an integer or float" unless post.nil? or post.is_a? Integer or post.is_a? Float
|
264
295
|
errors << "Unknown keys in 'pause': #{pause_val.keys.join ", "}" if pause_val.keys.any?
|
296
|
+
pre = Program::AST::Num.new(pre) if pre
|
297
|
+
post = Program::AST::Num.new(post) if post
|
265
298
|
Program::AST::Pause.new(pre, post)
|
266
299
|
when nil
|
267
300
|
nil
|
268
301
|
else
|
269
|
-
errors << "'pause' must be an integer or float"
|
302
|
+
errors << "'pause' must be an integer or float, or an object with 'before' and/or 'after'"
|
270
303
|
nil
|
271
304
|
end
|
272
305
|
end
|
273
306
|
|
307
|
+
def build_pause_1_2(t, errors, warnings)
|
308
|
+
pause_val = t.delete "pause"
|
309
|
+
case pause_val
|
310
|
+
when Integer, Float
|
311
|
+
Program::AST::Pause.new(nil, Program::AST::Num.new(pause_val))
|
312
|
+
when Hash
|
313
|
+
pre = build_pause_part(pause_val, "before", errors, warnings)
|
314
|
+
post = build_pause_part(pause_val, "after", errors, warnings)
|
315
|
+
errors << "'pause' requires one or both of 'before' or 'after'" if pre.nil? and post.nil?
|
316
|
+
errors << "Unknown keys in 'pause': #{pause_val.keys.join ", "}" if pause_val.keys.any?
|
317
|
+
Program::AST::Pause.new(pre, post)
|
318
|
+
when nil
|
319
|
+
nil
|
320
|
+
else
|
321
|
+
errors << "'pause' must be an integer or float, or an object with 'before' and/or 'after'"
|
322
|
+
nil
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def build_pause_part(t, part, errors, warnings)
|
327
|
+
val = t.delete part
|
328
|
+
case val
|
329
|
+
when nil
|
330
|
+
nil
|
331
|
+
when Integer, Float
|
332
|
+
Program::AST::Num.new(val)
|
333
|
+
when Hash
|
334
|
+
if @api_version < 1.2
|
335
|
+
errors << "Unknown 'pause.#{part}' type (#{val.class.name})"
|
336
|
+
return Program::AST::Num.new(1)
|
337
|
+
end
|
338
|
+
|
339
|
+
case val.keys
|
340
|
+
when RANDOM_KEYS
|
341
|
+
build_random val, errors, warnings
|
342
|
+
else
|
343
|
+
errors << "Expected 'pause.#{part}' to contain 'random', found #{val.keys.join ", "}"
|
344
|
+
Program::AST::Num.new(1)
|
345
|
+
end
|
346
|
+
else
|
347
|
+
errors << "Unknown 'pause.#{part}' type (#{val.class.name})"
|
348
|
+
Program::AST::Num.new(1)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
274
352
|
def build_wait(t, errors, warnings)
|
275
353
|
wait = t.delete "wait"
|
276
354
|
case wait
|
@@ -13,7 +13,7 @@ module Huebot
|
|
13
13
|
def self.build(src)
|
14
14
|
compiler_class =
|
15
15
|
case src.api_version
|
16
|
-
when 1.0, 1.1 then ApiV1
|
16
|
+
when 1.0, 1.1, 1.2 then ApiV1
|
17
17
|
else raise Error, "Unknown API version '#{src.api_version}'"
|
18
18
|
end
|
19
19
|
compiler = compiler_class.new(src.api_version)
|
data/lib/huebot/program.rb
CHANGED
@@ -26,6 +26,8 @@ module Huebot
|
|
26
26
|
DeadlineLoop = Struct.new(:stop_time, :pause)
|
27
27
|
|
28
28
|
Pause = Struct.new(:pre, :post)
|
29
|
+
Num = Struct.new(:n)
|
30
|
+
RandomNum = Struct.new(:min, :max)
|
29
31
|
DeviceRef = Struct.new(:ref)
|
30
32
|
Light = Struct.new(:name)
|
31
33
|
Group = Struct.new(:name)
|
@@ -56,14 +58,14 @@ module Huebot
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def errors(node = data)
|
59
|
-
node.children.reduce(node.errors) { |
|
60
|
-
|
61
|
+
node.children.reduce(node.errors) { |acc, child|
|
62
|
+
acc + errors(child)
|
61
63
|
}
|
62
64
|
end
|
63
65
|
|
64
66
|
def warnings(node = data)
|
65
|
-
node.children.reduce(node.warnings) { |
|
66
|
-
|
67
|
+
node.children.reduce(node.warnings) { |acc, child|
|
68
|
+
acc + warnings(child)
|
67
69
|
}
|
68
70
|
end
|
69
71
|
|
data/lib/huebot/version.rb
CHANGED