huebot 1.2.0 → 1.3.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: 7febcd2a42112b4c3636581d9419a58c490a1157637e143fd46e6e7d94aefba5
4
- data.tar.gz: d4a9ab3178d381786595f14a14899e55af7789b4650cfa2da1928b83b5fe2135
3
+ metadata.gz: 643b636258939c0eb021d13d9369d845be85a5854ca1f6c38fa66c6556551c68
4
+ data.tar.gz: e5e1e74fded7ed9d456f8a7ce0c65f8f787a08187eb019413bddaff7bddaca74
5
5
  SHA512:
6
- metadata.gz: 15150d4cd9652f7d5e2518bd714de8155f1b574054360f61c0ac383c30f6fe81e3421f0bf64f0da43f090dabd2677b71609ae0a1e4f3741524adcdd631bd1116
7
- data.tar.gz: 548d5adc4af710d2b1c9719e54fad7aa5fe5ba18749845089d0cb66fb59b4fd5e8279acc4214e961757c9956bad0b79ed8dacf4e531e3f54097322ce44a6b4bb
6
+ metadata.gz: '084232072462be01ec7867aadb43df7b476e4c0b6fe0fcc33b1d10cd23f568b8474d83ed450f0487123ce874c33a729d84bfa9e4cc683a59958e3b5fe7bd64e0'
7
+ data.tar.gz: 99c1ba63f4ff21161017fe42180ae76c5ee27721e32e7663eb5d09906953ef88886dd7e003f40e783e0ede9e3e5e123fab5635b5e0413085bf5882ebcb386bb8
data/README.md CHANGED
@@ -34,13 +34,15 @@ serial:
34
34
  state:
35
35
  bri: 150
36
36
  time: 10 # 10 second transition
37
- pause: 2 # 2 second pause before the next step
37
+ pause:
38
+ after: 2 # 2 second pause before the next step
38
39
 
39
40
  - transition:
40
41
  state:
41
42
  bri: 254
42
43
  time: 10 # 10 second transition
43
- pause: 2 # 2 second pause before the next step
44
+ pause:
45
+ after: 2 # 2 second pause before the next step
44
46
 
45
47
  - transition:
46
48
  state:
@@ -78,12 +80,14 @@ serial:
78
80
  state:
79
81
  bri: 254
80
82
  time: 10 # transition over 10 seconds
81
- pause: 5 # pause an extra 5 sec after the transition
83
+ pause:
84
+ after: 5 # pause an extra 5 sec after the transition
82
85
  - transition:
83
86
  state:
84
87
  bri: 25
85
88
  time: 10
86
- pause: 5
89
+ pause:
90
+ after: 5
87
91
 
88
92
  # Parallel branch 2: Fade inputs #2 and #4 down and up
89
93
  - serial:
@@ -96,12 +100,14 @@ serial:
96
100
  state:
97
101
  bri: 25
98
102
  time: 10
99
- pause: 5
103
+ pause:
104
+ after: 5
100
105
  - transition:
101
106
  state:
102
107
  bri: 254
103
108
  time: 10
104
- pause: 5
109
+ pause:
110
+ after: 5
105
111
  ```
106
112
 
107
113
  [See the Wiki](https://github.com/jhollinger/huebot/wiki) for more documentation and examples.
data/lib/huebot/bot.rb CHANGED
@@ -18,47 +18,49 @@ module Huebot
18
18
  private
19
19
 
20
20
  def exec(node)
21
- i = node.instruction
22
- case i
21
+ case node.instruction
23
22
  when Program::AST::Transition
24
- transition i.state, i.devices, i.sleep
23
+ transition node.instruction
25
24
  when Program::AST::SerialControl
26
- serial node.children, i.loop, i.sleep
25
+ serial node.children, node.instruction
27
26
  when Program::AST::ParallelControl
28
- parallel node.children, i.loop, i.sleep
27
+ parallel node.children, node.instruction
29
28
  else
30
- raise Error, "Unexpected instruction '#{i.class.name}'"
29
+ raise Error, "Unexpected instruction '#{node.instruction.class.name}'"
31
30
  end
32
31
  end
33
32
 
34
- def transition(state, device_refs, sleep_time = nil)
35
- time = (state["transitiontime"] || 4).to_f / 10
36
- devices = map_devices device_refs
33
+ def transition(i)
34
+ time = (i.state["transitiontime"] || 4).to_f / 10
35
+ devices = map_devices i.devices
37
36
  @logger.log :transition, {devices: devices.map(&:name)}
38
37
 
38
+ wait i.pause.pre if i.pause&.pre
39
39
  devices.map { |device|
40
40
  Thread.new {
41
41
  # TODO error handling
42
- _res = device.set_state state
43
- @logger.log :set_state, {device: device.name, state: state, result: nil}
44
- wait time
42
+ _res = device.set_state i.state
43
+ @logger.log :set_state, {device: device.name, state: i.state, result: nil}
44
+ wait time if i.wait
45
45
  }
46
46
  }.map(&:join)
47
- wait sleep_time if sleep_time
47
+ wait i.pause.post if i.pause&.post
48
48
  end
49
49
 
50
- def serial(nodes, lp, sleep_time = nil)
51
- control_loop(lp) { |loop_type|
50
+ def serial(nodes, i)
51
+ wait i.pause.pre if i.pause&.pre
52
+ control_loop(i.loop) { |loop_type|
52
53
  @logger.log :serial, {loop: loop_type}
53
54
  nodes.each { |node|
54
55
  exec node
55
56
  }
56
57
  }
57
- wait sleep_time if sleep_time
58
+ wait i.pause.post if i.pause&.post
58
59
  end
59
60
 
60
- def parallel(nodes, lp, sleep_time = nil)
61
- control_loop(lp) { |loop_type|
61
+ def parallel(nodes, i)
62
+ wait i.pause.pre if i.pause&.pre
63
+ control_loop(i.loop) { |loop_type|
62
64
  @logger.log :parallel, {loop: loop_type}
63
65
  nodes.map { |node|
64
66
  Thread.new {
@@ -67,33 +69,37 @@ module Huebot
67
69
  }
68
70
  }.map(&:join)
69
71
  }
70
- wait sleep_time if sleep_time
72
+ wait i.pause.post if i.pause&.post
71
73
  end
72
74
 
73
75
  def control_loop(lp)
74
76
  case lp
75
77
  when Program::AST::InfiniteLoop
76
78
  loop {
79
+ wait lp.pause.pre if lp.pause&.pre
77
80
  yield :infinite
78
- wait lp.pause if lp.pause
81
+ wait lp.pause.post if lp.pause&.post
79
82
  }
80
83
  when Program::AST::CountedLoop
81
84
  lp.n.times {
85
+ wait lp.pause.pre if lp.pause&.pre
82
86
  yield :counted
83
- wait lp.pause if lp.pause
87
+ wait lp.pause.post if lp.pause&.post
84
88
  }
85
89
  when Program::AST::DeadlineLoop
86
90
  until Time.now >= lp.stop_time
91
+ wait lp.pause.pre if lp.pause&.pre
87
92
  yield :deadline
88
- wait lp.pause if lp.pause
93
+ wait lp.pause.post if lp.pause&.post
89
94
  end
90
95
  when Program::AST::TimerLoop
91
96
  sec = ((lp.hours * 60) + lp.minutes) * 60
92
97
  time = 0
93
98
  until time >= sec
94
99
  start = Time.now
100
+ wait lp.pause.pre if lp.pause&.pre
95
101
  yield :timer
96
- wait lp.pause if lp.pause
102
+ wait lp.pause.post if lp.pause&.post
97
103
  time += (Time.now - start).round
98
104
  end
99
105
  else
@@ -5,6 +5,8 @@ require 'json'
5
5
  module Huebot
6
6
  module CLI
7
7
  module Helpers
8
+ DEFAULT_API_VERSION = 1.1
9
+
8
10
  #
9
11
  # Returns the command given to huebot.
10
12
  #
@@ -70,7 +72,7 @@ module Huebot
70
72
  opts.stderr.puts "Unknown file extension '#{ext}'. Expected .yaml, .yml, or .json"
71
73
  return []
72
74
  end
73
- version = (src.delete("version") || 1.0).to_f
75
+ version = (src.delete("version") || DEFAULT_API_VERSION).to_f
74
76
  Program::Src.new(src, path, version)
75
77
  }
76
78
 
@@ -80,7 +82,7 @@ module Huebot
80
82
  src = raw[0] == "{" ? JSON.load(raw) : YAML.safe_load(raw)
81
83
 
82
84
  opts.stdout.puts "Executing..." if opts.read_stdin
83
- version = (src.delete("version") || 1.0).to_f
85
+ version = (src.delete("version") || DEFAULT_API_VERSION).to_f
84
86
  sources << Program::Src.new(src, "STDIN", version)
85
87
  end
86
88
  sources
@@ -51,40 +51,56 @@ module Huebot
51
51
  end
52
52
 
53
53
  def build_transition(t, errors, warnings, inherited_devices = nil)
54
+ if t.nil?
55
+ errors << "'transition' may not be blank"
56
+ t = {}
57
+ end
58
+
54
59
  state = build_state(t, errors, warnings)
55
60
  devices = build_devices(t, errors, warnings, inherited_devices)
56
- slp = build_sleep(t, errors, warnings)
61
+ pause = build_pause(t, errors, warnings)
62
+ wait = @api_version >= 1.1 ? build_wait(t, errors, warnings) : true
57
63
 
58
64
  errors << "'transition' requires devices" if devices.empty?
59
65
  errors << "Unknown keys in 'transition': #{t.keys.join ", "}" if t.keys.any?
60
66
 
61
- instruction = Program::AST::Transition.new(state, devices, slp)
67
+ instruction = Program::AST::Transition.new(state, devices, wait, pause)
62
68
  return instruction, []
63
69
  end
64
70
 
65
71
  def build_serial(t, errors, warnings, inherited_devices = nil)
72
+ if t.nil?
73
+ errors << "'serial' may not be blank"
74
+ t = {}
75
+ end
76
+
66
77
  lp = build_loop(t, errors, warnings)
67
- slp = build_sleep(t, errors, warnings)
78
+ pause = build_pause(t, errors, warnings)
68
79
  devices = build_devices(t, errors, warnings, inherited_devices)
69
80
  children = build_steps(t, errors, warnings, devices)
70
81
 
71
82
  errors << "'serial' requires steps" if children.empty?
72
83
  errors << "Unknown keys in 'serial': #{t.keys.join ", "}" if t.keys.any?
73
84
 
74
- instruction = Program::AST::SerialControl.new(lp, slp)
85
+ instruction = Program::AST::SerialControl.new(lp, pause)
75
86
  return instruction, children
76
87
  end
77
88
 
78
89
  def build_parallel(t, errors, warnings, inherited_devices = nil)
90
+ if t.nil?
91
+ errors << "'parallel' may not be blank"
92
+ t = {}
93
+ end
94
+
79
95
  lp = build_loop(t, errors, warnings)
80
- slp = build_sleep(t, errors, warnings)
96
+ pause = build_pause(t, errors, warnings)
81
97
  devices = build_devices(t, errors, warnings, inherited_devices)
82
98
  children = build_steps(t, errors, warnings, devices)
83
99
 
84
100
  errors << "'parallel' requires steps" if children.empty?
85
101
  errors << "Unknown keys in 'parallel': #{t.keys.join ", "}" if t.keys.any?
86
102
 
87
- instruction = Program::AST::ParallelControl.new(lp, slp)
103
+ instruction = Program::AST::ParallelControl.new(lp, pause)
88
104
  return instruction, children
89
105
  end
90
106
 
@@ -167,9 +183,7 @@ module Huebot
167
183
  loop_val = t.delete "loop"
168
184
  case loop_val
169
185
  when Hash
170
- pause = loop_val.delete "pause"
171
- errors << "'loop.pause' must be an integer. Found '#{pause.class.name}'" if pause and !pause.is_a? Integer
172
-
186
+ pause = build_pause(loop_val, errors, warnings)
173
187
  lp =
174
188
  case loop_val.keys
175
189
  when INFINITE_KEYS
@@ -216,11 +230,19 @@ module Huebot
216
230
  Program::AST::DeadlineLoop.new(stop_time)
217
231
  end
218
232
 
219
- def build_sleep(t, errors, warnings)
220
- sleep_val = t.delete "pause"
221
- case sleep_val
233
+ def build_pause(t, errors, warnings)
234
+ case @api_version
235
+ when 1.0 then build_pause_1_0(t, errors, warnings)
236
+ when 1.1 then build_pause_1_1(t, errors, warnings)
237
+ else raise Error, "Unknown api version '#{@api_version}'"
238
+ end
239
+ end
240
+
241
+ def build_pause_1_0(t, errors, warnings)
242
+ pause_val = t.delete "pause"
243
+ case pause_val
222
244
  when Integer, Float
223
- sleep_val
245
+ Program::AST::Pause.new(nil, pause_val)
224
246
  when nil
225
247
  nil
226
248
  else
@@ -229,6 +251,39 @@ module Huebot
229
251
  end
230
252
  end
231
253
 
254
+ def build_pause_1_1(t, errors, warnings)
255
+ pause_val = t.delete "pause"
256
+ case pause_val
257
+ when Integer, Float
258
+ Program::AST::Pause.new(nil, pause_val)
259
+ when Hash
260
+ pre = pause_val.delete "before"
261
+ post = pause_val.delete "after"
262
+ errors << "'pause.before' must be an integer or float" unless pre.nil? or pre.is_a? Integer or pre.is_a? Float
263
+ errors << "'pause.after' must be an integer or float" unless post.nil? or post.is_a? Integer or post.is_a? Float
264
+ errors << "Unknown keys in 'pause': #{pause_val.keys.join ", "}" if pause_val.keys.any?
265
+ Program::AST::Pause.new(pre, post)
266
+ when nil
267
+ nil
268
+ else
269
+ errors << "'pause' must be an integer or float"
270
+ nil
271
+ end
272
+ end
273
+
274
+ def build_wait(t, errors, warnings)
275
+ wait = t.delete "wait"
276
+ case wait
277
+ when true, false
278
+ wait
279
+ when nil
280
+ true
281
+ else
282
+ errors << "'transition.wait' must be true or false"
283
+ true
284
+ end
285
+ end
286
+
232
287
  def build_devices(t, errors, warnings, inherited_devices = nil)
233
288
  devices_ref = t.delete("devices") || {}
234
289
  return inherited_devices if devices_ref.empty? and inherited_devices
@@ -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 then ApiV1
16
+ when 1.0, 1.1 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)
@@ -16,15 +16,16 @@ module Huebot
16
16
  module AST
17
17
  Node = Struct.new(:instruction, :children, :errors, :warnings)
18
18
 
19
- Transition = Struct.new(:state, :devices, :sleep)
20
- SerialControl = Struct.new(:loop, :sleep)
21
- ParallelControl = Struct.new(:loop, :sleep)
19
+ Transition = Struct.new(:state, :devices, :wait, :pause)
20
+ SerialControl = Struct.new(:loop, :pause)
21
+ ParallelControl = Struct.new(:loop, :pause)
22
22
 
23
23
  InfiniteLoop = Struct.new(:pause)
24
24
  CountedLoop = Struct.new(:n, :pause)
25
25
  TimerLoop = Struct.new(:hours, :minutes, :pause)
26
26
  DeadlineLoop = Struct.new(:stop_time, :pause)
27
27
 
28
+ Pause = Struct.new(:pre, :post)
28
29
  DeviceRef = Struct.new(:ref)
29
30
  Light = Struct.new(:name)
30
31
  Group = Struct.new(:name)
@@ -1,4 +1,4 @@
1
1
  module Huebot
2
2
  # Gem version
3
- VERSION = '1.2.0'
3
+ VERSION = '1.3.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: huebot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-21 00:00:00.000000000 Z
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest