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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 643b636258939c0eb021d13d9369d845be85a5854ca1f6c38fa66c6556551c68
4
- data.tar.gz: e5e1e74fded7ed9d456f8a7ce0c65f8f787a08187eb019413bddaff7bddaca74
3
+ metadata.gz: 2fd718fb625c5f417b31619579459bdfcaffac98276d9981882df7d9b1e192b9
4
+ data.tar.gz: 213fb99651e6826283d4c0be21c26e9442f146ea2df1734a2acb5c53f0e6eafa
5
5
  SHA512:
6
- metadata.gz: '084232072462be01ec7867aadb43df7b476e4c0b6fe0fcc33b1d10cd23f568b8474d83ed450f0487123ce874c33a729d84bfa9e4cc683a59958e3b5fe7bd64e0'
7
- data.tar.gz: 99c1ba63f4ff21161017fe42180ae76c5ee27721e32e7663eb5d09906953ef88886dd7e003f40e783e0ede9e3e5e123fab5635b5e0413085bf5882ebcb386bb8
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(seconds)
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
@@ -5,7 +5,7 @@ require 'json'
5
5
  module Huebot
6
6
  module CLI
7
7
  module Helpers
8
- DEFAULT_API_VERSION = 1.1
8
+ DEFAULT_API_VERSION = 1.2
9
9
 
10
10
  #
11
11
  # Returns the command given to huebot.
@@ -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["infinite"] == true ? Program::AST::InfiniteLoop.new : Program::AST::CountedLoop.new(1)
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["count"]
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["timer"], errors, warnings
201
+ build_timer_loop loop_val.fetch("timer"), errors, warnings
197
202
  when DEADLINE_KEYS
198
- build_deadline_loop loop_val["until"], errors, warnings
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)
@@ -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) { |errors, child|
60
- errors + child.errors
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) { |warnings, child|
66
- warnings + child.warnings
67
+ node.children.reduce(node.warnings) { |acc, child|
68
+ acc + warnings(child)
67
69
  }
68
70
  end
69
71
 
@@ -1,4 +1,4 @@
1
1
  module Huebot
2
2
  # Gem version
3
- VERSION = '1.3.0'
3
+ VERSION = '1.4.0'
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: huebot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger