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 +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
|