lifx 0.0.1 → 0.4.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/.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
|