lifx 0.0.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +71 -13
- data/Rakefile +12 -0
- data/bin/lifx-console +15 -0
- data/bin/lifx-snoop +50 -0
- data/examples/auto-off/Gemfile +3 -0
- data/examples/auto-off/auto-off.rb +35 -0
- data/examples/identify/Gemfile +3 -0
- data/examples/identify/identify.rb +70 -0
- data/examples/travis-build-light/Gemfile +4 -0
- data/examples/travis-build-light/build-light.rb +57 -0
- data/lib/bindata_ext/bool.rb +29 -0
- data/lib/bindata_ext/record.rb +11 -0
- data/lib/lifx/client.rb +136 -0
- data/lib/lifx/color.rb +190 -0
- data/lib/lifx/config.rb +12 -0
- data/lib/lifx/firmware.rb +55 -0
- data/lib/lifx/gateway_connection.rb +177 -0
- data/lib/lifx/light.rb +406 -0
- data/lib/lifx/light_collection.rb +105 -0
- data/lib/lifx/light_target.rb +189 -0
- data/lib/lifx/logging.rb +11 -0
- data/lib/lifx/message.rb +166 -0
- data/lib/lifx/network_context.rb +200 -0
- data/lib/lifx/observable.rb +46 -0
- data/lib/lifx/protocol/address.rb +21 -0
- data/lib/lifx/protocol/device.rb +225 -0
- data/lib/lifx/protocol/header.rb +24 -0
- data/lib/lifx/protocol/light.rb +110 -0
- data/lib/lifx/protocol/message.rb +17 -0
- data/lib/lifx/protocol/metadata.rb +21 -0
- data/lib/lifx/protocol/payload.rb +7 -0
- data/lib/lifx/protocol/sensor.rb +29 -0
- data/lib/lifx/protocol/type.rb +134 -0
- data/lib/lifx/protocol/wan.rb +50 -0
- data/lib/lifx/protocol/wifi.rb +76 -0
- data/lib/lifx/protocol_path.rb +84 -0
- data/lib/lifx/routing_manager.rb +110 -0
- data/lib/lifx/routing_table.rb +33 -0
- data/lib/lifx/seen.rb +15 -0
- data/lib/lifx/site.rb +89 -0
- data/lib/lifx/tag_manager.rb +105 -0
- data/lib/lifx/tag_table.rb +47 -0
- data/lib/lifx/target.rb +23 -0
- data/lib/lifx/timers.rb +18 -0
- data/lib/lifx/transport/tcp.rb +81 -0
- data/lib/lifx/transport/udp.rb +67 -0
- data/lib/lifx/transport.rb +41 -0
- data/lib/lifx/transport_manager/lan.rb +140 -0
- data/lib/lifx/transport_manager.rb +34 -0
- data/lib/lifx/utilities.rb +33 -0
- data/lib/lifx/version.rb +1 -1
- data/lib/lifx.rb +15 -1
- data/lifx.gemspec +11 -7
- data/spec/color_spec.rb +45 -0
- data/spec/gateway_connection_spec.rb +32 -0
- data/spec/integration/client_spec.rb +40 -0
- data/spec/integration/light_spec.rb +43 -0
- data/spec/integration/tags_spec.rb +31 -0
- data/spec/message_spec.rb +163 -0
- data/spec/protocol_path_spec.rb +109 -0
- data/spec/routing_manager_spec.rb +22 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/transport/udp_spec.rb +38 -0
- data/spec/transport_spec.rb +14 -0
- metadata +143 -26
data/lib/lifx.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
require "lifx/version"
|
2
|
+
require "bindata"
|
3
|
+
require "bindata_ext/bool"
|
4
|
+
require "bindata_ext/record"
|
5
|
+
require "lifx/utilities"
|
6
|
+
require "lifx/logging"
|
7
|
+
|
8
|
+
require "lifx/protocol/payload"
|
9
|
+
%w(device light sensor wan wifi message).each { |f| require "lifx/protocol/#{f}" }
|
10
|
+
require "lifx/protocol/type"
|
11
|
+
require "lifx/message"
|
12
|
+
require "lifx/transport"
|
13
|
+
|
14
|
+
require "lifx/config"
|
15
|
+
require "lifx/client"
|
2
16
|
|
3
17
|
module LIFX
|
4
|
-
|
18
|
+
|
5
19
|
end
|
data/lifx.gemspec
CHANGED
@@ -8,18 +8,22 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = LIFX::VERSION
|
9
9
|
spec.authors = ["Jack Chen (chendo)"]
|
10
10
|
spec.email = ["chendo@lifx.co"]
|
11
|
-
spec.description = %q{Ruby
|
12
|
-
spec.summary = %q{Ruby
|
13
|
-
spec.homepage = ""
|
11
|
+
spec.description = %q{A Ruby gem that allows easy interaction with LIFX devices.}
|
12
|
+
spec.summary = %q{A Ruby gem that allows easy interaction with LIFX devices. Handles discovery, rate limiting, tags, gateway connections and provides an object-based API for interacting with LIFX devices. }
|
13
|
+
spec.homepage = "https://github.com/LIFX/lifx-gem"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.files = `git ls-files`.split($/).reject { |f| f =~ /^script\// }
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
+
spec.required_ruby_version = ">= 2.1"
|
20
21
|
|
21
|
-
spec.add_dependency
|
22
|
+
spec.add_dependency "bindata", "~> 2.0"
|
23
|
+
spec.add_dependency "yell", "~> 2.0"
|
24
|
+
spec.add_dependency "timers", "~> 2.0"
|
25
|
+
spec.add_dependency "configatron", "~> 3.0"
|
22
26
|
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
-
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "rake", '~> 10.1'
|
28
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
25
29
|
end
|
data/spec/color_spec.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe Color do
|
5
|
+
let(:default_kelvin) { 3500 }
|
6
|
+
describe '.rgb' do
|
7
|
+
context 'translating from RGB' do
|
8
|
+
it 'translates red correctly' do
|
9
|
+
c = Color.rgb(255, 0, 0)
|
10
|
+
c.to_a.should == [0, 1, 1, default_kelvin]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'translates yellow correctly' do
|
14
|
+
c = Color.rgb(255, 255, 0)
|
15
|
+
c.to_a.should == [60, 1, 1, default_kelvin]
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'translates green correctly' do
|
19
|
+
c = Color.rgb(0, 255, 0)
|
20
|
+
c.to_a.should == [120, 1, 1, default_kelvin]
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'translates cyan correctly' do
|
24
|
+
c = Color.rgb(0, 255, 255)
|
25
|
+
c.to_a.should == [180, 1, 1, default_kelvin]
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'translates blue correctly' do
|
29
|
+
c = Color.rgb(0, 0, 255)
|
30
|
+
c.to_a.should == [240, 1, 1, default_kelvin]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'translates white correctly' do
|
34
|
+
c = Color.rgb(255, 255, 255)
|
35
|
+
c.to_a.should == [0, 0, 1, default_kelvin]
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'translates black correctly' do
|
39
|
+
c = Color.rgb(0, 0, 0)
|
40
|
+
c.to_a.should == [0, 0, 0, default_kelvin]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe GatewayConnection do
|
5
|
+
subject do
|
6
|
+
GatewayConnection.new
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:message) { double(Message).tap { |m| m.stub(:is_a?).and_return(true); m.stub(:pack) } }
|
10
|
+
let(:ip) { '127.0.0.1' }
|
11
|
+
let(:port) { 35003 }
|
12
|
+
|
13
|
+
after do
|
14
|
+
subject.close
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'write queue resiliency' do
|
18
|
+
it 'does not send if there is no available connection' do
|
19
|
+
expect(subject).to_not receive(:actually_write)
|
20
|
+
subject.write(message)
|
21
|
+
expect { subject.flush(timeout: 0.5) }.to raise_error(Timeout::Error)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'pushes message back into queue if unable to write' do
|
25
|
+
subject.connect_udp(ip, port)
|
26
|
+
expect(subject).to receive(:actually_write).and_return(false, true)
|
27
|
+
subject.write(message)
|
28
|
+
subject.flush
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe Client, integration: true do
|
5
|
+
describe '#sync' do
|
6
|
+
it 'schedules sending all messages to be executed at the same time' do
|
7
|
+
|
8
|
+
lifx.discover! do
|
9
|
+
lights.count >= 3
|
10
|
+
end
|
11
|
+
|
12
|
+
if lights.count < 3
|
13
|
+
fail "This test requires 3 or more lights tagged under Test"
|
14
|
+
end
|
15
|
+
|
16
|
+
white = LIFX::Color.white(brightness: 0.5)
|
17
|
+
lights.set_color(white, duration: 0)
|
18
|
+
sleep 1
|
19
|
+
|
20
|
+
udp = Transport::UDP.new('0.0.0.0', 56750)
|
21
|
+
msgs = []
|
22
|
+
udp.add_observer(self) do |message:, ip:, transport:|
|
23
|
+
msgs << message if message.payload.is_a?(Protocol::Light::SetWaveform)
|
24
|
+
end
|
25
|
+
udp.listen
|
26
|
+
|
27
|
+
delay = lifx.sync do
|
28
|
+
lights.each do |light|
|
29
|
+
light.pulse(LIFX::Color.hsb(rand(360), 1, 1), period: 1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
msgs.count.should == lights.count
|
34
|
+
msgs.map(&:at_time).uniq.count.should == 1
|
35
|
+
|
36
|
+
flush
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe Light, integration: true do
|
5
|
+
describe '#set_power' do
|
6
|
+
it "sets the power of the light asynchronously" do
|
7
|
+
light.set_power(0)
|
8
|
+
wait { light.off?.should == true }
|
9
|
+
light.set_power(1)
|
10
|
+
wait { light.on?.should == true }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#set_power!' do
|
15
|
+
it "sets the power of the light synchronously" do
|
16
|
+
light.set_power!(0)
|
17
|
+
light.off?.should == true
|
18
|
+
light.set_power!(1)
|
19
|
+
light.on?.should == true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#set_color' do
|
24
|
+
it "sets the color of the light asynchronously" do
|
25
|
+
color = Color.hsb(rand(360), rand, rand)
|
26
|
+
light.set_color(color, duration: 0)
|
27
|
+
sleep 1
|
28
|
+
light.refresh
|
29
|
+
wait { light.color.should == color }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#set_label' do
|
34
|
+
it "sets the label of the light synchronously" do
|
35
|
+
label = light.label.sub(/\d+|$/, rand(100).to_s)
|
36
|
+
light.set_label(label)
|
37
|
+
light.label.should == label
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe "tags", integration: true do
|
5
|
+
it 'Clearing, setting and using tags' do
|
6
|
+
light.add_tag('Foo')
|
7
|
+
light.tags.should include('Foo')
|
8
|
+
|
9
|
+
test_tag = lights.with_tag('Foo')
|
10
|
+
test_tag.turn_on
|
11
|
+
color = Color.hsb(rand(360), 0.3, 0.3)
|
12
|
+
test_tag.set_color(color, duration: 0)
|
13
|
+
flush
|
14
|
+
sleep 1 # Set messages are scheduled 250ms if no at_time is set
|
15
|
+
# It also returns the current light state rather than the final state
|
16
|
+
light.refresh
|
17
|
+
wait { light.color.should == color }
|
18
|
+
|
19
|
+
light.remove_tag('Foo')
|
20
|
+
wait { light.tags.should_not include('Foo') }
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'deletes tags when no longer assigned to a light' do
|
24
|
+
light.add_tag('TempTag')
|
25
|
+
light.remove_tag('TempTag')
|
26
|
+
lifx.unused_tags.should include('TempTag')
|
27
|
+
lifx.purge_unused_tags!
|
28
|
+
lifx.unused_tags.should be_empty
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LIFX::Message do
|
4
|
+
context 'unpacking' do
|
5
|
+
let(:data) { "\x39\x00\x00\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x31\x6c\x69\x66\x78\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x67\x00\x00\x00\x00\x01\x00\x00\xff\xff\xff\xff\xac\x0d\xc8\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x00".b }
|
6
|
+
let(:msg) { LIFX::Message.unpack(data) }
|
7
|
+
|
8
|
+
it 'unpacks without errors' do
|
9
|
+
msg.should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns the correct frame data' do
|
13
|
+
msg.msg_size.should == 57
|
14
|
+
msg.protocol.should == 1024
|
15
|
+
msg.addressable?.should == true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns the correct address data' do
|
19
|
+
msg.raw_site.should == '1lifx1'
|
20
|
+
msg.raw_target.should == "\x00" * 8
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has correct ProtocolPath data' do
|
24
|
+
msg.path.should be_instance_of(LIFX::ProtocolPath)
|
25
|
+
msg.path.site_id.should == '316c69667831'
|
26
|
+
msg.path.tag_ids.should == []
|
27
|
+
msg.path.device_id.should == nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns the correct metadata' do
|
31
|
+
msg.at_time.should == 0
|
32
|
+
msg.type.should == 103
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:payload) { msg.payload }
|
36
|
+
it 'returns the payload' do
|
37
|
+
payload.class.should == LIFX::Protocol::Light::SetWaveform
|
38
|
+
payload.stream.should == 0
|
39
|
+
payload.transient.should be_true
|
40
|
+
payload.color.hue.should == 0
|
41
|
+
payload.color.saturation.should == 65535
|
42
|
+
payload.color.brightness.should == 65535
|
43
|
+
payload.color.kelvin.should == 3500
|
44
|
+
payload.period.should == 200
|
45
|
+
payload.cycles.should == 1.0
|
46
|
+
payload.duty_cycle.should == 0
|
47
|
+
payload.waveform.should == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'repacks to the same data' do
|
51
|
+
msg.pack.should == data
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'packing' do
|
56
|
+
context 'no attributes' do
|
57
|
+
let(:msg) { LIFX::Message.new }
|
58
|
+
|
59
|
+
it 'throws an exception' do
|
60
|
+
expect { msg.pack }.to raise_error(LIFX::Message::NoPayload)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'no path' do
|
65
|
+
let(:msg) { LIFX::Message.new(payload: LIFX::Protocol::Device::SetPower.new) }
|
66
|
+
|
67
|
+
it 'throws an exception' do
|
68
|
+
expect { msg.pack }.to raise_error(LIFX::Message::NoPath)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'passed in via hash' do
|
73
|
+
let(:msg) do
|
74
|
+
LIFX::Message.new({
|
75
|
+
path: LIFX::ProtocolPath.new(tagged: false, raw_target: "abcdefgh"),
|
76
|
+
at_time: 9001,
|
77
|
+
payload: LIFX::Protocol::Wifi::SetAccessPoint.new(
|
78
|
+
interface: 1,
|
79
|
+
ssid: "who let the dogs out",
|
80
|
+
pass: "woof, woof, woof woof!",
|
81
|
+
security: 1
|
82
|
+
)
|
83
|
+
})
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'sets the size' do
|
87
|
+
msg.msg_size.should == 134
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'packs correctly' do
|
91
|
+
msg.pack.should == "\x86\x00\x00\x14\x00\x00\x00\x00abcdefgh\x00\x00\x00\x00\x00\x00\x00\x00)#\x00\x00\x00\x00\x00\x001\x01\x00\x00\x01who let the dogs out\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00woof, woof, woof woof!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".b
|
92
|
+
unpacked = LIFX::Message.unpack(msg.pack)
|
93
|
+
msg.protocol.should == 1024
|
94
|
+
msg.path.tagged?.should == false
|
95
|
+
msg.addressable?.should == true
|
96
|
+
msg.path.raw_target.should == 'abcdefgh'
|
97
|
+
msg.at_time.should == 9001
|
98
|
+
msg.type.should == 305
|
99
|
+
msg.payload.class.should == LIFX::Protocol::Wifi::SetAccessPoint
|
100
|
+
msg.payload.interface.should == 1
|
101
|
+
msg.payload.ssid.should == "who let the dogs out"
|
102
|
+
msg.payload.pass.should == "woof, woof, woof woof!"
|
103
|
+
msg.payload.security.should == 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'packing with tags' do
|
108
|
+
let(:msg) do
|
109
|
+
LIFX::Message.new({
|
110
|
+
path: LIFX::ProtocolPath.new(tag_ids: [0,1]),
|
111
|
+
at_time: 9001,
|
112
|
+
payload: LIFX::Protocol::Device::GetTime.new
|
113
|
+
})
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:unpacked) { LIFX::Message.unpack(msg.pack) }
|
117
|
+
|
118
|
+
it 'packs the tag correctly' do
|
119
|
+
msg.pack.should == "$\x00\x004\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)#\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00".b
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'sets tagged' do
|
123
|
+
unpacked.path.tagged?.should == true
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'sets tags' do
|
127
|
+
unpacked.path.tag_ids.should == [0, 1]
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'device should be nil' do
|
131
|
+
unpacked.path.device_id.should == nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'packing with device' do
|
136
|
+
let(:msg) do
|
137
|
+
LIFX::Message.new({
|
138
|
+
path: LIFX::ProtocolPath.new(device_id: '0123456789ab', site_id: '0' * 12),
|
139
|
+
at_time: 9001,
|
140
|
+
payload: LIFX::Protocol::Device::GetTime.new
|
141
|
+
})
|
142
|
+
end
|
143
|
+
|
144
|
+
let(:unpacked) { LIFX::Message.unpack(msg.pack) }
|
145
|
+
|
146
|
+
it 'packs the tag correctly' do
|
147
|
+
msg.pack.should == "$\x00\x00\x14\x00\x00\x00\x00\x01#Eg\x89\xAB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00)#\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00".b
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'sets tagged to false' do
|
151
|
+
unpacked.path.tagged?.should == false
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'sets device' do
|
155
|
+
unpacked.path.device_id.should == '0123456789ab'
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'tags should be nil' do
|
159
|
+
unpacked.path.tag_ids.should == nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe ProtocolPath do
|
5
|
+
describe 'initializing from raw data' do
|
6
|
+
context 'device target' do
|
7
|
+
subject do
|
8
|
+
ProtocolPath.new(raw_site: "1lifx1", raw_target: "\xAB\xCD\xEF\x12\x34\x56\x00\x00", tagged: false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns site_id in hex' do
|
12
|
+
subject.site_id.should == "316c69667831"
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns device_id in hex' do
|
16
|
+
subject.device_id.should == 'abcdef123456'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns tagged as false' do
|
20
|
+
subject.tagged?.should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns nil for tag_ids' do
|
24
|
+
subject.tag_ids.should == nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'tagged target' do
|
29
|
+
subject do
|
30
|
+
ProtocolPath.new(raw_site: "1lifx1", raw_target: "\x03\x00\x00\x00\x00\x00\x00\x00", tagged: true)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns site_id in hex' do
|
34
|
+
subject.site_id.should == "316c69667831"
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'returns device_id as nil' do
|
38
|
+
subject.device_id.should == nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns tagged as true' do
|
42
|
+
subject.tagged?.should be_true
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns the tag_ids' do
|
46
|
+
subject.tag_ids.should == [0, 1]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'initializing from strings' do
|
52
|
+
context 'device target' do
|
53
|
+
subject do
|
54
|
+
ProtocolPath.new(site_id: "316c69667831", device_id: 'abcdef123456')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'sets raw_site correctly' do
|
58
|
+
subject.raw_site.should == "1lifx1"
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'sets raw_target correctly' do
|
62
|
+
subject.raw_target.should == "\xAB\xCD\xEF\x12\x34\x56\x00\x00".b
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'sets tagged to false' do
|
66
|
+
subject.tagged?.should be_false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'tagged target' do
|
71
|
+
subject do
|
72
|
+
ProtocolPath.new(site_id: "316c69667831", tag_ids: [0, 1])
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'sets raw_site properly' do
|
76
|
+
subject.raw_site.should == "1lifx1"
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'sets raw_target correctly' do
|
80
|
+
subject.raw_target.should == "\x03\x00\x00\x00\x00\x00\x00\x00".b
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns tagged as true' do
|
84
|
+
subject.tagged?.should be_true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'tagged target with no site' do
|
89
|
+
subject do
|
90
|
+
ProtocolPath.new(tagged: true)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'raw_site should be null string' do
|
94
|
+
subject.raw_site.should == "\x00\x00\x00\x00\x00\x00".b
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets raw_target correctly' do
|
98
|
+
subject.raw_target.should == "\x00\x00\x00\x00\x00\x00\x00\x00".b
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns tagged as true' do
|
102
|
+
subject.tagged?.should be_true
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe RoutingManager do
|
5
|
+
describe '#tags_for_device_id' do
|
6
|
+
subject do
|
7
|
+
RoutingManager.new(context: double)
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
subject.tag_table.update_table(site_id: 'site', tag_id: 0, label: 'Some label')
|
12
|
+
subject.tag_table.update_table(site_id: 'site', tag_id: 1, label: 'Another label')
|
13
|
+
subject.tag_table.update_table(site_id: 'site', tag_id: 2, label: 'Much label')
|
14
|
+
subject.routing_table.update_table(site_id: 'site', device_id: 'device', tag_ids: [0,2])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'resolves tags' do
|
18
|
+
subject.tags_for_device_id('device').should == ['Some label', 'Much label']
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
require 'lifx'
|
6
|
+
require 'lifx/utilities'
|
7
|
+
|
8
|
+
shared_context 'integration', integration: true do
|
9
|
+
def lifx
|
10
|
+
$lifx ||= begin
|
11
|
+
c = LIFX::Client.lan
|
12
|
+
begin
|
13
|
+
c.discover! do
|
14
|
+
c.tags.include?('Test') && c.lights.with_tag('Test').count > 0
|
15
|
+
end
|
16
|
+
rescue Timeout::Error
|
17
|
+
raise "Could not find any lights with tag Test in #{c.lights.inspect}"
|
18
|
+
end
|
19
|
+
c
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def flush
|
24
|
+
lifx.flush
|
25
|
+
end
|
26
|
+
|
27
|
+
def wait(timeout: 5, retry_wait: 0.1, &block)
|
28
|
+
Timeout.timeout(timeout) do
|
29
|
+
begin
|
30
|
+
block.call
|
31
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
32
|
+
sleep(retry_wait)
|
33
|
+
retry
|
34
|
+
end
|
35
|
+
end
|
36
|
+
rescue Timeout::Error
|
37
|
+
block.call
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:lights) { lifx.lights.with_tag('Test') }
|
41
|
+
let(:light) { lights.first }
|
42
|
+
end
|
43
|
+
|
44
|
+
if ENV['DEBUG']
|
45
|
+
LIFX::Config.logger = Yell.new(STDERR)
|
46
|
+
end
|
47
|
+
|
48
|
+
RSpec.configure do |config|
|
49
|
+
config.formatter = 'documentation'
|
50
|
+
config.color = true
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LIFX::Transport::UDP do
|
4
|
+
let(:port) { 45828 }
|
5
|
+
subject do
|
6
|
+
LIFX::Transport::UDP.new('localhost', port)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#write' do
|
10
|
+
let(:message) { double }
|
11
|
+
let(:payload) { double }
|
12
|
+
it 'writes a Message to specified host' do
|
13
|
+
message.should_receive(:pack).and_return(payload)
|
14
|
+
UDPSocket.any_instance.should_receive(:send).with(payload, 0, 'localhost', port)
|
15
|
+
subject.write(message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#listen' do
|
20
|
+
let(:raw_message) { 'some binary data' }
|
21
|
+
let(:message) { double }
|
22
|
+
let(:socket) { UDPSocket.new }
|
23
|
+
|
24
|
+
it 'listens to the specified socket data, unpacks it and notifies observers' do
|
25
|
+
messages = []
|
26
|
+
subject.add_observer(self) do |message:, ip:, transport:|
|
27
|
+
messages << message
|
28
|
+
end
|
29
|
+
subject.listen
|
30
|
+
|
31
|
+
LIFX::Message.should_receive(:unpack).with(raw_message).and_return(message)
|
32
|
+
socket.send(raw_message, 0, 'localhost', port)
|
33
|
+
sleep 0.01
|
34
|
+
messages.should include(message)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe LIFX::Transport do
|
4
|
+
# Transport handles communicating to the bulbs
|
5
|
+
# UDP, TCP, Cloud
|
6
|
+
|
7
|
+
describe 'initialize' do
|
8
|
+
it 'takes an host and port' do
|
9
|
+
transport = LIFX::Transport.new('127.0.0.1', 31337)
|
10
|
+
transport.host.should == '127.0.0.1'
|
11
|
+
transport.port.should == 31337
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|