lifx 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
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