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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9cdb2d3a2365da0d3411c5fcea5763a749ea723e
4
- data.tar.gz: 7bb97947fe58342b0355bd67775016d05ac7635f
3
+ metadata.gz: 357ce53a9c5e511833c6917f09d72396d203df38
4
+ data.tar.gz: 135ecdc739e64aaf4615702eedb273f9c3cb570e
5
5
  SHA512:
6
- metadata.gz: ad5318cf0898bc5d07e6bc0e8a5818c2fff94f2dd3485e5d47d12f5e1d3aaa14966eb20436b0a56d17b1cd7a9a3fa61e7699f8ab1a196abf3cafb32437e42faf
7
- data.tar.gz: 67edd75288e6fb22a5fc614473bf4c8b912e51c06874c8eaffe09170fbda73439a9220aa0d398d2d5cbd92dc02ed697dff63ac3e812097f3601d7f5828fd5dcc
6
+ metadata.gz: 7d2ad759de8697cf02de2914782742315be5c545678bae5b848e9dfeb13acc945823b78a1145f82df308e2dec308c31ec01aae6601c67262632cf64f36f56867
7
+ data.tar.gz: 212a318bab27a3f967971364e8384daa82842334a3b8f9327de66c12e3e01309b9b2d015f9c650dea8e0e91ea1cbf63a7ed3b2e722fc47186889b21f908506a4
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/Gemfile CHANGED
@@ -1,4 +1,14 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ group :development do
4
+ gem 'redcarpet'
5
+ gem 'erubis'
6
+ gem 'ruby-prof'
7
+ gem 'pry-byebug'
8
+ gem 'pry-rescue'
9
+ gem 'pry-stack_explorer'
10
+ gem 'yard'
11
+ end
12
+
3
13
  # Specify your gem's dependencies in lifx.gemspec
4
14
  gemspec
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 LIFX Labs
1
+ Copyright (c) 2014 LIFX Labs
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,29 +1,87 @@
1
1
  # LIFX
2
2
 
3
- A gem for LIFX bulbs.
3
+ This gem allows you to control your [LIFX](http://lifx.co) lights.
4
+
5
+ It handles discovery, gateway connections, tags, and provides a object-based API
6
+ for talking to Lights.
7
+
8
+ Due to the nature of the current protocol, some methods are asynchronous.
9
+
10
+ This gem is in an early beta state. Expect breaking API changes.
11
+
12
+ ## Requirements
13
+
14
+ * Ruby 2.1.1
15
+ * Bundler
4
16
 
5
17
  ## Installation
6
18
 
7
19
  Add this line to your application's Gemfile:
8
20
 
9
- gem 'lifx'
21
+ ```ruby
22
+ gem 'lifx', git: "git@github.com:LIFX/lifx-gem.git"
23
+ ```
10
24
 
11
25
  And then execute:
12
26
 
13
- $ bundle
27
+ ```shell
28
+ $ bundle
29
+ ```
30
+
31
+ ## Usage
14
32
 
15
- Or install it yourself as:
33
+ ```ruby
34
+ client = LIFX::Client.lan # Talk to bulbs on the LAN
35
+ client.discover! do |c| # Discover lights. Blocks until a light with the label 'Office' is found
36
+ c.lights.with_label('Office')
37
+ end
38
+ # Blocks for a default of 10 seconds or until a light is found
39
+ client.lights.turn_on # Tell all lights to turn on
40
+ light = client.lights.with_label('Office') # Get light with label 'Office'
16
41
 
17
- $ gem install lifx
42
+ # Set the Office light to pale green over 5 seconds
43
+ green = LIFX::Color.green(saturation: 0.5)
44
+ light.set_color(green, duration: 5) # Light#set_color is asynchronous
18
45
 
19
- ## Usage
46
+ sleep 5 # Wait for light to finish changing
47
+ light.set_label('My Office')
48
+
49
+ light.add_tag('Offices') # Add tag to light
50
+
51
+ client.lights.with_tag('Offices').turn_off
52
+
53
+ client.flush # Wait until all the packets have been sent
54
+ ```
55
+
56
+ ## Documentation
57
+
58
+ Documentation is available at http://rubydoc.info/gems/lifx. Please note that undocumented classes/methods and classes/methods marked private are not intended for public use.
59
+
60
+ ## Examples
61
+
62
+ Examples are located in the `examples/` folder.
63
+
64
+ * [travis-build-light](examples/travis-build-light/build-light.rb): Changes the colour of a light based on the build status of a project on Travis.
65
+ * [auto-off](examples/auto-off/auto-off.rb): Turns a light off after X seconds of it being detected turned on.
66
+ * [identify](examples/identify/identify.rb): Use divide-and-conquer search algorithm to identify a light visually.
67
+
68
+ ## Useful utilities
69
+
70
+ * [lifx-console](http://github.com/chendo/lifx-console): A Pry-enabled REPL to play with LIFX easily.
71
+ * [lifx-http](http://github.com/chendo/lifx-http): A HTTP API for LIFX.
72
+
73
+ ## Testing
74
+
75
+ Run with `bundle exec rspec`.
76
+
77
+ The integration specs rely on a least one device tagged with `Test` to function. At this point, they can fail occasionally due to the async nature of the protocol, and there's not much coverage at the moment as the architecture is still in flux.
78
+
79
+ A more comprehensive test suite is in the works.
80
+
81
+ ## Feedback
20
82
 
21
- TODO: Write usage instructions here
83
+ Please file an issue for general feedback, bugs, clarification, examples, etc etc. Feel free to hit me up on Twitter, too: [@chendo](https://twitter.com/chendo).
22
84
 
23
- ## Contributing
85
+ ## License
24
86
 
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
87
+ MIT. See `LICENSE.txt`
data/Rakefile CHANGED
@@ -1 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :console do
4
+ require "lifx"
5
+ require "pry"
6
+ if ENV['DEBUG']
7
+ LIFX::Config.logger = Yell.new(STDERR)
8
+ end
9
+ LIFX::Client.lan.discover! do |c|
10
+ c.lights.count > 0
11
+ end
12
+ LIFX::Client.lan.pry
13
+ end
data/bin/lifx-console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # LIFX Console
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
4
+ require 'lifx'
5
+ require 'rubygems'
6
+ begin
7
+ require 'pry'
8
+ rescue LoadError
9
+ $stderr.puts("You must have pry installed to use lifx-console. gem install pry")
10
+ end
11
+
12
+ c = LIFX::Client.lan
13
+ c.discover!
14
+ c.extend(LIFX::Colors)
15
+ c.pry
data/bin/lifx-snoop ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ # lifx-snoop is a utility that snoops on LIFX UDP traffic on both the broadcast port
3
+ # and the peer broadcast port.
4
+ #
5
+ # Usage:
6
+ # lifx-snoop [regexp, ...]
7
+ #
8
+ # By default, it will show all messages. Any arguments passed in will become
9
+ # regular expressions, which will then match on the string representation of the
10
+ # message.
11
+ #
12
+ # Example:
13
+ # lifx-snoop Light::Get # Shows only Light::Get messages
14
+ # lifx-snoop Light::State site=d073d5000000 # Shows only Light::State messages matching site d073d5000000
15
+
16
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "lib")
17
+ require 'lifx'
18
+ require 'time'
19
+
20
+ matchers = ARGV.map do |arg|
21
+ Regexp.new(arg, Regexp::IGNORECASE)
22
+ end
23
+
24
+ if matchers.empty?
25
+ matchers << //
26
+ end
27
+
28
+ begin
29
+ light_udp = LIFX::Transport::UDP.new('0.0.0.0', 56700)
30
+ light_udp.add_observer(self) do |message:, ip:, transport:|
31
+ if matchers.all? { |m| message.to_s =~ m }
32
+ puts "#{Time.now.iso8601(5)} BROADCAST: #{ip} #{message}"
33
+ end
34
+ end
35
+ light_udp.listen
36
+
37
+ peer_udp = LIFX::Transport::UDP.new('0.0.0.0', 56750)
38
+ peer_udp.add_observer(self) do |message:, ip:, transport:|
39
+ if matchers.all? { |m| message.to_s =~ m }
40
+ puts "#{Time.now.iso8601(5)} PEER: #{ip} #{message}"
41
+ end
42
+ end
43
+ peer_udp.listen
44
+ rescue LIFX::Message::UnsupportedProtocolVersion
45
+ end
46
+
47
+ puts "Listening on 56700 and 56750..."
48
+ puts "^C to quit."
49
+
50
+ sleep
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'lifx', path: '../../'
@@ -0,0 +1,35 @@
1
+ # Auto-off script
2
+ #
3
+ # This script will turn a light off after 10 seconds when it has detected it has turned on.
4
+ # To run: bundle; bundle ruby auto-off.rb [light label]
5
+
6
+ require 'bundler'
7
+ Bundler.require
8
+
9
+ AUTO_OFF_DELAY = 10
10
+
11
+ label = ARGV.first
12
+ lifx = LIFX::Client.lan
13
+ lifx.discover! do
14
+ label ? lifx.lights.with_label(label) : lifx.lights.first
15
+ end
16
+
17
+ light = label ? lifx.lights.with_label(label) : lifx.lights.first
18
+
19
+ puts "#{light} will be automatically turned off after #{AUTO_OFF_DELAY} seconds"
20
+
21
+ thr = Thread.new do
22
+ loop do
23
+ if light.on? && !(@off_thr && @off_thr.alive?)
24
+ puts "Light detected on. Turning off in #{AUTO_OFF_DELAY}"
25
+ @off_thr = Thread.new do
26
+ sleep AUTO_OFF_DELAY
27
+ light.turn_off
28
+ puts "Turning off"
29
+ end
30
+ end
31
+ sleep 1
32
+ end
33
+ end
34
+
35
+ thr.join
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'lifx', path: '../../'
@@ -0,0 +1,70 @@
1
+ # Identify
2
+ #
3
+ # This example uses a divide and conquer search algorithm to identify a light visually.
4
+ # It will set all lights to white, then partition them by colour.
5
+ # On each iteration, it will ask you what colour the bulb you're trying to identify is showing
6
+ # until it narrows it down to a single bulb.
7
+ # Please note it does not restore the light state before identification.
8
+
9
+ require 'bundler'
10
+ Bundler.require
11
+
12
+ COLOURS = {
13
+ 'red' => [0, 1, 1],
14
+ 'yellow' => [50, 1, 1],
15
+ 'green' => [120, 1, 1],
16
+ 'blue' => [220, 1, 1]
17
+ }
18
+
19
+ LIFX::Config.logger = Yell.new(STDERR, :level => :error)
20
+ c = LIFX::Client.lan
21
+ c.discover
22
+ 5.times do
23
+ c.lights.refresh
24
+ c.flush
25
+ sleep 1
26
+ puts "Lights found: #{c.lights.count}"
27
+ end
28
+
29
+
30
+ def partition(list, partitions)
31
+ [].tap do |array|
32
+ list.each_slice((list.count / partitions.to_f).ceil) do |chunk|
33
+ array << chunk
34
+ end
35
+ end
36
+ end
37
+
38
+ lights = c.lights.to_a
39
+ mapping = {}
40
+
41
+ while lights.count > 1
42
+ puts "Searching through #{lights.count} lights..."
43
+ c.lights.set_color(LIFX::Color.white)
44
+ partitions = partition(lights, COLOURS.values.count)
45
+ COLOURS.keys.each_with_index do |color_name, index|
46
+ color = LIFX::Color.hsb(*COLOURS[color_name])
47
+ mapping[color_name] = partitions[index]
48
+ next if partitions[index].nil?
49
+ partitions[index].each do |l|
50
+ l.set_color(color, duration: 0)
51
+ end
52
+ end
53
+ puts "Waiting for flush."
54
+ c.flush
55
+ puts "What colour is the bulb you're trying to identify? (#{COLOURS.keys.join(', ')})"
56
+ resp = gets.strip
57
+ if mapping.has_key?(resp)
58
+ lights = mapping[resp]
59
+ else
60
+ puts "Colour not found. Iterating again"
61
+ end
62
+ end
63
+
64
+ if lights.count == 1
65
+ puts "Light identified: #{lights.first}"
66
+ else
67
+ puts "No bulbs found."
68
+ end
69
+
70
+ c.flush
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'lifx', path: '../../'
4
+ gem 'travis'
@@ -0,0 +1,57 @@
1
+ # A Travis CI Build Light
2
+ #
3
+ # To run: bundle; bundle ruby build-light.rb [user/repo]
4
+ # Please note this doesn't have error handling yet.
5
+
6
+ require 'bundler'
7
+ Bundler.require
8
+
9
+ lifx = LIFX::Client.lan
10
+ lifx.discover!
11
+ sleep 2 # Wait for tag data to come back
12
+
13
+ light = if lifx.tags.include?('Build Light')
14
+ lights = lifx.lights.with_tag('Build Light')
15
+ if lights.empty?
16
+ puts "No lights in the Build Light tag, using the first light found."
17
+ lifx.lights.first
18
+ else
19
+ lights
20
+ end
21
+ else
22
+ lifx.lights.first
23
+ end
24
+
25
+ if !light
26
+ puts "No LIFX lights found."
27
+ exit 1
28
+ end
29
+
30
+ puts "Using light(s): #{light}"
31
+ repo_path = ARGV.first || 'rails/rails'
32
+
33
+ repo = Travis::Repository.find(repo_path)
34
+ puts "Watching repository #{repo.slug}"
35
+
36
+ def update_light(light, repository)
37
+ color = case repository.color
38
+ when 'green'
39
+ LIFX::Color.hsb(120, 1, 1)
40
+ when 'yellow'
41
+ LIFX::Color.hsb(60, 1, 1)
42
+ when 'red'
43
+ LIFX::Color.hsb(0, 1, 1)
44
+ end
45
+
46
+ light.turn_on!
47
+ light.set_color(color, duration: 0.2)
48
+ puts "#{Time.now}: Build ##{repository.last_build.number} is #{repository.color}."
49
+ end
50
+
51
+ update_light(light, repo)
52
+
53
+ Travis.listen(repo) do |stream|
54
+ stream.on('build:started', 'build:finished') do |event|
55
+ update_light(light, event.repository)
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ module BinData
2
+ class Bool < Primitive
3
+ uint8 :_value
4
+
5
+ def get
6
+ (self._value || 0) > 0
7
+ end
8
+
9
+ def set(value)
10
+ self._value = value ? 1 : 0
11
+ end
12
+ end
13
+
14
+ class BoolBit1 < Primitive
15
+ bit1le :_value
16
+
17
+ def get
18
+ (self._value || 0) > 0
19
+ end
20
+
21
+ def set(value)
22
+ self._value = value ? 1 : 0
23
+ end
24
+
25
+ def !
26
+ !get
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'stringio'
2
+ BinData::Record
3
+ module BinData
4
+ class Record
5
+ def pack
6
+ s = StringIO.new
7
+ write(s)
8
+ s.string.b
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,136 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+ require 'yell'
4
+
5
+ require 'lifx/network_context'
6
+ require 'lifx/light_collection'
7
+
8
+ module LIFX
9
+ # {LIFX::Client} is the top level interface to the library. It mainly maps
10
+ # methods to the backing {NetworkContext} instance.
11
+ class Client
12
+
13
+ class << self
14
+ # Returns a {Client} set up for accessing devices on the LAN
15
+ #
16
+ # @return [Client] A LAN LIFX::Client
17
+ def lan
18
+ @lan ||= new
19
+ end
20
+ end
21
+
22
+ extend Forwardable
23
+ include Utilities
24
+
25
+ # Refers to the client's network context.
26
+ # @return [NetworkContext] Enclosed network context
27
+ attr_reader :context
28
+
29
+ # @param transport: [:lan] Specify which transport to use
30
+ def initialize(transport: :lan)
31
+ @context = NetworkContext.new(transport: transport)
32
+ end
33
+
34
+ # Default timeout in seconds for discovery
35
+ DISCOVERY_DEFAULT_TIMEOUT = 10
36
+
37
+ # This method tells the {NetworkContext} to look for devices asynchronously.
38
+ # @return [Client] self
39
+ def discover
40
+ @context.discover
41
+ end
42
+
43
+ class DiscoveryTimeout < Timeout::Error; end
44
+ # This method tells the {NetworkContext} to look for devices, and will block
45
+ # until there's at least one device.
46
+ #
47
+ # @example Wait until at least three lights have been found
48
+ # client.discover! { |c| c.lights.count >= 3 }
49
+ #
50
+ # @param timeout: [Numeric] How long to try to wait for before returning
51
+ # @param condition_interval: [Numeric] Seconds between evaluating the block
52
+ # @yield [Client] This block is evaluated every `condition_interval` seconds. If true, method returns. If no block is supplied, it will block until it finds at least one light.
53
+ # @raise [DiscoveryTimeout] If discovery times out
54
+ # @return [Client] self
55
+ def discover!(timeout: DISCOVERY_DEFAULT_TIMEOUT, condition_interval: 0.1, &block)
56
+ block ||= -> { self.lights.count > 0 }
57
+ try_until -> { block.arity == 1 ? block.call(self) : block.call },
58
+ timeout: timeout,
59
+ timeout_exception: DiscoveryTimeout,
60
+ condition_interval: condition_interval do
61
+ discover
62
+ end
63
+ self
64
+ end
65
+
66
+ # Sends a request to refresh devices and tags.
67
+ # @return [void]
68
+ def refresh
69
+ @context.refresh
70
+ end
71
+
72
+ # This method takes a block consisting of multiple asynchronous color or power changing targets
73
+ # and it will try to schedule them so they run at the same time.
74
+ #
75
+ # You cannot nest `sync` calls, nor call synchronous methods inside a `sync` block.
76
+ #
77
+ # Due to messaging rate constraints, the amount of messages determine the delay before
78
+ # the commands are executed. This method also assumes all the lights have the same time.
79
+ # @example This example sets all the lights to a random colour at the same time.
80
+ # client.sync do
81
+ # client.lights.each do |light|
82
+ # light.set_color(rand(4) * 90, 1, 1)
83
+ # end
84
+ # end
85
+ #
86
+ # @note This method is in alpha and might go away. Use tags for better group messaging.
87
+ # @yield Block of commands to synchronize
88
+ # @return [Float] Number of seconds until commands are executed
89
+ def sync(&block)
90
+ @context.sync(&block)
91
+ end
92
+
93
+ # This is the same as {#sync}, except it will block until the commands have been executed.
94
+ # @see #sync
95
+ # @return [Float] Number of seconds slept
96
+ def sync!(&block)
97
+ sync(&block).tap do |delay|
98
+ sleep(delay)
99
+ end
100
+ end
101
+
102
+ # @return [LightCollection] Lights available to the client
103
+ # @see [NetworkContext#lights]
104
+ def lights
105
+ context.lights
106
+ end
107
+
108
+ # @return [Array<String>] All tags visible to the client
109
+ # @see [NetworkContext#tags]
110
+ def tags
111
+ context.tags
112
+ end
113
+
114
+ # @return [Array<String>] Tags that are currently unused by known devices
115
+ # @see [NetworkContext#unused_tags]
116
+ def unused_tags
117
+ context.unused_tags
118
+ end
119
+
120
+ # Purges unused tags from the system.
121
+ # Should only use when all devices are on the network, otherwise
122
+ # offline devices using their tags will not be tagged correctly.
123
+ # @return [Array<String>] Tags that were purged
124
+ def purge_unused_tags!
125
+ context.purge_unused_tags!
126
+ end
127
+
128
+ # Blocks until all messages have been sent to the gateways
129
+ # @param timeout: [Numeric] When specified, flush will wait `timeout:` seconds before throwing `Timeout::Error`
130
+ # @raise [Timeout::Error] if `timeout:` was exceeded while waiting for send queue to flush
131
+ # @return [void]
132
+ def flush(timeout: nil)
133
+ context.flush(timeout: timeout)
134
+ end
135
+ end
136
+ end