lifx-lan 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/.yardopts +3 -0
- data/CHANGES.md +45 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +23 -0
- data/README.md +15 -0
- data/Rakefile +20 -0
- data/bin/lifx-snoop +50 -0
- data/examples/auto-off/auto-off.rb +34 -0
- data/examples/blink/blink.rb +19 -0
- data/examples/identify/identify.rb +69 -0
- data/examples/travis-build-light/build-light.rb +57 -0
- data/lib/bindata_ext/bool.rb +30 -0
- data/lib/bindata_ext/record.rb +11 -0
- data/lib/lifx-lan.rb +27 -0
- data/lib/lifx/lan/client.rb +149 -0
- data/lib/lifx/lan/color.rb +199 -0
- data/lib/lifx/lan/config.rb +17 -0
- data/lib/lifx/lan/firmware.rb +60 -0
- data/lib/lifx/lan/gateway_connection.rb +185 -0
- data/lib/lifx/lan/light.rb +440 -0
- data/lib/lifx/lan/light_collection.rb +111 -0
- data/lib/lifx/lan/light_target.rb +185 -0
- data/lib/lifx/lan/logging.rb +14 -0
- data/lib/lifx/lan/message.rb +168 -0
- data/lib/lifx/lan/network_context.rb +188 -0
- data/lib/lifx/lan/observable.rb +66 -0
- data/lib/lifx/lan/protocol/address.rb +25 -0
- data/lib/lifx/lan/protocol/device.rb +387 -0
- data/lib/lifx/lan/protocol/header.rb +24 -0
- data/lib/lifx/lan/protocol/light.rb +142 -0
- data/lib/lifx/lan/protocol/message.rb +19 -0
- data/lib/lifx/lan/protocol/metadata.rb +23 -0
- data/lib/lifx/lan/protocol/payload.rb +12 -0
- data/lib/lifx/lan/protocol/sensor.rb +31 -0
- data/lib/lifx/lan/protocol/type.rb +204 -0
- data/lib/lifx/lan/protocol/wan.rb +51 -0
- data/lib/lifx/lan/protocol/wifi.rb +102 -0
- data/lib/lifx/lan/protocol_path.rb +85 -0
- data/lib/lifx/lan/required_keyword_arguments.rb +12 -0
- data/lib/lifx/lan/routing_manager.rb +114 -0
- data/lib/lifx/lan/routing_table.rb +48 -0
- data/lib/lifx/lan/seen.rb +25 -0
- data/lib/lifx/lan/site.rb +97 -0
- data/lib/lifx/lan/tag_manager.rb +111 -0
- data/lib/lifx/lan/tag_table.rb +49 -0
- data/lib/lifx/lan/target.rb +24 -0
- data/lib/lifx/lan/thread.rb +13 -0
- data/lib/lifx/lan/timers.rb +29 -0
- data/lib/lifx/lan/transport.rb +46 -0
- data/lib/lifx/lan/transport/tcp.rb +91 -0
- data/lib/lifx/lan/transport/udp.rb +87 -0
- data/lib/lifx/lan/transport_manager.rb +43 -0
- data/lib/lifx/lan/transport_manager/lan.rb +169 -0
- data/lib/lifx/lan/utilities.rb +36 -0
- data/lib/lifx/lan/version.rb +5 -0
- data/lifx-lan.gemspec +26 -0
- data/spec/color_spec.rb +43 -0
- data/spec/gateway_connection_spec.rb +30 -0
- data/spec/integration/client_spec.rb +42 -0
- data/spec/integration/light_spec.rb +56 -0
- data/spec/integration/tags_spec.rb +42 -0
- data/spec/light_collection_spec.rb +37 -0
- data/spec/message_spec.rb +183 -0
- data/spec/protocol_path_spec.rb +109 -0
- data/spec/routing_manager_spec.rb +25 -0
- data/spec/routing_table_spec.rb +23 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/transport/udp_spec.rb +44 -0
- data/spec/transport_spec.rb +14 -0
- metadata +187 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4ef94c0b7aaaa1efaf0ed5510237c2f8e8638c69
|
4
|
+
data.tar.gz: 24ecd96f704325934c37882f5bd7f7417dbb4287
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87d2775a26c2104f0789d07a9bb38ea1dda76ca5d03ac3031bdd41190f76cabc34e932a5785edc940e67458fdab064874a10d50f6a06a0d41da159d8f596157a
|
7
|
+
data.tar.gz: 18691da3eb56988e02c2a3fb94ec9f74b1271a901ea80b76a80e6c91e85bad86b32ea7c5c77051687df06495a88e406810a8fbea5dfa6f48bd6ca53d20c1bc36
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/CHANGES.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
### 0.4.10
|
2
|
+
|
3
|
+
- Fix `message_rate` visibility bug
|
4
|
+
|
5
|
+
### 0.4.9
|
6
|
+
|
7
|
+
- Message rate checker only runs when connection is idle
|
8
|
+
- Now uses `LIFX::TimeoutError` rather than `Timeout::Error` for internal timeout exceptions
|
9
|
+
|
10
|
+
### 0.4.8
|
11
|
+
|
12
|
+
- Routing table is only updated from State messages
|
13
|
+
- Fix memory leaks
|
14
|
+
|
15
|
+
### 0.4.7
|
16
|
+
|
17
|
+
- Only create Light devices when a Light::State is received
|
18
|
+
- Message rate checker only checks lights considered alive
|
19
|
+
|
20
|
+
### 0.4.6.1
|
21
|
+
|
22
|
+
- Fix `Time.parse` issue
|
23
|
+
|
24
|
+
### 0.4.6
|
25
|
+
|
26
|
+
- `Color#==` has been renamed to `Color#similar_to?`
|
27
|
+
- Broadcast IP configurable through `LIFX::Config.broadcast_ip`
|
28
|
+
- Removed Yell gem. Use stdlib Logger instead
|
29
|
+
- Uninitialized lights no longer shows up in `Client#lights`
|
30
|
+
- Handle Rubies that don't have IPv6 enabled
|
31
|
+
|
32
|
+
### 0.4.5
|
33
|
+
|
34
|
+
- Now supports Ruby 2.0
|
35
|
+
- Light#label can be nil
|
36
|
+
- Light#set_power and Light#set_power! now take :on and :off rather than magic number
|
37
|
+
- Use timers 1.x so no compilation is required
|
38
|
+
|
39
|
+
### 0.4.4
|
40
|
+
|
41
|
+
- Fix SO_REUSEPORT issue on older Linux kernels.
|
42
|
+
|
43
|
+
### 0.4.3
|
44
|
+
|
45
|
+
- Initial public release
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source 'https://rubygems.org'
|
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
|
+
|
13
|
+
group :test do
|
14
|
+
gem 'rake', '~> 10.1'
|
15
|
+
gem 'rspec', '~> 3.0.0'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Specify your gem's dependencies in lifx.gemspec
|
19
|
+
gemspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012-2014 LIFX Labs
|
2
|
+
Copyright (c) 2017-2018 Julian Cheal
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# LIFX-lan
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/juliancheal/lifx-lan.svg)](https://travis-ci.org/juliancheal/lifx-lan)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/juliancheal/lifx-lan/badges/gpa.svg)](https://codeclimate.com/github/juliancheal/lifx-lan)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/juliancheal/lifx-lan/badge.svg?branch=master&service=github)](https://coveralls.io/github/juliancheal/lifx-lan?branch=master)
|
6
|
+
[![Dependency Status](https://gemnasium.com/juliancheal/lifx-lan.svg)](https://gemnasium.com/juliancheal/lifx-lan)
|
7
|
+
[![Security](https://hakiri.io/github/juliancheal/lifx-lan/master.svg)](https://hakiri.io/github/juliancheal/lifx-lan/master)
|
8
|
+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
9
|
+
|
10
|
+
|
11
|
+
[![Build history for master branch](https://buildstats.info/travisci/chart/ManageIQ/manageiq?branch=master&buildCount=50)](https://travis-ci.org/juliancheal/lifx-lan/branches)
|
12
|
+
|
13
|
+
LIFX-lan is a fork of the [lifx-gem](https://github.com/LIFX/lifx-gem). This gem is based on the [Lifx LAN](https://api.developer.lifx.com/) protocol.
|
14
|
+
|
15
|
+
Lifx have a Developer Zone over on their community site https://community.lifx.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
+
t.rspec_opts = "--tag ~integration"
|
6
|
+
end
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
task :console do
|
10
|
+
$LOAD_PATH << "lib"
|
11
|
+
require "lifx"
|
12
|
+
require "pry"
|
13
|
+
if ENV['DEBUG']
|
14
|
+
LIFX::LAN::Config.logger.level = Logger::DEBUG
|
15
|
+
end
|
16
|
+
LIFX::LAN::Client.lan.discover! do |c|
|
17
|
+
c.lights.count > 0
|
18
|
+
end
|
19
|
+
LIFX::LAN::Client.lan.pry
|
20
|
+
end
|
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-lan'
|
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::LAN::Transport::UDP.new('0.0.0.0', 56700)
|
30
|
+
light_udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
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::LAN::Transport::UDP.new('0.0.0.0', 56750)
|
38
|
+
peer_udp.add_observer(self, :message_received) do |message: nil, ip: nil, transport: nil|
|
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::LAN::Message::UnsupportedProtocolVersion
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "Listening on 56700 and 56750..."
|
48
|
+
puts "^C to quit."
|
49
|
+
|
50
|
+
sleep
|
@@ -0,0 +1,34 @@
|
|
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 'lifx-lan'
|
7
|
+
|
8
|
+
AUTO_OFF_DELAY = 10
|
9
|
+
|
10
|
+
label = ARGV.first
|
11
|
+
lifx = LIFX::LAN::Client.lan
|
12
|
+
lifx.discover! do
|
13
|
+
label ? lifx.lights.with_label(label) : lifx.lights.first
|
14
|
+
end
|
15
|
+
|
16
|
+
light = label ? lifx.lights.with_label(label) : lifx.lights.first
|
17
|
+
|
18
|
+
puts "#{light} will be automatically turned off after #{AUTO_OFF_DELAY} seconds"
|
19
|
+
|
20
|
+
thr = Thread.new do
|
21
|
+
loop do
|
22
|
+
if light.on? && !(@off_thr && @off_thr.alive?)
|
23
|
+
puts "Light detected on. Turning off in #{AUTO_OFF_DELAY}"
|
24
|
+
@off_thr = Thread.new do
|
25
|
+
sleep AUTO_OFF_DELAY
|
26
|
+
light.turn_off
|
27
|
+
puts "Turning off"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
sleep 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
thr.join
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'lifx-lan'
|
2
|
+
|
3
|
+
lifx = LIFX::LAN::Client.lan
|
4
|
+
lifx.discover!
|
5
|
+
|
6
|
+
light = lifx.lights.first
|
7
|
+
light.set_color LIFX::LAN::Color.white, duration: 0
|
8
|
+
|
9
|
+
sleep(0.5)
|
10
|
+
|
11
|
+
light.set_color LIFX::LAN::Color.red, duration: 0
|
12
|
+
|
13
|
+
sleep(0.5)
|
14
|
+
|
15
|
+
light.set_color LIFX::LAN::Color.white, duration: 0
|
16
|
+
|
17
|
+
sleep(0.5)
|
18
|
+
|
19
|
+
light.set_color LIFX::LAN::Color.red, duration: 0
|
@@ -0,0 +1,69 @@
|
|
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 'lifx-lan'
|
10
|
+
|
11
|
+
COLOURS = {
|
12
|
+
'red' => [0, 1, 1],
|
13
|
+
'yellow' => [50, 1, 1],
|
14
|
+
'green' => [120, 1, 1],
|
15
|
+
'blue' => [220, 1, 1]
|
16
|
+
}
|
17
|
+
|
18
|
+
LIFX::LAN::Config.logger = Logger.new(STDERR)
|
19
|
+
c = LIFX::Client.lan
|
20
|
+
c.discover
|
21
|
+
5.times do
|
22
|
+
c.lights.refresh
|
23
|
+
c.flush
|
24
|
+
sleep 1
|
25
|
+
puts "Lights found: #{c.lights.count}"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def partition(list, partitions)
|
30
|
+
[].tap do |array|
|
31
|
+
list.each_slice((list.count / partitions.to_f).ceil) do |chunk|
|
32
|
+
array << chunk
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
lights = c.lights.to_a
|
38
|
+
mapping = {}
|
39
|
+
|
40
|
+
while lights.count > 1
|
41
|
+
puts "Searching through #{lights.count} lights..."
|
42
|
+
c.lights.set_color(LIFX::LAN::Color.white)
|
43
|
+
partitions = partition(lights, COLOURS.values.count)
|
44
|
+
COLOURS.keys.each_with_index do |color_name, index|
|
45
|
+
color = LIFX::LAN::Color.hsb(*COLOURS[color_name])
|
46
|
+
mapping[color_name] = partitions[index]
|
47
|
+
next if partitions[index].nil?
|
48
|
+
partitions[index].each do |l|
|
49
|
+
l.set_color(color, duration: 0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
puts "Waiting for flush."
|
53
|
+
c.flush
|
54
|
+
puts "What colour is the bulb you're trying to identify? (#{COLOURS.keys.join(', ')})"
|
55
|
+
resp = gets.strip
|
56
|
+
if mapping.has_key?(resp)
|
57
|
+
lights = mapping[resp]
|
58
|
+
else
|
59
|
+
puts "Colour not found. Iterating again"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if lights.count == 1
|
64
|
+
puts "Light identified: #{lights.first}"
|
65
|
+
else
|
66
|
+
puts "No bulbs found."
|
67
|
+
end
|
68
|
+
|
69
|
+
c.flush
|
@@ -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 'travis'
|
7
|
+
require 'lifx-lan'
|
8
|
+
|
9
|
+
lifx = LIFX::LAN::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::LAN::Color.hsb(120, 1, 1)
|
40
|
+
when 'yellow'
|
41
|
+
LIFX::LAN::Color.hsb(60, 1, 1)
|
42
|
+
when 'red'
|
43
|
+
LIFX::LAN::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,30 @@
|
|
1
|
+
# @private
|
2
|
+
module BinData
|
3
|
+
class Bool < Primitive
|
4
|
+
uint8 :_value
|
5
|
+
|
6
|
+
def get
|
7
|
+
(self._value || 0) > 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(value)
|
11
|
+
self._value = value ? 1 : 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class BoolBit1 < Primitive
|
16
|
+
bit1le :_value
|
17
|
+
|
18
|
+
def get
|
19
|
+
(self._value || 0) > 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(value)
|
23
|
+
self._value = value ? 1 : 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def !
|
27
|
+
!get
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|