lifx 0.0.1 → 0.4.0

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