lifx 0.4.7 → 0.4.8
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/CHANGES.md +5 -0
- data/bin/lifx-snoop +2 -2
- data/lib/lifx.rb +2 -0
- data/lib/lifx/color.rb +5 -5
- data/lib/lifx/gateway_connection.rb +12 -8
- data/lib/lifx/light.rb +4 -4
- data/lib/lifx/network_context.rb +14 -6
- data/lib/lifx/observable.rb +31 -15
- data/lib/lifx/routing_manager.rb +9 -2
- data/lib/lifx/routing_table.rb +8 -2
- data/lib/lifx/site.rb +12 -6
- data/lib/lifx/tag_manager.rb +4 -2
- data/lib/lifx/thread.rb +11 -0
- data/lib/lifx/timers.rb +9 -1
- data/lib/lifx/transport.rb +6 -3
- data/lib/lifx/transport/tcp.rb +13 -7
- data/lib/lifx/transport/udp.rb +7 -5
- data/lib/lifx/transport_manager.rb +7 -2
- data/lib/lifx/transport_manager/lan.rb +17 -11
- data/lib/lifx/version.rb +1 -1
- data/spec/integration/client_spec.rb +1 -1
- data/spec/routing_manager_spec.rb +1 -1
- data/spec/routing_table_spec.rb +21 -0
- data/spec/transport/udp_spec.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4d3a3b49f4182b136607335b3439a6d8c475af4
|
4
|
+
data.tar.gz: 5119e5d44ca2c1649a9f4226678dc1c3c04900c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76282a8c2b04cf4f0c0b199532d2847b97eacf7f8313c246c9aedafbf1cf6d35b02d9c75e75d472bb4f41a278988b92aae63cace417e5902a9e84095161f62f2
|
7
|
+
data.tar.gz: 0b81b9d0165ce833a8436bb597a5f6398ebf4cebee892f02116a6af9a7450b851c782639c9c077b8672bd3b836cf1f6be03e7dfb219ca02f3255165b12311355
|
data/CHANGES.md
CHANGED
data/bin/lifx-snoop
CHANGED
@@ -27,7 +27,7 @@ end
|
|
27
27
|
|
28
28
|
begin
|
29
29
|
light_udp = LIFX::Transport::UDP.new('0.0.0.0', 56700)
|
30
|
-
light_udp.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
30
|
+
light_udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
31
31
|
if matchers.all? { |m| message.to_s =~ m }
|
32
32
|
puts "#{Time.now.iso8601(5)} BROADCAST: #{ip} #{message}"
|
33
33
|
end
|
@@ -35,7 +35,7 @@ begin
|
|
35
35
|
light_udp.listen
|
36
36
|
|
37
37
|
peer_udp = LIFX::Transport::UDP.new('0.0.0.0', 56750)
|
38
|
-
peer_udp.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
38
|
+
peer_udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
39
39
|
if matchers.all? { |m| message.to_s =~ m }
|
40
40
|
puts "#{Time.now.iso8601(5)} PEER: #{ip} #{message}"
|
41
41
|
end
|
data/lib/lifx.rb
CHANGED
@@ -7,6 +7,8 @@ require "lifx/required_keyword_arguments"
|
|
7
7
|
require "lifx/utilities"
|
8
8
|
require "lifx/logging"
|
9
9
|
|
10
|
+
require "lifx/thread"
|
11
|
+
|
10
12
|
require "lifx/protocol/payload"
|
11
13
|
%w(device light sensor wan wifi message).each { |f| require "lifx/protocol/#{f}" }
|
12
14
|
require "lifx/protocol/type"
|
data/lib/lifx/color.rb
CHANGED
@@ -19,7 +19,7 @@ module LIFX
|
|
19
19
|
|
20
20
|
# Helper to create a white {Color}
|
21
21
|
# @param brightness: [Float] Valid range: `0..1`
|
22
|
-
# @param kelvin: [Integer] Valid range: `2500..
|
22
|
+
# @param kelvin: [Integer] Valid range: `2500..9000`
|
23
23
|
# @return [Color]
|
24
24
|
def white(brightness: 1.0, kelvin: DEFAULT_KELVIN)
|
25
25
|
Color.new(0, 0, brightness, kelvin)
|
@@ -37,7 +37,7 @@ module LIFX
|
|
37
37
|
extend Colors
|
38
38
|
UINT16_MAX = 65535
|
39
39
|
KELVIN_MIN = 2500
|
40
|
-
KELVIN_MAX =
|
40
|
+
KELVIN_MAX = 9000
|
41
41
|
|
42
42
|
class << self
|
43
43
|
# Helper method to create from HSB/HSV
|
@@ -54,7 +54,7 @@ module LIFX
|
|
54
54
|
# @param hue [Float] Valid range: `0..360`
|
55
55
|
# @param saturation [Float] Valid range: `0..1`
|
56
56
|
# @param brightness [Float] Valid range: `0..1`
|
57
|
-
# @param kelvin [Integer] Valid range: `2500..
|
57
|
+
# @param kelvin [Integer] Valid range: `2500..9000`
|
58
58
|
# @return [Color]
|
59
59
|
def hsbk(hue, saturation, brightness, kelvin)
|
60
60
|
new(hue, saturation, brightness, kelvin)
|
@@ -150,7 +150,7 @@ module LIFX
|
|
150
150
|
end
|
151
151
|
|
152
152
|
# Returns a new Color with the kelvin changed while keeping other attributes
|
153
|
-
# @param kelvin [Integer] Kelvin. `2500..
|
153
|
+
# @param kelvin [Integer] Kelvin. `2500..9000`
|
154
154
|
# @return [Color]
|
155
155
|
def with_kelvin(kelvin)
|
156
156
|
Color.new(hue, saturation, brightness, kelvin)
|
@@ -184,7 +184,7 @@ module LIFX
|
|
184
184
|
conditions = []
|
185
185
|
|
186
186
|
conditions << (((hue - other.hue).abs < (threshold * 360)) || begin
|
187
|
-
# FIXME: Surely there's a better way.
|
187
|
+
# FIXME: Surely there's a better way.
|
188
188
|
hues = [hue, other.hue].sort
|
189
189
|
hues[0] += 360
|
190
190
|
(hues[0] - hues[1]).abs < (threshold * 360)
|
@@ -66,13 +66,10 @@ module LIFX
|
|
66
66
|
@tcp_attempts += 1
|
67
67
|
logger.info("#{self}: Establishing connection to #{ip}:#{port}")
|
68
68
|
@tcp_transport = Transport::TCP.new(ip, port)
|
69
|
-
@tcp_transport.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
70
|
-
notify_observers(message: message, ip: ip, transport: @tcp_transport)
|
69
|
+
@tcp_transport.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
70
|
+
notify_observers(:message_received, message: message, ip: ip, transport: @tcp_transport)
|
71
71
|
end
|
72
72
|
@tcp_transport.listen
|
73
|
-
at_exit do
|
74
|
-
@tcp_transport.close if @tcp_transport
|
75
|
-
end
|
76
73
|
end
|
77
74
|
|
78
75
|
def write(message)
|
@@ -80,7 +77,9 @@ module LIFX
|
|
80
77
|
end
|
81
78
|
|
82
79
|
def close
|
83
|
-
@threads.each
|
80
|
+
@threads.each do |thr|
|
81
|
+
thr.abort
|
82
|
+
end
|
84
83
|
[@tcp_transport, @udp_transport].compact.each(&:close)
|
85
84
|
end
|
86
85
|
|
@@ -118,8 +117,7 @@ module LIFX
|
|
118
117
|
def initialize_write_queue
|
119
118
|
@queue = SizedQueue.new(MAXIMUM_QUEUE_LENGTH)
|
120
119
|
@last_write = Time.now
|
121
|
-
Thread.
|
122
|
-
Thread.new do
|
120
|
+
Thread.start do
|
123
121
|
loop do
|
124
122
|
if !connected?
|
125
123
|
sleep 0.1
|
@@ -174,5 +172,11 @@ module LIFX
|
|
174
172
|
false
|
175
173
|
end
|
176
174
|
|
175
|
+
def observer_callback_definition
|
176
|
+
{
|
177
|
+
message_received: -> (message: nil, ip: nil, transport: nil) {}
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
177
181
|
end
|
178
182
|
end
|
data/lib/lifx/light.rb
CHANGED
@@ -76,7 +76,7 @@ module LIFX
|
|
76
76
|
# @return [Color] Color
|
77
77
|
def color(refresh: false, fetch: true)
|
78
78
|
@color = nil if refresh
|
79
|
-
send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::
|
79
|
+
send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if fetch && !@color
|
80
80
|
@color
|
81
81
|
end
|
82
82
|
|
@@ -86,7 +86,7 @@ module LIFX
|
|
86
86
|
# @return [String, nil] Label
|
87
87
|
def label(refresh: false, fetch: true)
|
88
88
|
@label = nil if refresh
|
89
|
-
send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::
|
89
|
+
send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if fetch && !@label
|
90
90
|
@label
|
91
91
|
end
|
92
92
|
|
@@ -158,7 +158,7 @@ module LIFX
|
|
158
158
|
# @return [:unknown, :off, :on] Light power state
|
159
159
|
def power(refresh: false, fetch: true)
|
160
160
|
@power = nil if refresh
|
161
|
-
send_message!(Protocol::
|
161
|
+
send_message!(Protocol::Light::Get.new, wait_for: Protocol::Light::State) if !@power && fetch
|
162
162
|
case @power
|
163
163
|
when nil
|
164
164
|
:unknown
|
@@ -331,7 +331,7 @@ module LIFX
|
|
331
331
|
def tags
|
332
332
|
context.tags_for_device(self)
|
333
333
|
end
|
334
|
-
|
334
|
+
|
335
335
|
# Returns a nice string representation of the Light
|
336
336
|
# @return [String]
|
337
337
|
def to_s
|
data/lib/lifx/network_context.rb
CHANGED
@@ -4,12 +4,16 @@ require 'lifx/routing_manager'
|
|
4
4
|
require 'lifx/tag_manager'
|
5
5
|
require 'lifx/light'
|
6
6
|
require 'lifx/protocol_path'
|
7
|
+
require 'lifx/timers'
|
8
|
+
|
9
|
+
require 'weakref'
|
7
10
|
|
8
11
|
module LIFX
|
9
12
|
class NetworkContext
|
10
13
|
include Logging
|
11
14
|
include Utilities
|
12
15
|
include RequiredKeywordArguments
|
16
|
+
include Timers
|
13
17
|
extend Forwardable
|
14
18
|
|
15
19
|
# NetworkContext stores lights and ties together TransportManager, TagManager and RoutingManager
|
@@ -19,14 +23,15 @@ module LIFX
|
|
19
23
|
@devices = {}
|
20
24
|
|
21
25
|
@transport_manager = transport_manager
|
22
|
-
@transport_manager.context = self
|
23
|
-
@transport_manager.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
26
|
+
@transport_manager.context = WeakRef.new(self)
|
27
|
+
@transport_manager.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
24
28
|
handle_message(message, ip, transport)
|
25
29
|
end
|
26
30
|
|
27
31
|
reset!
|
28
32
|
|
29
33
|
@threads = []
|
34
|
+
@threads << initialize_timer_thread
|
30
35
|
end
|
31
36
|
|
32
37
|
def discover
|
@@ -44,9 +49,12 @@ module LIFX
|
|
44
49
|
|
45
50
|
def stop
|
46
51
|
@transport_manager.stop
|
52
|
+
stop_timers
|
47
53
|
@threads.each do |thread|
|
48
|
-
|
54
|
+
thread.abort
|
55
|
+
thread.join
|
49
56
|
end
|
57
|
+
@threads = nil
|
50
58
|
end
|
51
59
|
|
52
60
|
# Sends a message to their destination(s)
|
@@ -84,7 +92,7 @@ module LIFX
|
|
84
92
|
if within_sync?
|
85
93
|
raise "You cannot nest sync"
|
86
94
|
end
|
87
|
-
messages = Thread.
|
95
|
+
messages = Thread.start do
|
88
96
|
Thread.current[:sync_enabled] = true
|
89
97
|
Thread.current[:sync_messages] = messages = []
|
90
98
|
block.call
|
@@ -94,11 +102,11 @@ module LIFX
|
|
94
102
|
|
95
103
|
time = nil
|
96
104
|
try_until -> { time } do
|
97
|
-
light =
|
105
|
+
light = lights.alive.sample
|
98
106
|
time = light && light.time
|
99
107
|
end
|
100
108
|
|
101
|
-
delay = (messages.count + 1) * (1.0 / message_rate)
|
109
|
+
delay = (messages.count + 1) * (1.0 / @transport_manager.message_rate)
|
102
110
|
at_time = ((time.to_f + delay) * 1_000_000_000).to_i
|
103
111
|
messages.each do |m|
|
104
112
|
m.at_time = at_time
|
data/lib/lifx/observable.rb
CHANGED
@@ -1,36 +1,52 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
1
3
|
module LIFX
|
2
4
|
# @private
|
3
5
|
module Observable
|
4
6
|
class ObserverCallbackMismatch < ArgumentError; end
|
5
|
-
|
6
|
-
|
7
|
+
class ObserverCallbackNotFound < ArgumentError; end
|
8
|
+
|
9
|
+
def add_observer(obj, type, &callback)
|
10
|
+
if !callback_type_exists?(type)
|
11
|
+
raise ObserverCallbackNotFound.new
|
12
|
+
end
|
13
|
+
if !callback_has_required_keys?(type, callback)
|
7
14
|
raise ObserverCallbackMismatch.new
|
8
15
|
end
|
9
|
-
observers[obj] = callback
|
16
|
+
observers[type][WeakRef.new(obj)] = callback
|
10
17
|
end
|
11
18
|
|
12
|
-
def remove_observer(obj)
|
13
|
-
observers.delete(obj)
|
19
|
+
def remove_observer(obj, type)
|
20
|
+
observers[type].delete(obj)
|
14
21
|
end
|
15
22
|
|
16
|
-
def
|
17
|
-
observers.
|
23
|
+
def remove_observers
|
24
|
+
observers.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify_observers(type, **args)
|
28
|
+
observers[type].each do |_, callback|
|
18
29
|
callback.call(**args)
|
19
30
|
end
|
20
31
|
end
|
21
32
|
|
22
|
-
def
|
23
|
-
|
33
|
+
def callback_type_exists?(type)
|
34
|
+
!!observer_callback_definition[type]
|
35
|
+
end
|
36
|
+
|
37
|
+
def callback_has_required_keys?(type, callback)
|
38
|
+
(required_keys_for_callback(type) - required_keys_in_proc(callback)).empty?
|
24
39
|
end
|
25
40
|
|
26
41
|
def observer_callback_definition
|
27
|
-
|
42
|
+
{}
|
28
43
|
end
|
29
44
|
|
30
|
-
def required_keys_for_callback
|
31
|
-
@_required_keys_for_callback ||=
|
32
|
-
|
33
|
-
|
45
|
+
def required_keys_for_callback(type)
|
46
|
+
@_required_keys_for_callback ||= {}
|
47
|
+
@_required_keys_for_callback[type] ||= begin
|
48
|
+
return [] if !observer_callback_definition[type]
|
49
|
+
required_keys_in_proc(observer_callback_definition[type])
|
34
50
|
end
|
35
51
|
end
|
36
52
|
|
@@ -41,7 +57,7 @@ module LIFX
|
|
41
57
|
end
|
42
58
|
|
43
59
|
def observers
|
44
|
-
@_observers ||= {}
|
60
|
+
@_observers ||= Hash.new { |h, k| h[k] = {} }
|
45
61
|
end
|
46
62
|
end
|
47
63
|
end
|
data/lib/lifx/routing_manager.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'lifx/routing_table'
|
2
2
|
require 'lifx/tag_table'
|
3
3
|
require 'lifx/utilities'
|
4
|
+
require 'weakref'
|
4
5
|
|
5
6
|
module LIFX
|
6
7
|
# @private
|
@@ -13,11 +14,16 @@ module LIFX
|
|
13
14
|
|
14
15
|
attr_reader :context, :tag_table, :routing_table
|
15
16
|
|
17
|
+
STALE_ROUTING_TABLE_PURGE_INTERVAL = 60
|
18
|
+
|
16
19
|
def initialize(context: required!(:context))
|
17
|
-
@context = context
|
20
|
+
@context = WeakRef.new(context)
|
18
21
|
@routing_table = RoutingTable.new
|
19
22
|
@tag_table = TagTable.new
|
20
23
|
@last_refresh_seen = {}
|
24
|
+
@context.timers.every(STALE_ROUTING_TABLE_PURGE_INTERVAL) do
|
25
|
+
routing_table.clear_stale_entries
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
def resolve_target(target)
|
@@ -85,8 +91,9 @@ module LIFX
|
|
85
91
|
@routing_table.update_table(site_id: message.site_id,
|
86
92
|
device_id: message.device_id,
|
87
93
|
tag_ids: tag_ids_from_field(message.payload.tags))
|
94
|
+
when Protocol::Device::StatePanGateway, Protocol::Device::StatePower
|
95
|
+
@routing_table.update_table(site_id: message.site_id, device_id: message.device_id)
|
88
96
|
end
|
89
|
-
@routing_table.update_table(site_id: message.site_id, device_id: message.device_id)
|
90
97
|
end
|
91
98
|
|
92
99
|
MINIMUM_REFRESH_INTERVAL = 20
|
data/lib/lifx/routing_table.rb
CHANGED
@@ -7,10 +7,10 @@ module LIFX
|
|
7
7
|
@device_site_mapping = entries
|
8
8
|
end
|
9
9
|
|
10
|
-
def update_table(site_id: site_id, device_id: device_id, tag_ids: nil)
|
10
|
+
def update_table(site_id: site_id, device_id: device_id, tag_ids: nil, last_seen: Time.now)
|
11
11
|
device_mapping = @device_site_mapping[device_id] ||= Entry.new(site_id, device_id, [])
|
12
12
|
device_mapping.site_id = site_id
|
13
|
-
device_mapping.last_seen =
|
13
|
+
device_mapping.last_seen = last_seen
|
14
14
|
device_mapping.tag_ids = tag_ids if tag_ids
|
15
15
|
end
|
16
16
|
|
@@ -30,5 +30,11 @@ module LIFX
|
|
30
30
|
def entries
|
31
31
|
@device_site_mapping.values
|
32
32
|
end
|
33
|
+
|
34
|
+
def clear_stale_entries(threshold: 60 * 5)
|
35
|
+
@device_site_mapping.reject! do |device_id, entry|
|
36
|
+
entry.last_seen < Time.now - threshold
|
37
|
+
end
|
38
|
+
end
|
33
39
|
end
|
34
40
|
end
|
data/lib/lifx/site.rb
CHANGED
@@ -11,7 +11,7 @@ module LIFX
|
|
11
11
|
include Logging
|
12
12
|
include Observable
|
13
13
|
include RequiredKeywordArguments
|
14
|
-
|
14
|
+
|
15
15
|
attr_reader :id, :gateways, :tag_manager
|
16
16
|
|
17
17
|
def initialize(id: required!(:id))
|
@@ -38,8 +38,8 @@ module LIFX
|
|
38
38
|
@gateways_mutex.synchronize do
|
39
39
|
@gateways[message.device_id] ||= GatewayConnection.new
|
40
40
|
@gateways[message.device_id].handle_message(message, ip, transport)
|
41
|
-
@gateways[message.device_id].add_observer(self) do |**args|
|
42
|
-
notify_observers(**args)
|
41
|
+
@gateways[message.device_id].add_observer(self, :message_received) do |**args|
|
42
|
+
notify_observers(:message_received, **args)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -48,12 +48,12 @@ module LIFX
|
|
48
48
|
|
49
49
|
def flush(**options)
|
50
50
|
@gateways.values.map do |gateway|
|
51
|
-
Thread.
|
51
|
+
Thread.start do
|
52
52
|
gateway.flush(**options)
|
53
53
|
end
|
54
54
|
end.each(&:join)
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
def to_s
|
58
58
|
%Q{#<LIFX::Site id=#{id}>}
|
59
59
|
end
|
@@ -61,13 +61,19 @@ module LIFX
|
|
61
61
|
|
62
62
|
def stop
|
63
63
|
@threads.each do |thread|
|
64
|
-
|
64
|
+
thread.abort
|
65
65
|
end
|
66
66
|
@gateways.values.each do |gateway|
|
67
67
|
gateway.close
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
def observer_callback_definition
|
72
|
+
{
|
73
|
+
message_received: -> (message: nil, ip: nil, transport: nil) {}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
71
77
|
|
72
78
|
protected
|
73
79
|
|
data/lib/lifx/tag_manager.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
1
3
|
module LIFX
|
2
4
|
# @api private
|
3
5
|
# @private
|
@@ -15,7 +17,7 @@ module LIFX
|
|
15
17
|
class TagLimitReached < StandardError; end
|
16
18
|
|
17
19
|
def initialize(context: required!(:context), tag_table: required!(:tag_table))
|
18
|
-
@context = context
|
20
|
+
@context = WeakRef.new(context)
|
19
21
|
@tag_table = tag_table
|
20
22
|
end
|
21
23
|
|
@@ -83,7 +85,7 @@ module LIFX
|
|
83
85
|
end
|
84
86
|
end
|
85
87
|
end
|
86
|
-
|
88
|
+
|
87
89
|
protected
|
88
90
|
|
89
91
|
VALID_TAG_IDS = (0...64).to_a.freeze
|
data/lib/lifx/thread.rb
ADDED
data/lib/lifx/timers.rb
CHANGED
@@ -5,13 +5,21 @@ module LIFX
|
|
5
5
|
protected
|
6
6
|
def initialize_timer_thread
|
7
7
|
timers.after(1) {} # Just so timers.wait doesn't complain when there's no timer
|
8
|
-
Thread.
|
8
|
+
@timer_thread = Thread.start do
|
9
9
|
loop do
|
10
10
|
timers.wait
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def stop_timers
|
16
|
+
timers.each(&:cancel)
|
17
|
+
if @timer_thread
|
18
|
+
@timer_thread.abort
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
15
23
|
def timers
|
16
24
|
@timers ||= ::Timers.new
|
17
25
|
end
|
data/lib/lifx/transport.rb
CHANGED
@@ -5,7 +5,7 @@ module LIFX
|
|
5
5
|
class Transport
|
6
6
|
include Logging
|
7
7
|
include Observable
|
8
|
-
|
8
|
+
|
9
9
|
attr_reader :host, :port
|
10
10
|
|
11
11
|
def initialize(host, port, ignore_unpackable_messages: true)
|
@@ -23,7 +23,7 @@ module LIFX
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def close
|
26
|
-
|
26
|
+
remove_observers
|
27
27
|
end
|
28
28
|
|
29
29
|
def to_s
|
@@ -32,7 +32,10 @@ module LIFX
|
|
32
32
|
alias_method :inspect, :to_s
|
33
33
|
|
34
34
|
def observer_callback_definition
|
35
|
-
|
35
|
+
{
|
36
|
+
message_received: -> (message: nil, ip: nil, transport: nil) {},
|
37
|
+
disconnected: -> {}
|
38
|
+
}
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
data/lib/lifx/transport/tcp.rb
CHANGED
@@ -13,7 +13,7 @@ module LIFX
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def connected?
|
16
|
-
@socket && !@socket.closed?
|
16
|
+
!!(@socket && !@socket.closed?)
|
17
17
|
end
|
18
18
|
|
19
19
|
CONNECT_TIMEOUT = 3
|
@@ -31,17 +31,23 @@ module LIFX
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def close
|
34
|
+
super
|
34
35
|
return if !@socket
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
if !@socket.closed?
|
37
|
+
@socket.close
|
38
|
+
notify_observers(:disconnected)
|
39
|
+
end
|
38
40
|
@socket = nil
|
41
|
+
if @listener
|
42
|
+
@listener.abort
|
43
|
+
end
|
44
|
+
@listener = nil
|
39
45
|
end
|
40
46
|
|
41
47
|
HEADER_SIZE = 8
|
42
48
|
def listen
|
43
49
|
return if @listener
|
44
|
-
@listener = Thread.
|
50
|
+
@listener = Thread.start do
|
45
51
|
while @socket do
|
46
52
|
begin
|
47
53
|
header_data = @socket.recv(HEADER_SIZE, Socket::MSG_PEEK)
|
@@ -49,8 +55,8 @@ module LIFX
|
|
49
55
|
size = header.msg_size
|
50
56
|
data = @socket.recv(size)
|
51
57
|
message = Message.unpack(data)
|
52
|
-
|
53
|
-
notify_observers(message: message, ip: host, transport: self)
|
58
|
+
|
59
|
+
notify_observers(:message_received, {message: message, ip: host, transport: self})
|
54
60
|
rescue Message::UnpackError
|
55
61
|
if Config.log_invalid_messages
|
56
62
|
logger.info("#{self}: Exception occured while decoding message - #{ex}")
|
data/lib/lifx/transport/udp.rb
CHANGED
@@ -28,9 +28,8 @@ module LIFX
|
|
28
28
|
if @listener
|
29
29
|
raise "Socket already being listened to"
|
30
30
|
end
|
31
|
-
|
32
|
-
Thread.
|
33
|
-
@listener = Thread.new do
|
31
|
+
|
32
|
+
@listener = Thread.start do
|
34
33
|
reader = UDPSocket.new
|
35
34
|
reader.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
|
36
35
|
reader.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true) if Socket.const_defined?('SO_REUSEPORT')
|
@@ -39,7 +38,7 @@ module LIFX
|
|
39
38
|
begin
|
40
39
|
bytes, (_, _, ip, _) = reader.recvfrom(128)
|
41
40
|
message = Message.unpack(bytes)
|
42
|
-
notify_observers(message: message, ip: ip, transport: self)
|
41
|
+
notify_observers(:message_received, {message: message, ip: ip, transport: self})
|
43
42
|
rescue Message::UnpackError
|
44
43
|
if Config.log_invalid_messages
|
45
44
|
logger.warn("#{self}: Unrecognised bytes: #{bytes.bytes.map { |b| '%02x ' % b }.join}")
|
@@ -50,10 +49,13 @@ module LIFX
|
|
50
49
|
end
|
51
50
|
|
52
51
|
def close
|
53
|
-
|
52
|
+
super
|
54
53
|
return if !@socket
|
55
54
|
@socket.close
|
56
55
|
@socket = nil
|
56
|
+
if @listener
|
57
|
+
@listener.abort
|
58
|
+
end
|
57
59
|
end
|
58
60
|
|
59
61
|
protected
|
@@ -24,10 +24,15 @@ module LIFX
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def stop
|
27
|
-
|
27
|
+
@context = nil
|
28
|
+
remove_observers
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
+
def observer_callback_definition
|
32
|
+
{
|
33
|
+
message_received: -> (message: nil, ip: nil, transport: nil) {}
|
34
|
+
}
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -21,7 +21,7 @@ module LIFX
|
|
21
21
|
|
22
22
|
def flush(**options)
|
23
23
|
@sites.values.map do |site|
|
24
|
-
Thread.
|
24
|
+
Thread.start do
|
25
25
|
site.flush(**options)
|
26
26
|
end
|
27
27
|
end.each(&:join)
|
@@ -31,8 +31,7 @@ module LIFX
|
|
31
31
|
DISCOVERY_INTERVAL = 15 # seconds
|
32
32
|
def discover
|
33
33
|
stop_discovery
|
34
|
-
Thread.
|
35
|
-
@discovery_thread = Thread.new do
|
34
|
+
@discovery_thread = Thread.start do
|
36
35
|
@last_request_seen = Time.at(0)
|
37
36
|
message = Message.new(path: ProtocolPath.new(tagged: true), payload: Protocol::Device::GetPanGateway.new)
|
38
37
|
logger.info("Discovering gateways on #{@bind_ip}:#{@port}")
|
@@ -49,12 +48,19 @@ module LIFX
|
|
49
48
|
end
|
50
49
|
|
51
50
|
def stop_discovery
|
52
|
-
|
51
|
+
if @discovery_thread
|
52
|
+
@discovery_thread.abort
|
53
|
+
end
|
53
54
|
end
|
54
55
|
|
55
56
|
def stop
|
57
|
+
super
|
56
58
|
stop_discovery
|
57
|
-
|
59
|
+
stop_timers
|
60
|
+
@threads.each do |thr|
|
61
|
+
thr.abort
|
62
|
+
end
|
63
|
+
@peer_transport.close
|
58
64
|
@transport.close
|
59
65
|
@sites.values.each do |site|
|
60
66
|
site.stop
|
@@ -152,17 +158,17 @@ module LIFX
|
|
152
158
|
|
153
159
|
def create_broadcast_transport
|
154
160
|
@transport = Transport::UDP.new(@send_ip, @port)
|
155
|
-
@transport.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
161
|
+
@transport.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
156
162
|
handle_broadcast_message(message, ip, @transport)
|
157
|
-
notify_observers(message: message, ip: ip, transport: transport)
|
163
|
+
notify_observers(:message_received, message: message, ip: ip, transport: transport)
|
158
164
|
end
|
159
165
|
@transport.listen(ip: @bind_ip)
|
160
166
|
end
|
161
167
|
|
162
168
|
def create_peer_transport
|
163
169
|
@peer_transport = Transport::UDP.new('255.255.255.255', @peer_port)
|
164
|
-
@peer_transport.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
165
|
-
notify_observers(message: message, ip: ip, transport: transport)
|
170
|
+
@peer_transport.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
171
|
+
notify_observers(:message_received, message: message, ip: ip, transport: transport)
|
166
172
|
end
|
167
173
|
@peer_transport.listen(ip: @bind_ip)
|
168
174
|
end
|
@@ -174,8 +180,8 @@ module LIFX
|
|
174
180
|
when Protocol::Device::StatePanGateway
|
175
181
|
if !@sites.has_key?(message.path.site_id)
|
176
182
|
@sites[message.path.site_id] = Site.new(id: message.path.site_id)
|
177
|
-
@sites[message.path.site_id].add_observer(self) do |**args|
|
178
|
-
notify_observers(**args)
|
183
|
+
@sites[message.path.site_id].add_observer(self, :message_received) do |**args|
|
184
|
+
notify_observers(:message_received, **args)
|
179
185
|
end
|
180
186
|
end
|
181
187
|
@sites[message.path.site_id].handle_message(message, ip, transport)
|
data/lib/lifx/version.rb
CHANGED
@@ -19,7 +19,7 @@ module LIFX
|
|
19
19
|
sleep 1
|
20
20
|
|
21
21
|
msgs = []
|
22
|
-
udp.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
22
|
+
udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
23
23
|
msgs << message if message.payload.is_a?(Protocol::Light::SetWaveform)
|
24
24
|
end
|
25
25
|
udp.listen
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
module LIFX
|
4
4
|
describe RoutingManager do
|
5
5
|
describe '#tags_for_device_id' do
|
6
|
-
subject(:manager) { RoutingManager.new(context: double) }
|
6
|
+
subject(:manager) { RoutingManager.new(context: double(timers: double(every: double))) }
|
7
7
|
|
8
8
|
before do
|
9
9
|
['Some label', 'Another label', 'Much label'].each_with_index do |lbl, i|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LIFX
|
4
|
+
describe RoutingTable do
|
5
|
+
describe '#clear_stale_entries' do
|
6
|
+
subject(:table) { RoutingTable.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
table.update_table(site_id: 'site', device_id: 'stale device', last_seen: Time.now - 305)
|
10
|
+
table.update_table(site_id: 'site', device_id: 'recent device', last_seen: Time.now)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'clears only entries older than 5 minutes' do
|
14
|
+
expect(table.entries.count).to eq(2)
|
15
|
+
table.clear_stale_entries
|
16
|
+
expect(table.entries.count).to eq(1)
|
17
|
+
expect(table.entries.first.device_id).to eq('recent device')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/transport/udp_spec.rb
CHANGED
@@ -24,7 +24,7 @@ module LIFX
|
|
24
24
|
let(:socket) { UDPSocket.new }
|
25
25
|
let(:messages) { [] }
|
26
26
|
before do
|
27
|
-
udp.add_observer(self) do |message: nil, ip: nil, transport: nil|
|
27
|
+
udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
28
28
|
messages << message
|
29
29
|
end
|
30
30
|
udp.listen
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lifx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Chen (chendo)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|
@@ -152,6 +152,7 @@ files:
|
|
152
152
|
- lib/lifx/tag_manager.rb
|
153
153
|
- lib/lifx/tag_table.rb
|
154
154
|
- lib/lifx/target.rb
|
155
|
+
- lib/lifx/thread.rb
|
155
156
|
- lib/lifx/timers.rb
|
156
157
|
- lib/lifx/transport.rb
|
157
158
|
- lib/lifx/transport/tcp.rb
|
@@ -170,6 +171,7 @@ files:
|
|
170
171
|
- spec/message_spec.rb
|
171
172
|
- spec/protocol_path_spec.rb
|
172
173
|
- spec/routing_manager_spec.rb
|
174
|
+
- spec/routing_table_spec.rb
|
173
175
|
- spec/spec_helper.rb
|
174
176
|
- spec/transport/udp_spec.rb
|
175
177
|
- spec/transport_spec.rb
|
@@ -209,6 +211,7 @@ test_files:
|
|
209
211
|
- spec/message_spec.rb
|
210
212
|
- spec/protocol_path_spec.rb
|
211
213
|
- spec/routing_manager_spec.rb
|
214
|
+
- spec/routing_table_spec.rb
|
212
215
|
- spec/spec_helper.rb
|
213
216
|
- spec/transport/udp_spec.rb
|
214
217
|
- spec/transport_spec.rb
|