huebot 0.5.0 → 1.0.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 +96 -27
- data/bin/huebot +35 -32
- data/lib/huebot/bot.rb +82 -39
- data/lib/huebot/cli/helpers.rb +170 -0
- data/lib/huebot/cli/runner.rb +78 -0
- data/lib/huebot/cli.rb +2 -139
- data/lib/huebot/client.rb +5 -4
- data/lib/huebot/compiler/api_v1.rb +285 -0
- data/lib/huebot/compiler.rb +11 -149
- data/lib/huebot/device_mapper.rb +38 -24
- data/lib/huebot/device_state.rb +5 -1
- data/lib/huebot/group.rb +10 -3
- data/lib/huebot/light.rb +10 -3
- data/lib/huebot/program.rb +71 -21
- data/lib/huebot/version.rb +1 -1
- data/lib/huebot.rb +3 -22
- metadata +34 -3
data/lib/huebot/device_mapper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Huebot
|
2
2
|
class DeviceMapper
|
3
|
-
Unmapped = Class.new(
|
3
|
+
Unmapped = Class.new(Error)
|
4
4
|
|
5
5
|
def initialize(bridge, inputs = [])
|
6
6
|
all_lights, all_groups = bridge.lights, bridge.groups
|
@@ -9,43 +9,57 @@ module Huebot
|
|
9
9
|
@lights_by_name = all_lights.reduce({}) { |a, l| a[l.name] = l; a }
|
10
10
|
@groups_by_id = all_groups.reduce({}) { |a, g| a[g.id] = g; a }
|
11
11
|
@groups_by_name = all_groups.reduce({}) { |a, g| a[g.name] = g; a }
|
12
|
-
@devices_by_var = inputs.each_with_index.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
a
|
12
|
+
@devices_by_var = inputs.each_with_index.each_with_object({}) { |(x, idx), obj|
|
13
|
+
obj[idx + 1] =
|
14
|
+
case x
|
15
|
+
when Light::Input then @lights_by_id[x.val.to_i] || @lights_by_name[x.val]
|
16
|
+
when Group::Input then @groups_by_id[x.val.to_i] || @groups_by_name[x.val]
|
17
|
+
else raise Error, "Invalid input: #{x}"
|
18
|
+
end || raise(Unmapped, "Could not find #{x.class.name[8..-6].downcase} with id or name '#{x.val}'")
|
20
19
|
}
|
21
20
|
@all = @devices_by_var.values
|
22
21
|
end
|
23
22
|
|
23
|
+
def each
|
24
|
+
if block_given?
|
25
|
+
@all.each { |device| yield device }
|
26
|
+
else
|
27
|
+
@all.each
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
24
31
|
def light!(id)
|
25
|
-
|
26
|
-
when Integer
|
27
|
-
@lights_by_id[id]
|
28
|
-
when String
|
29
|
-
@lights_by_name[id]
|
30
|
-
end || (raise Unmapped, "Unmapped light '#{id}'")
|
32
|
+
@lights_by_id[id] || @lights_by_name[id] || (raise Unmapped, "Unmapped light '#{id}'")
|
31
33
|
end
|
32
34
|
|
33
35
|
def group!(id)
|
34
|
-
|
35
|
-
when Integer
|
36
|
-
@groups_by_id[id]
|
37
|
-
when String
|
38
|
-
@groups_by_name[id]
|
39
|
-
end || (raise Unmapped, "Unmapped group '#{id}'")
|
36
|
+
@groups_by_id[id] || @groups_by_name[id] || (raise Unmapped, "Unmapped group '#{id}'")
|
40
37
|
end
|
41
38
|
|
42
39
|
def var!(id)
|
43
40
|
case id
|
44
|
-
when
|
41
|
+
when :all
|
45
42
|
@all
|
46
43
|
else
|
47
|
-
@devices_by_var[id]
|
48
|
-
end
|
44
|
+
@devices_by_var[id] || (raise Unmapped, "Unmapped device '#{id}'")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def missing_lights(names)
|
49
|
+
names - @lights_by_name.keys
|
50
|
+
end
|
51
|
+
|
52
|
+
def missing_groups(names)
|
53
|
+
names - @groups_by_name.keys
|
54
|
+
end
|
55
|
+
|
56
|
+
def missing_vars(vars)
|
57
|
+
missing = vars - @devices_by_var.keys
|
58
|
+
if @all.any?
|
59
|
+
missing - [:all]
|
60
|
+
else
|
61
|
+
missing
|
62
|
+
end
|
49
63
|
end
|
50
64
|
end
|
51
65
|
end
|
data/lib/huebot/device_state.rb
CHANGED
data/lib/huebot/group.rb
CHANGED
@@ -1,22 +1,29 @@
|
|
1
1
|
module Huebot
|
2
2
|
class Group
|
3
|
+
#
|
4
|
+
# Struct for specifying a Group input (id or name)
|
5
|
+
#
|
6
|
+
# @attr val [Integer|String] id or name
|
7
|
+
#
|
8
|
+
Input = Struct.new(:val)
|
9
|
+
|
3
10
|
include DeviceState
|
4
11
|
attr_reader :client, :id, :name
|
5
12
|
|
6
13
|
def initialize(client, id, attrs)
|
7
14
|
@client = client
|
8
|
-
@id = id
|
15
|
+
@id = id.to_i
|
9
16
|
@name = attrs.fetch("name")
|
10
17
|
@attrs = attrs
|
11
18
|
end
|
12
19
|
|
13
20
|
private
|
14
21
|
|
15
|
-
def
|
22
|
+
def state_change_url
|
16
23
|
url "/action"
|
17
24
|
end
|
18
25
|
|
19
|
-
def url(path)
|
26
|
+
def url(path = "")
|
20
27
|
"/groups/#{id}#{path}"
|
21
28
|
end
|
22
29
|
end
|
data/lib/huebot/light.rb
CHANGED
@@ -1,22 +1,29 @@
|
|
1
1
|
module Huebot
|
2
2
|
class Light
|
3
|
+
#
|
4
|
+
# Struct for specifying a Light input (id or name)
|
5
|
+
#
|
6
|
+
# @attr val [Integer|String] id or name
|
7
|
+
#
|
8
|
+
Input = Struct.new(:val)
|
9
|
+
|
3
10
|
include DeviceState
|
4
11
|
attr_reader :client, :id, :name
|
5
12
|
|
6
13
|
def initialize(client, id, attrs)
|
7
14
|
@client = client
|
8
|
-
@id = id
|
15
|
+
@id = id.to_i
|
9
16
|
@name = attrs.fetch("name")
|
10
17
|
@attrs = attrs
|
11
18
|
end
|
12
19
|
|
13
20
|
private
|
14
21
|
|
15
|
-
def
|
22
|
+
def state_change_url
|
16
23
|
url "/state"
|
17
24
|
end
|
18
25
|
|
19
|
-
def url(path)
|
26
|
+
def url(path = "")
|
20
27
|
"/lights/#{id}#{path}"
|
21
28
|
end
|
22
29
|
end
|
data/lib/huebot/program.rb
CHANGED
@@ -1,32 +1,82 @@
|
|
1
1
|
module Huebot
|
2
2
|
class Program
|
3
|
-
|
4
|
-
|
3
|
+
#
|
4
|
+
# Struct for storing a program's Intermediate Representation and source filepath.
|
5
|
+
#
|
6
|
+
# @attr tokens [Hash]
|
7
|
+
# @attr filepath [String]
|
8
|
+
# @attr api_version [Float] API version
|
9
|
+
#
|
10
|
+
Src = Struct.new(:tokens, :filepath, :api_version) do
|
11
|
+
def default_name
|
12
|
+
File.basename(filepath, ".*")
|
13
|
+
end
|
14
|
+
end
|
5
15
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@errors = []
|
23
|
-
@warnings = []
|
16
|
+
module AST
|
17
|
+
Node = Struct.new(:instruction, :children, :errors, :warnings)
|
18
|
+
|
19
|
+
Transition = Struct.new(:state, :devices, :sleep)
|
20
|
+
SerialControl = Struct.new(:loop, :sleep)
|
21
|
+
ParallelControl = Struct.new(:loop, :sleep)
|
22
|
+
|
23
|
+
InfiniteLoop = Struct.new(:pause)
|
24
|
+
CountedLoop = Struct.new(:n, :pause)
|
25
|
+
TimerLoop = Struct.new(:hours, :minutes, :pause)
|
26
|
+
DeadlineLoop = Struct.new(:stop_time, :pause)
|
27
|
+
|
28
|
+
DeviceRef = Struct.new(:ref)
|
29
|
+
Light = Struct.new(:name)
|
30
|
+
Group = Struct.new(:name)
|
31
|
+
NoOp = Struct.new(:x)
|
24
32
|
end
|
25
33
|
|
34
|
+
attr_accessor :name
|
35
|
+
attr_accessor :api_version
|
36
|
+
attr_accessor :data
|
37
|
+
|
26
38
|
def valid?
|
27
39
|
errors.empty?
|
28
40
|
end
|
29
41
|
|
30
|
-
|
42
|
+
# Returns all light names hard-coded into the program
|
43
|
+
def light_names(node = data)
|
44
|
+
devices(AST::Light).uniq.map(&:name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns all group names hard-coded into the program
|
48
|
+
def group_names(node = data)
|
49
|
+
devices(AST::Group).uniq.map(&:name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns all device refs (e.g. $all, $1, $2) in the program
|
53
|
+
def device_refs(node = data)
|
54
|
+
devices(AST::DeviceRef).uniq.map(&:ref)
|
55
|
+
end
|
56
|
+
|
57
|
+
def errors(node = data)
|
58
|
+
node.children.reduce(node.errors) { |errors, child|
|
59
|
+
errors + child.errors
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def warnings(node = data)
|
64
|
+
node.children.reduce(node.warnings) { |warnings, child|
|
65
|
+
warnings + child.warnings
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def devices(type, node = data)
|
72
|
+
case node.instruction
|
73
|
+
when AST::Transition
|
74
|
+
node.instruction.devices.select { |d| d.is_a? type }
|
75
|
+
when AST::SerialControl, AST::ParallelControl
|
76
|
+
node.children.map { |n| devices type, n }.flatten
|
77
|
+
else
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
end
|
31
81
|
end
|
32
82
|
end
|
data/lib/huebot/version.rb
CHANGED
data/lib/huebot.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
module Huebot
|
2
|
+
Error = Class.new(StandardError)
|
3
|
+
|
2
4
|
autoload :Config, 'huebot/config'
|
3
5
|
autoload :Client, 'huebot/client'
|
6
|
+
autoload :CLI, 'huebot/cli'
|
4
7
|
autoload :Bridge, 'huebot/bridge'
|
5
8
|
autoload :DeviceState, 'huebot/device_state'
|
6
9
|
autoload :Light, 'huebot/light'
|
@@ -10,26 +13,4 @@ module Huebot
|
|
10
13
|
autoload :Compiler, 'huebot/compiler'
|
11
14
|
autoload :Bot, 'huebot/bot'
|
12
15
|
autoload :VERSION, 'huebot/version'
|
13
|
-
|
14
|
-
#
|
15
|
-
# Struct for storing a program's Intermediate Representation and source filepath.
|
16
|
-
#
|
17
|
-
# @attr ir [Hash]
|
18
|
-
# @attr filepath [String]
|
19
|
-
#
|
20
|
-
ProgramSrc = Struct.new(:ir, :filepath)
|
21
|
-
|
22
|
-
#
|
23
|
-
# Struct for specifying a Light input (id or name)
|
24
|
-
#
|
25
|
-
# @attr val [Integer|String] id or name
|
26
|
-
#
|
27
|
-
LightInput = Struct.new(:val)
|
28
|
-
|
29
|
-
#
|
30
|
-
# Struct for specifying a Group input (id or name)
|
31
|
-
#
|
32
|
-
# @attr val [Integer|String] id or name
|
33
|
-
#
|
34
|
-
GroupInput = Struct.new(:val)
|
35
16
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: huebot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.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-
|
12
|
-
dependencies:
|
11
|
+
date: 2023-12-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
13
41
|
description: Declare and run YAML programs for Philips Hue devices
|
14
42
|
email: jordan.hollinger@gmail.com
|
15
43
|
executables:
|
@@ -23,8 +51,11 @@ files:
|
|
23
51
|
- lib/huebot/bot.rb
|
24
52
|
- lib/huebot/bridge.rb
|
25
53
|
- lib/huebot/cli.rb
|
54
|
+
- lib/huebot/cli/helpers.rb
|
55
|
+
- lib/huebot/cli/runner.rb
|
26
56
|
- lib/huebot/client.rb
|
27
57
|
- lib/huebot/compiler.rb
|
58
|
+
- lib/huebot/compiler/api_v1.rb
|
28
59
|
- lib/huebot/config.rb
|
29
60
|
- lib/huebot/device_mapper.rb
|
30
61
|
- lib/huebot/device_state.rb
|