huebot 1.2.0 → 1.3.0
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 +4 -4
- data/README.md +12 -6
- data/lib/huebot/bot.rb +29 -23
- data/lib/huebot/cli/helpers.rb +4 -2
- data/lib/huebot/compiler/api_v1.rb +68 -13
- data/lib/huebot/compiler/compiler.rb +1 -1
- data/lib/huebot/program.rb +4 -3
- data/lib/huebot/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 643b636258939c0eb021d13d9369d845be85a5854ca1f6c38fa66c6556551c68
|
4
|
+
data.tar.gz: e5e1e74fded7ed9d456f8a7ce0c65f8f787a08187eb019413bddaff7bddaca74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
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:
|
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:
|
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:
|
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:
|
103
|
+
pause:
|
104
|
+
after: 5
|
100
105
|
- transition:
|
101
106
|
state:
|
102
107
|
bri: 254
|
103
108
|
time: 10
|
104
|
-
pause:
|
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
|
-
|
22
|
-
case i
|
21
|
+
case node.instruction
|
23
22
|
when Program::AST::Transition
|
24
|
-
transition
|
23
|
+
transition node.instruction
|
25
24
|
when Program::AST::SerialControl
|
26
|
-
serial node.children,
|
25
|
+
serial node.children, node.instruction
|
27
26
|
when Program::AST::ParallelControl
|
28
|
-
parallel node.children,
|
27
|
+
parallel node.children, node.instruction
|
29
28
|
else
|
30
|
-
raise Error, "Unexpected instruction '#{
|
29
|
+
raise Error, "Unexpected instruction '#{node.instruction.class.name}'"
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def transition(
|
35
|
-
time = (state["transitiontime"] || 4).to_f / 10
|
36
|
-
devices = map_devices
|
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
|
47
|
+
wait i.pause.post if i.pause&.post
|
48
48
|
end
|
49
49
|
|
50
|
-
def serial(nodes,
|
51
|
-
|
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
|
58
|
+
wait i.pause.post if i.pause&.post
|
58
59
|
end
|
59
60
|
|
60
|
-
def parallel(nodes,
|
61
|
-
|
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
|
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
|
data/lib/huebot/cli/helpers.rb
CHANGED
@@ -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") ||
|
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") ||
|
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
|
-
|
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,
|
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
|
-
|
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,
|
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
|
-
|
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,
|
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
|
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
|
220
|
-
|
221
|
-
|
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
|
-
|
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
|
data/lib/huebot/program.rb
CHANGED
@@ -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, :
|
20
|
-
SerialControl = Struct.new(:loop, :
|
21
|
-
ParallelControl = Struct.new(:loop, :
|
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)
|
data/lib/huebot/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2023-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|