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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +71 -13
  6. data/Rakefile +12 -0
  7. data/bin/lifx-console +15 -0
  8. data/bin/lifx-snoop +50 -0
  9. data/examples/auto-off/Gemfile +3 -0
  10. data/examples/auto-off/auto-off.rb +35 -0
  11. data/examples/identify/Gemfile +3 -0
  12. data/examples/identify/identify.rb +70 -0
  13. data/examples/travis-build-light/Gemfile +4 -0
  14. data/examples/travis-build-light/build-light.rb +57 -0
  15. data/lib/bindata_ext/bool.rb +29 -0
  16. data/lib/bindata_ext/record.rb +11 -0
  17. data/lib/lifx/client.rb +136 -0
  18. data/lib/lifx/color.rb +190 -0
  19. data/lib/lifx/config.rb +12 -0
  20. data/lib/lifx/firmware.rb +55 -0
  21. data/lib/lifx/gateway_connection.rb +177 -0
  22. data/lib/lifx/light.rb +406 -0
  23. data/lib/lifx/light_collection.rb +105 -0
  24. data/lib/lifx/light_target.rb +189 -0
  25. data/lib/lifx/logging.rb +11 -0
  26. data/lib/lifx/message.rb +166 -0
  27. data/lib/lifx/network_context.rb +200 -0
  28. data/lib/lifx/observable.rb +46 -0
  29. data/lib/lifx/protocol/address.rb +21 -0
  30. data/lib/lifx/protocol/device.rb +225 -0
  31. data/lib/lifx/protocol/header.rb +24 -0
  32. data/lib/lifx/protocol/light.rb +110 -0
  33. data/lib/lifx/protocol/message.rb +17 -0
  34. data/lib/lifx/protocol/metadata.rb +21 -0
  35. data/lib/lifx/protocol/payload.rb +7 -0
  36. data/lib/lifx/protocol/sensor.rb +29 -0
  37. data/lib/lifx/protocol/type.rb +134 -0
  38. data/lib/lifx/protocol/wan.rb +50 -0
  39. data/lib/lifx/protocol/wifi.rb +76 -0
  40. data/lib/lifx/protocol_path.rb +84 -0
  41. data/lib/lifx/routing_manager.rb +110 -0
  42. data/lib/lifx/routing_table.rb +33 -0
  43. data/lib/lifx/seen.rb +15 -0
  44. data/lib/lifx/site.rb +89 -0
  45. data/lib/lifx/tag_manager.rb +105 -0
  46. data/lib/lifx/tag_table.rb +47 -0
  47. data/lib/lifx/target.rb +23 -0
  48. data/lib/lifx/timers.rb +18 -0
  49. data/lib/lifx/transport/tcp.rb +81 -0
  50. data/lib/lifx/transport/udp.rb +67 -0
  51. data/lib/lifx/transport.rb +41 -0
  52. data/lib/lifx/transport_manager/lan.rb +140 -0
  53. data/lib/lifx/transport_manager.rb +34 -0
  54. data/lib/lifx/utilities.rb +33 -0
  55. data/lib/lifx/version.rb +1 -1
  56. data/lib/lifx.rb +15 -1
  57. data/lifx.gemspec +11 -7
  58. data/spec/color_spec.rb +45 -0
  59. data/spec/gateway_connection_spec.rb +32 -0
  60. data/spec/integration/client_spec.rb +40 -0
  61. data/spec/integration/light_spec.rb +43 -0
  62. data/spec/integration/tags_spec.rb +31 -0
  63. data/spec/message_spec.rb +163 -0
  64. data/spec/protocol_path_spec.rb +109 -0
  65. data/spec/routing_manager_spec.rb +22 -0
  66. data/spec/spec_helper.rb +52 -0
  67. data/spec/transport/udp_spec.rb +38 -0
  68. data/spec/transport_spec.rb +14 -0
  69. 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
- # Your code goes here...
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 client for LIFX}
12
- spec.summary = %q{Ruby client for LIFX}
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 'bindata'
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
@@ -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
@@ -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