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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e74701d68bb9de49cf00008a94e46f8721c5d3e6
4
- data.tar.gz: 8dd72e9b4f78d2bc70d042142879b3c98df2b172
3
+ metadata.gz: c4d3a3b49f4182b136607335b3439a6d8c475af4
4
+ data.tar.gz: 5119e5d44ca2c1649a9f4226678dc1c3c04900c3
5
5
  SHA512:
6
- metadata.gz: 8c620c92cf6dd96dac5b2abac78563e3e7276fa7a3a08b17f010eb7fd123320a3cce52d807c6c6eb7cd46d595800e8fba539625cf287cb858888ef812566e148
7
- data.tar.gz: 98187dcc6c15b1d53c48ae6cd7ed95dc86c781b3218e2ab79a51d8bf4c56570b1b3152b97091b56f3e41ee3455cde8449c1eed5b408cad1d75b88dc48df227f8
6
+ metadata.gz: 76282a8c2b04cf4f0c0b199532d2847b97eacf7f8313c246c9aedafbf1cf6d35b02d9c75e75d472bb4f41a278988b92aae63cace417e5902a9e84095161f62f2
7
+ data.tar.gz: 0b81b9d0165ce833a8436bb597a5f6398ebf4cebee892f02116a6af9a7450b851c782639c9c077b8672bd3b836cf1f6be03e7dfb219ca02f3255165b12311355
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 0.4.8
2
+
3
+ - Routing table is only updated from State messages
4
+ - Fix memory leaks
5
+
1
6
  ### 0.4.7
2
7
 
3
8
  - Only create Light devices when a Light::State is received
@@ -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
@@ -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"
@@ -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..10000`
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 = 10000
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..10000`
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..10000`
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 { |thr| Thread.kill(thr) }
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.abort_on_exception = true
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
@@ -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::Get) if fetch && !@color
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::Get) if fetch && !@label
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::Device::GetPower.new, wait_for: Protocol::Device::StatePower) if !@power && fetch
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
@@ -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
- Thread.kill(thread)
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.new do
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 = gateways.sample
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
@@ -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
- def add_observer(obj, &callback)
6
- if !callback_has_required_keys?(callback)
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 notify_observers(**args)
17
- observers.each do |_, callback|
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 callback_has_required_keys?(callback)
23
- (required_keys_for_callback - required_keys_in_proc(callback)).empty?
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
- nil
42
+ {}
28
43
  end
29
44
 
30
- def required_keys_for_callback
31
- @_required_keys_for_callback ||= begin
32
- return [] if !observer_callback_definition
33
- required_keys_in_proc(observer_callback_definition)
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
@@ -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
@@ -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 = Time.now
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
@@ -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.new do
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
- Thread.kill(thread)
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
 
@@ -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
@@ -0,0 +1,11 @@
1
+ require 'thread'
2
+
3
+ module LIFX
4
+ class Thread < ::Thread
5
+ def abort
6
+ if alive?
7
+ kill.join
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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.new do
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
@@ -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
- raise NotImplementedError
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
- -> (message: nil, ip: nil, transport: nil) {}
35
+ {
36
+ message_received: -> (message: nil, ip: nil, transport: nil) {},
37
+ disconnected: -> {}
38
+ }
36
39
  end
37
40
  end
38
41
  end
@@ -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
- Thread.kill(@listener) if @listener
36
- @listener = nil
37
- @socket.close if !@socket.closed?
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.new do
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}")
@@ -28,9 +28,8 @@ module LIFX
28
28
  if @listener
29
29
  raise "Socket already being listened to"
30
30
  end
31
-
32
- Thread.abort_on_exception = true
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
- Thread.kill(@listener) if @listener
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
- raise NotImplementedError
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.new do
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.abort_on_exception = true
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
- Thread.kill(@discovery_thread) if @discovery_thread
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
- @threads.each(&:kill)
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)
@@ -1,3 +1,3 @@
1
1
  module LIFX
2
- VERSION = "0.4.7"
2
+ VERSION = "0.4.8"
3
3
  end
@@ -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
@@ -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.7
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-04-16 00:00:00.000000000 Z
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