milight-easybulb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rubocop-https---raw-githubusercontent-com-dvmtn-house-style-master-rubocop-yml +64 -0
- data/.rubocop.yml +2 -0
- data/.semver +6 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +102 -0
- data/Guardfile +15 -0
- data/README.md +37 -0
- data/Rakefile +12 -0
- data/bin/example +74 -0
- data/lib/milight.rb +5 -0
- data/lib/milight/brightness.rb +27 -0
- data/lib/milight/colour.rb +118 -0
- data/lib/milight/commander.rb +22 -0
- data/lib/milight/controller.rb +23 -0
- data/lib/milight/rgbw_all.rb +73 -0
- data/lib/milight/rgbw_group.rb +85 -0
- data/lib/milight/version.rb +3 -0
- data/milight.gemspec +20 -0
- data/spec/milight/brightness_spec.rb +22 -0
- data/spec/milight/colour_spec.rb +84 -0
- data/spec/milight/commander_spec.rb +38 -0
- data/spec/milight/controller_spec.rb +22 -0
- data/spec/milight/rgbw_all_spec.rb +93 -0
- data/spec/milight/rgbw_group_spec.rb +100 -0
- data/spec/spec_helper.rb +17 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4a0fc0787879689b686f3cb0b6a7162eded64ea0
|
4
|
+
data.tar.gz: 1a587697381987b7db82e693a9ba5aad58a5a3e1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 06e31da3238529ee47fb1a702bed3e91f20477b46727d1e1d43cd4a2ca448526b0fc7f74e7c0b9a99dfd07bec0b2b6657558871f71e0cbd2a1ca867b81296dd3
|
7
|
+
data.tar.gz: 713aede83575d8a77004933e6ecc2abac29f7e26b261e5edd7de1d85dcba2b44a844d12ad7c835aa94f0753ce320f5ca63b7173cadb50226a48ac35bb5037eab
|
data/.gitignore
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
AllCops:
|
2
|
+
Include:
|
3
|
+
- '**/Rakefile'
|
4
|
+
- '**/config.ru'
|
5
|
+
Exclude:
|
6
|
+
- 'db/**/*'
|
7
|
+
- 'config/**/*'
|
8
|
+
- 'script/**/*'
|
9
|
+
- 'tmp/**/*'
|
10
|
+
- 'vendor/**/*'
|
11
|
+
- 'bin/**/*'
|
12
|
+
- 'log/**/*'
|
13
|
+
|
14
|
+
Rails:
|
15
|
+
Enabled: true
|
16
|
+
|
17
|
+
Style/AlignHash:
|
18
|
+
EnforcedHashRocketStyle: table
|
19
|
+
EnforcedColonStyle: table
|
20
|
+
EnforcedLastArgumentHashStyle: ignore_implicit
|
21
|
+
SupportedLastArgumentHashStyles: ignore_implicit
|
22
|
+
|
23
|
+
Style/DefWithParentheses:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/Documentation:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/MethodDefParentheses:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Style/Encoding:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Style/PercentLiteralDelimiters:
|
36
|
+
PreferredDelimiters:
|
37
|
+
'%': ()
|
38
|
+
'%i': '[]'
|
39
|
+
'%q': '[]'
|
40
|
+
'%Q': '[]'
|
41
|
+
'%r': '[]'
|
42
|
+
'%s': '[]'
|
43
|
+
'%w': '[]'
|
44
|
+
'%W': '[]'
|
45
|
+
'%x': '[]'
|
46
|
+
|
47
|
+
Style/PredicateName:
|
48
|
+
NamePrefixBlacklist:
|
49
|
+
- is_
|
50
|
+
- has_
|
51
|
+
- have_
|
52
|
+
- the_
|
53
|
+
- a_
|
54
|
+
- should_
|
55
|
+
|
56
|
+
Style/SignalException:
|
57
|
+
EnforcedStyle: only_raise
|
58
|
+
|
59
|
+
Style/EmptyLinesAroundClassBody:
|
60
|
+
EnforcedStyle: empty_lines
|
61
|
+
|
62
|
+
Metrics/LineLength:
|
63
|
+
Max: 140
|
64
|
+
AllowURI: true
|
data/.rubocop.yml
ADDED
data/.semver
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
ast (2.1.0)
|
5
|
+
astrolabe (1.3.1)
|
6
|
+
parser (~> 2.2)
|
7
|
+
byebug (4.0.5)
|
8
|
+
columnize (= 0.9.0)
|
9
|
+
coderay (1.1.1)
|
10
|
+
columnize (0.9.0)
|
11
|
+
diff-lcs (1.2.5)
|
12
|
+
docile (1.1.5)
|
13
|
+
ffi (1.9.10)
|
14
|
+
formatador (0.2.5)
|
15
|
+
guard (2.13.0)
|
16
|
+
formatador (>= 0.2.4)
|
17
|
+
listen (>= 2.7, <= 4.0)
|
18
|
+
lumberjack (~> 1.0)
|
19
|
+
nenv (~> 0.1)
|
20
|
+
notiffany (~> 0.0)
|
21
|
+
pry (>= 0.9.12)
|
22
|
+
shellany (~> 0.0)
|
23
|
+
thor (>= 0.18.1)
|
24
|
+
guard-compat (1.2.1)
|
25
|
+
guard-rspec (4.6.4)
|
26
|
+
guard (~> 2.1)
|
27
|
+
guard-compat (~> 1.1)
|
28
|
+
rspec (>= 2.99.0, < 4.0)
|
29
|
+
listen (3.0.6)
|
30
|
+
rb-fsevent (>= 0.9.3)
|
31
|
+
rb-inotify (>= 0.9.7)
|
32
|
+
lumberjack (1.0.10)
|
33
|
+
method_source (0.8.2)
|
34
|
+
multi_json (1.11.0)
|
35
|
+
nenv (0.3.0)
|
36
|
+
notiffany (0.0.8)
|
37
|
+
nenv (~> 0.1)
|
38
|
+
shellany (~> 0.0)
|
39
|
+
parser (2.2.3.0)
|
40
|
+
ast (>= 1.1, < 3.0)
|
41
|
+
powerpack (0.1.1)
|
42
|
+
pry (0.10.3)
|
43
|
+
coderay (~> 1.1.0)
|
44
|
+
method_source (~> 0.8.1)
|
45
|
+
slop (~> 3.4)
|
46
|
+
pry-byebug (3.1.0)
|
47
|
+
byebug (~> 4.0)
|
48
|
+
pry (~> 0.10)
|
49
|
+
rainbow (2.0.0)
|
50
|
+
rake (10.5.0)
|
51
|
+
rake-n-bake (1.4.0)
|
52
|
+
rake (~> 10)
|
53
|
+
term-ansicolor (~> 1.3)
|
54
|
+
rb-fsevent (0.9.7)
|
55
|
+
rb-inotify (0.9.7)
|
56
|
+
ffi (>= 0.5.0)
|
57
|
+
rspec (3.4.0)
|
58
|
+
rspec-core (~> 3.4.0)
|
59
|
+
rspec-expectations (~> 3.4.0)
|
60
|
+
rspec-mocks (~> 3.4.0)
|
61
|
+
rspec-core (3.4.4)
|
62
|
+
rspec-support (~> 3.4.0)
|
63
|
+
rspec-expectations (3.4.0)
|
64
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
65
|
+
rspec-support (~> 3.4.0)
|
66
|
+
rspec-mocks (3.4.1)
|
67
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
68
|
+
rspec-support (~> 3.4.0)
|
69
|
+
rspec-support (3.4.1)
|
70
|
+
rubocop (0.35.1)
|
71
|
+
astrolabe (~> 1.3)
|
72
|
+
parser (>= 2.2.3.0, < 3.0)
|
73
|
+
powerpack (~> 0.1)
|
74
|
+
rainbow (>= 1.99.1, < 3.0)
|
75
|
+
ruby-progressbar (~> 1.7)
|
76
|
+
tins (<= 1.6.0)
|
77
|
+
ruby-progressbar (1.7.5)
|
78
|
+
shellany (0.0.1)
|
79
|
+
simplecov (0.9.2)
|
80
|
+
docile (~> 1.1.0)
|
81
|
+
multi_json (~> 1.0)
|
82
|
+
simplecov-html (~> 0.9.0)
|
83
|
+
simplecov-html (0.9.0)
|
84
|
+
slop (3.6.0)
|
85
|
+
term-ansicolor (1.3.2)
|
86
|
+
tins (~> 1.0)
|
87
|
+
thor (0.19.1)
|
88
|
+
tins (1.6.0)
|
89
|
+
|
90
|
+
PLATFORMS
|
91
|
+
ruby
|
92
|
+
|
93
|
+
DEPENDENCIES
|
94
|
+
guard-rspec
|
95
|
+
pry-byebug
|
96
|
+
rake-n-bake
|
97
|
+
rspec
|
98
|
+
rubocop
|
99
|
+
simplecov
|
100
|
+
|
101
|
+
BUNDLED WITH
|
102
|
+
1.12.3
|
data/Guardfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
guard :rspec, cmd: "bundle exec rspec --color" do
|
2
|
+
clearing :on
|
3
|
+
|
4
|
+
require "guard/rspec/dsl"
|
5
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
15
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Milight
|
2
|
+
=======
|
3
|
+
A gem for controlling Milight Wifi bulbs
|
4
|
+
|
5
|
+
Usage
|
6
|
+
-----
|
7
|
+
1. Require the gem
|
8
|
+
2. Initialise a new controller with the IP address of your controller
|
9
|
+
3. Send commands to all groups, or just one
|
10
|
+
|
11
|
+
For example, connecting to a controller on `192.168.0.10`, turning on group 1 and setting it to a teal colour at 50% brightness:
|
12
|
+
|
13
|
+
```Ruby
|
14
|
+
require 'milight'
|
15
|
+
|
16
|
+
# Initialise a controller
|
17
|
+
lights = Milight::Controller.new '192.168.0.10'
|
18
|
+
|
19
|
+
# Turn a group of lights on and set their colour
|
20
|
+
lights.group(1).on
|
21
|
+
lights.group(1).colour '#ff00ff'
|
22
|
+
|
23
|
+
# Or chain the commands to one group
|
24
|
+
lights.group(1).on.hue('#f00').brightness(50)
|
25
|
+
|
26
|
+
# Go to bed after happily messing with lights!
|
27
|
+
lights.all.off
|
28
|
+
```
|
29
|
+
|
30
|
+
Take a look at [the example script](bin/example) for an example script using the gem.
|
31
|
+
|
32
|
+
Things you should know
|
33
|
+
----------------------
|
34
|
+
* Colours can be set using the `colour` method via a HEX colour code (for example, `#f00` or `#a0f060`)
|
35
|
+
* Brightness is given as a percentage (0-100)
|
36
|
+
* Hue can also be set using a hex code (but doesn't change the brightness)
|
37
|
+
* Everything is sent over UDP, as per the Milight spec. This means that some commands will get 'lost', especially if you send many in quick succession. The gem tries to account for this by adding very short delays between commands but it isn't guaranteed.
|
data/Rakefile
ADDED
data/bin/example
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
require 'milight'
|
5
|
+
|
6
|
+
if ARGV.length != 1
|
7
|
+
puts "Usage: #{$PROGRAM_NAME} <ip_address>"
|
8
|
+
puts ' For example:', " #{$PROGRAM_NAME} 192.168.0.10"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
ip = ARGV.shift
|
13
|
+
lights = Milight::Controller.new ip
|
14
|
+
|
15
|
+
red = '#f00'
|
16
|
+
green = '#0f0'
|
17
|
+
blue = '#00f'
|
18
|
+
yellow = '#ff0'
|
19
|
+
|
20
|
+
# Control all lights on all channels
|
21
|
+
lights.all.on
|
22
|
+
sleep 1
|
23
|
+
lights.all.colour(red)
|
24
|
+
sleep 1
|
25
|
+
lights.all.colour(green)
|
26
|
+
sleep 1
|
27
|
+
lights.all.colour(blue)
|
28
|
+
sleep 1
|
29
|
+
lights.all.off
|
30
|
+
sleep 1
|
31
|
+
|
32
|
+
# Control each channel/group of lights
|
33
|
+
lights.group(1).colour(red)
|
34
|
+
lights.group(2).colour(green)
|
35
|
+
lights.group(3).colour(yellow)
|
36
|
+
lights.group(4).colour(blue)
|
37
|
+
sleep 1
|
38
|
+
|
39
|
+
# Assign the group to a variable for easier identification
|
40
|
+
bedroom = lights.group(1)
|
41
|
+
kitchen = lights.group(2)
|
42
|
+
|
43
|
+
bedroom.colour('#2F22ff')
|
44
|
+
kitchen.colour('#FFF')
|
45
|
+
|
46
|
+
# Set either the hue or brightness without affecting the other
|
47
|
+
lights.group(3).hue(yellow)
|
48
|
+
lights.group(4).brightness(25) # given as a percentage
|
49
|
+
sleep 1
|
50
|
+
lights.all.off
|
51
|
+
sleep 3
|
52
|
+
|
53
|
+
# Lets simulate the sunrise!
|
54
|
+
all = lights.all
|
55
|
+
SUNRISE = %w[ #003 #026 #02A #23A #F50 #F92 #FC0 #FF4 #FFF]
|
56
|
+
|
57
|
+
puts 'Sunrise!'
|
58
|
+
SUNRISE.each do |colour|
|
59
|
+
all.colour(colour)
|
60
|
+
print '.'
|
61
|
+
sleep 2
|
62
|
+
end
|
63
|
+
|
64
|
+
puts 'Sunset!'
|
65
|
+
SUNRISE.reverse_each do |colour|
|
66
|
+
all.colour(colour)
|
67
|
+
print '.'
|
68
|
+
sleep 2
|
69
|
+
end
|
70
|
+
|
71
|
+
sleep 1
|
72
|
+
|
73
|
+
# ...and set everything back to white when we're done!
|
74
|
+
lights.all.white
|
data/lib/milight.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Milight
|
2
|
+
class Brightness
|
3
|
+
|
4
|
+
MIN = 2
|
5
|
+
MAX = 27
|
6
|
+
|
7
|
+
def initialize percent
|
8
|
+
raise invalid_brightness unless valid_brightness?(percent)
|
9
|
+
@percent = percent
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_milight_brightness
|
13
|
+
MIN + ((MAX - MIN) * @percent / 100).round
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def invalid_brightness
|
19
|
+
ArgumentError.new 'Brightness must be given as a percentage (0 - 100)'
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_brightness? percentage
|
23
|
+
percentage >= 0 && percentage <= 100
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require_relative 'brightness'
|
2
|
+
|
3
|
+
module Milight
|
4
|
+
class Colour
|
5
|
+
|
6
|
+
attr_reader :hue, :saturation, :luminosity, :red, :green, :blue
|
7
|
+
|
8
|
+
HUE_OFFSET = 176
|
9
|
+
VALID_HEX_REGEX = /^#?([0-9a-f]{3}){,2}$/i
|
10
|
+
|
11
|
+
def initialize(value)
|
12
|
+
case value
|
13
|
+
when String then from_hex(value)
|
14
|
+
when Array then from_rgb(*value)
|
15
|
+
else
|
16
|
+
raise invalid_colour_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hsl
|
21
|
+
[@hue, @saturation, @luminosity]
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_rgb
|
25
|
+
[@red, @green, @blue]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_milight_colour
|
29
|
+
((256 + HUE_OFFSET - (hue / 360.0 * 255.0)) % 256).to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_milight_brightness
|
33
|
+
percent = [brightness_for_saturation, 100].min
|
34
|
+
Milight::Brightness.new(percent).to_milight_brightness
|
35
|
+
end
|
36
|
+
|
37
|
+
def greyscale?
|
38
|
+
@red == @green && @red == @blue
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def from_rgb(r, g, b)
|
44
|
+
@red = r
|
45
|
+
@green = g
|
46
|
+
@blue = b
|
47
|
+
@hue, @saturation, @luminosity = rgb_to_hsl(r, g, b)
|
48
|
+
end
|
49
|
+
|
50
|
+
def from_hex(hex)
|
51
|
+
raise invalid_hex_colour_error unless valid_hex_colour? hex
|
52
|
+
r, g, b = hex_to_rgb(hex)
|
53
|
+
from_rgb(r, g, b)
|
54
|
+
end
|
55
|
+
|
56
|
+
def brightness_for_saturation
|
57
|
+
r, g, b = *to_rgb
|
58
|
+
(Math.sqrt(r * r + g * g + b * b) / 2.55).round
|
59
|
+
end
|
60
|
+
|
61
|
+
def hex_to_rgb(hex)
|
62
|
+
hex.sub('#', '')
|
63
|
+
.chars
|
64
|
+
.each_slice(hex.length / 3)
|
65
|
+
.map { |val| val.length == 1 ? val * 2 : val }
|
66
|
+
.map(&:join)
|
67
|
+
.map { |h| h.to_i(16) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def rgb_to_hsl(r1, g1, b1)
|
71
|
+
r, g, b = [r1, g1, b1].map { |c| c / 255.0 }
|
72
|
+
h = rgb_to_hue(r, g, b)
|
73
|
+
l = rgb_to_luminosity(r, g, b)
|
74
|
+
s = rgbl_to_saturation(r, g, b, l)
|
75
|
+
[h, s, l]
|
76
|
+
end
|
77
|
+
|
78
|
+
def rgb_to_hue(r, g, b)
|
79
|
+
return 0 if greyscale?
|
80
|
+
delta = delta(r, g, b)
|
81
|
+
offset = case [r, g, b].max
|
82
|
+
when r then ((g - b) / delta)
|
83
|
+
when g then ((b - r) / delta) + 2
|
84
|
+
when b then ((r - g) / delta) + 4
|
85
|
+
end
|
86
|
+
60 * offset % 360
|
87
|
+
end
|
88
|
+
|
89
|
+
def rgb_to_luminosity(r, g, b)
|
90
|
+
[r, g, b].minmax.inject(:+) / 2
|
91
|
+
end
|
92
|
+
|
93
|
+
def rgbl_to_saturation(r, g, b, l)
|
94
|
+
return 0 if greyscale?
|
95
|
+
(delta(r, g, b) / 1 - (2 * l - 1)).abs
|
96
|
+
end
|
97
|
+
|
98
|
+
def delta(r, g, b)
|
99
|
+
[r, g, b].minmax.reverse.inject(:-)
|
100
|
+
end
|
101
|
+
|
102
|
+
def valid_hex_colour?(value)
|
103
|
+
value =~ VALID_HEX_REGEX
|
104
|
+
end
|
105
|
+
|
106
|
+
def invalid_colour_error
|
107
|
+
ArgumentError.new(
|
108
|
+
"Colours must be given as with a hex colour string ( #{self.class}.new('#11A401') )" \
|
109
|
+
" or a RGB array ( #{self.class}.new([r,g,b]) )"
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
def invalid_hex_colour_error
|
114
|
+
ArgumentError.new('Hex colours codes must be 3 or 6 0-9 or a-f characters')
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Milight
|
4
|
+
class Commander
|
5
|
+
|
6
|
+
def initialize(ip_address, port = 8899)
|
7
|
+
@ip_address = ip_address
|
8
|
+
@port = port
|
9
|
+
end
|
10
|
+
|
11
|
+
def send_command(cmd, arg1 = 0x00)
|
12
|
+
socket = UDPSocket.new
|
13
|
+
socket.send [cmd, arg1, 0x55].pack('C*'), 0, @ip_address, @port
|
14
|
+
socket.close
|
15
|
+
end
|
16
|
+
|
17
|
+
def command_delay
|
18
|
+
sleep 0.1
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'milight/commander'
|
2
|
+
require 'milight/rgbw_group'
|
3
|
+
require 'milight/rgbw_all'
|
4
|
+
|
5
|
+
module Milight
|
6
|
+
class Controller
|
7
|
+
|
8
|
+
attr_reader :commander
|
9
|
+
|
10
|
+
def initialize(ip_address, port = 8899)
|
11
|
+
@commander = Milight::Commander.new ip_address, port
|
12
|
+
end
|
13
|
+
|
14
|
+
def all
|
15
|
+
Milight::RgbwAll.new(@commander)
|
16
|
+
end
|
17
|
+
|
18
|
+
def group(channel)
|
19
|
+
Milight::RgbwGroup.new(@commander, channel)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'milight/colour'
|
2
|
+
|
3
|
+
module Milight
|
4
|
+
class RgbwAll
|
5
|
+
|
6
|
+
ALL_OFF = 0x41
|
7
|
+
ALL_ON = 0x42
|
8
|
+
WHITE = 0xC2
|
9
|
+
COLOUR = 0x40
|
10
|
+
BRIGHTNESS = 0x4E
|
11
|
+
|
12
|
+
def initialize(commander, colour_helper: Milight::Colour)
|
13
|
+
@commander = commander
|
14
|
+
@colour_helper = colour_helper
|
15
|
+
end
|
16
|
+
|
17
|
+
def on
|
18
|
+
@commander.send_command ALL_ON
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def off
|
23
|
+
@commander.send_command ALL_OFF
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def white
|
28
|
+
send_white_cmd
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def hue(hue)
|
33
|
+
colour = @colour_helper.new(hue)
|
34
|
+
send_colour_cmd colour
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def brightness(value)
|
39
|
+
brightness = Milight::Brightness.new(value)
|
40
|
+
send_brightness_cmd brightness
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def colour(colour)
|
45
|
+
colour_value = @colour_helper.new(colour)
|
46
|
+
colour_value.greyscale? ? send_white_cmd : send_colour_cmd(colour_value)
|
47
|
+
send_brightness_cmd colour_value
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def send_white_cmd
|
54
|
+
select
|
55
|
+
@commander.send_command WHITE
|
56
|
+
end
|
57
|
+
|
58
|
+
def send_colour_cmd colour
|
59
|
+
select
|
60
|
+
@commander.send_command COLOUR, colour.to_milight_colour
|
61
|
+
end
|
62
|
+
|
63
|
+
def send_brightness_cmd brightness
|
64
|
+
select
|
65
|
+
@commander.send_command BRIGHTNESS, brightness.to_milight_brightness
|
66
|
+
end
|
67
|
+
|
68
|
+
def select
|
69
|
+
on
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'milight/colour'
|
2
|
+
|
3
|
+
module Milight
|
4
|
+
class RgbwGroup
|
5
|
+
|
6
|
+
attr_reader :commander
|
7
|
+
|
8
|
+
GROUP_ON = [0x45, 0x47, 0x49, 0x4B]
|
9
|
+
GROUP_OFF = [0x46, 0x48, 0x4A, 0x4C]
|
10
|
+
GROUP_WHITE = [0xC5, 0xC7, 0xC9, 0xCB]
|
11
|
+
|
12
|
+
BRIGHTNESS = 0x4E
|
13
|
+
COLOUR = 0x40
|
14
|
+
|
15
|
+
def initialize(commander, group, colour_helper: Milight::Colour)
|
16
|
+
raise invalid_group_error unless valid_group? group
|
17
|
+
@index = group - 1
|
18
|
+
@commander = commander
|
19
|
+
@colour_helper = colour_helper
|
20
|
+
end
|
21
|
+
|
22
|
+
def on
|
23
|
+
@commander.send_command GROUP_ON[@index]
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def off
|
28
|
+
@commander.send_command GROUP_OFF[@index]
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def white
|
33
|
+
send_white_cmd
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def hue(hue)
|
38
|
+
colour = @colour_helper.new(hue)
|
39
|
+
send_colour_cmd colour
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def brightness(value)
|
44
|
+
brightness = Milight::Brightness.new(value)
|
45
|
+
send_brightness_cmd brightness
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def colour(colour)
|
50
|
+
colour_value = @colour_helper.new(colour)
|
51
|
+
colour_value.greyscale? ? send_white_cmd : send_colour_cmd(colour_value)
|
52
|
+
send_brightness_cmd colour_value
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def send_white_cmd
|
59
|
+
@commander.send_command GROUP_WHITE[@index]
|
60
|
+
end
|
61
|
+
|
62
|
+
def send_colour_cmd colour
|
63
|
+
select
|
64
|
+
@commander.send_command COLOUR, colour.to_milight_colour
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_brightness_cmd colour
|
68
|
+
select
|
69
|
+
@commander.send_command BRIGHTNESS, colour.to_milight_brightness
|
70
|
+
end
|
71
|
+
|
72
|
+
def select
|
73
|
+
on
|
74
|
+
end
|
75
|
+
|
76
|
+
def invalid_group_error
|
77
|
+
ArgumentError.new('Group must be between 1 and 4')
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid_group?(value)
|
81
|
+
value.between?(1, 4)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
data/milight.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'milight/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'milight-easybulb'
|
8
|
+
gem.version = Milight::VERSION
|
9
|
+
gem.authors = ['Adam Whittingham']
|
10
|
+
gem.email = 'adam.whittingham@gmail.com'
|
11
|
+
|
12
|
+
gem.date = '2014-05-15'
|
13
|
+
gem.summary = 'A ruby interface for Milight and Easybulb LED lights'
|
14
|
+
gem.description = 'A ruby interface for Milight and Easybulb LED lights'
|
15
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
|
18
|
+
gem.homepage = 'https://github.com/AdamWhittingham/milight'
|
19
|
+
gem.license = 'MIT'
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require production_code
|
3
|
+
|
4
|
+
describe Milight::Brightness do
|
5
|
+
describe '#to_milight_brightness' do
|
6
|
+
{
|
7
|
+
100 => 27,
|
8
|
+
75 => 20,
|
9
|
+
50 => 14,
|
10
|
+
25 => 8,
|
11
|
+
0 => 2
|
12
|
+
}.each do |percent, milight|
|
13
|
+
it "converts #{percent}% to the milight code #{milight}" do
|
14
|
+
expect(described_class.new(percent).to_milight_brightness).to eq milight
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an exception for invalid numbers' do
|
19
|
+
expect { described_class.new(101) }.to raise_error ArgumentError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
require production_code
|
4
|
+
|
5
|
+
describe Milight::Colour do
|
6
|
+
let(:red) { OpenStruct.new(milight: 176, rgb: [255, 0, 0], hsl: [0.0, 1.0, 0.5]) }
|
7
|
+
let(:green) { OpenStruct.new(milight: 85, rgb: [0, 255, 0], hsl: [120.0, 1.0, 0.5]) }
|
8
|
+
let(:blue) { OpenStruct.new(milight: 0, rgb: [0, 0, 255], hsl: [240.0, 1.0, 0.5]) }
|
9
|
+
|
10
|
+
describe '#new' do
|
11
|
+
it 'takes valid 6-digit hex codes' do
|
12
|
+
expect(described_class.new('#FF0000').to_hsl).to eq red.hsl
|
13
|
+
expect(described_class.new('#00FF00').to_hsl).to eq green.hsl
|
14
|
+
expect(described_class.new('#0000FF').to_hsl).to eq blue.hsl
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'takes valid 3-digit hex hsls' do
|
18
|
+
expect(described_class.new('#0F0').to_hsl).to eq green.hsl
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'takes valid hex hsls without a leading hash' do
|
22
|
+
expect(described_class.new('0F0').to_hsl).to eq green.hsl
|
23
|
+
expect(described_class.new('00ff00').to_hsl).to eq green.hsl
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'raises an arguement error for invalid hex codes' do
|
27
|
+
expect { described_class.new('#00112233') }.to raise_error ArgumentError
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises an arguement error for invalid colour codes' do
|
31
|
+
expect { described_class.new(:hello) }.to raise_error ArgumentError
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'takes an RGB array' do
|
35
|
+
expect(described_class.new([255, 0, 0]).to_hsl).to eq red.hsl
|
36
|
+
expect(described_class.new([0, 255, 0]).to_hsl).to eq green.hsl
|
37
|
+
expect(described_class.new([0, 0, 255]).to_hsl).to eq blue.hsl
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#to_milight_colour' do
|
42
|
+
it 'converts hex codes to milight colour codes' do
|
43
|
+
expect(described_class.new('#FF0000').to_milight_colour).to eq red.milight
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'converts an array of RGB values to milight colour codes' do
|
47
|
+
expect(described_class.new([255, 0, 0]).to_milight_colour).to eq red.milight
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#to_milight_brightness' do
|
52
|
+
it 'converts hex codes to milight brightness codes' do
|
53
|
+
expect(described_class.new('#00FF00').to_milight_brightness).to eq 27
|
54
|
+
expect(described_class.new('#008800').to_milight_brightness).to eq 15
|
55
|
+
expect(described_class.new('#000000').to_milight_brightness).to eq 2
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#to_rgb' do
|
60
|
+
it 'outputs an array of RGB' do
|
61
|
+
expect(described_class.new('#FF0000').to_rgb).to eq red.rgb
|
62
|
+
expect(described_class.new('#00FF00').to_rgb).to eq green.rgb
|
63
|
+
expect(described_class.new('#0000FF').to_rgb).to eq blue.rgb
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#to_hsl' do
|
68
|
+
it 'outputs an array of HSL' do
|
69
|
+
expect(described_class.new('#FF0000').to_hsl).to eq red.hsl
|
70
|
+
expect(described_class.new('#00FF00').to_hsl).to eq green.hsl
|
71
|
+
expect(described_class.new('#0000FF').to_hsl).to eq blue.hsl
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#greyscale?' do
|
76
|
+
it 'returns true for greyscale colours' do
|
77
|
+
expect(described_class.new('#aaa').greyscale?).to eq true
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns false for non-greyscale colours' do
|
81
|
+
expect(described_class.new('#f0f').greyscale?).to eq false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require production_code
|
3
|
+
|
4
|
+
describe Milight::Commander do
|
5
|
+
let(:socket) { instance_double(UDPSocket) }
|
6
|
+
let(:ip_address) { '127.0.0.1' }
|
7
|
+
let(:port) { 8080 }
|
8
|
+
|
9
|
+
subject { described_class.new ip_address, port }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(UDPSocket).to receive(:new).and_return(socket)
|
13
|
+
allow(socket).to receive(:close)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#send_command' do
|
17
|
+
it 'sends the given bytes' do
|
18
|
+
payload = [0x42, 0x00, 0x55].pack('C*')
|
19
|
+
expect(socket).to receive(:send).with(payload, 0, ip_address, port)
|
20
|
+
subject.send_command 0x42
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'sends the given argument if one is given' do
|
24
|
+
payload = [0x42, 0x01, 0x55].pack('C*')
|
25
|
+
expect(socket).to receive(:send).with(payload, 0, ip_address, port)
|
26
|
+
subject.send_command 0x42, 0x01
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#command_delay' do
|
31
|
+
it 'pauses for 100 milliseconds' do
|
32
|
+
before = Time.now
|
33
|
+
subject.command_delay
|
34
|
+
after = Time.now
|
35
|
+
expect(after - before).to be > 0.1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require production_code
|
3
|
+
|
4
|
+
describe Milight::Controller do
|
5
|
+
let(:commander) { instance_double(Milight::Commander) }
|
6
|
+
|
7
|
+
subject { described_class.new '127.0.0.1', 8080 }
|
8
|
+
|
9
|
+
before { allow(Milight::Commander).to receive(:new).and_return(commander) }
|
10
|
+
|
11
|
+
describe '#all' do
|
12
|
+
it 'returns an RGBW all object' do
|
13
|
+
expect(subject.all).to be_a Milight::RgbwAll
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#group' do
|
18
|
+
it 'returns a RGBW group object' do
|
19
|
+
expect(subject.group(1)).to be_a Milight::RgbwGroup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'milight/commander'
|
3
|
+
require production_code
|
4
|
+
|
5
|
+
describe Milight::RgbwAll do
|
6
|
+
let(:commander) { double Milight::Commander, send_command: true, command_delay: true }
|
7
|
+
|
8
|
+
subject { described_class.new commander }
|
9
|
+
|
10
|
+
describe '#on' do
|
11
|
+
it 'sends the ON packet for the given group' do
|
12
|
+
expect(commander).to receive(:send_command).with(0x42)
|
13
|
+
subject.on
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'is chainable' do
|
17
|
+
expect(subject.on).to eq subject
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#off' do
|
22
|
+
it 'sends the OFF packet for the given group' do
|
23
|
+
expect(commander).to receive(:send_command).with(0x41)
|
24
|
+
subject.off
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is chainable' do
|
28
|
+
expect(subject.off).to eq subject
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#white' do
|
33
|
+
it 'sends the WHITE packet for the given group' do
|
34
|
+
expect(commander).to receive(:send_command).with(0xC2)
|
35
|
+
subject.white
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'is chainable' do
|
39
|
+
expect(subject.white).to eq subject
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#hue' do
|
44
|
+
it 'sends a COLOUR packet' do
|
45
|
+
expect(commander).to receive(:send_command).with(0x40, 176)
|
46
|
+
subject.hue '#f00'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'is chainable' do
|
50
|
+
expect(subject.hue('#f00')).to eq subject
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#brightness' do
|
55
|
+
it 'sends the BRIGHTNESS packet' do
|
56
|
+
expect(commander).to receive(:send_command).with(0x4E, 4)
|
57
|
+
subject.brightness 10
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'is chainable' do
|
61
|
+
expect(subject.brightness(10)).to eq subject
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#colour' do
|
66
|
+
before do
|
67
|
+
allow(commander).to receive(:send_command).with(0x40, anything)
|
68
|
+
allow(commander).to receive(:send_command).with(0x4E, anything)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'sets the hue' do
|
72
|
+
expect(commander).to receive(:send_command).with(0x40, 219)
|
73
|
+
subject.colour '#880088'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'sets the brightness' do
|
77
|
+
expect(commander).to receive(:send_command).with(0x4E, 20)
|
78
|
+
subject.colour '#880088'
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'for a greyscale colour' do
|
82
|
+
it 'sets the light to white' do
|
83
|
+
expect(commander).to receive(:send_command).with(0xC2)
|
84
|
+
subject.colour '#aaa'
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'sets the brightness' do
|
88
|
+
expect(commander).to receive(:send_command).with(0x4E, 25)
|
89
|
+
subject.colour '#888'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'milight/commander'
|
3
|
+
require production_code
|
4
|
+
|
5
|
+
describe Milight::RgbwGroup do
|
6
|
+
let(:commander) { double Milight::Commander, send_command: true, command_delay: true }
|
7
|
+
|
8
|
+
subject { described_class.new commander, 1 }
|
9
|
+
|
10
|
+
describe '.new' do
|
11
|
+
it 'raises an ArguementError for groups not in the valid range' do
|
12
|
+
expect { described_class.new commander, 5 }.to raise_error ArgumentError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#on' do
|
17
|
+
it 'sends the ON packet for the given group' do
|
18
|
+
expect(commander).to receive(:send_command).with(0x45)
|
19
|
+
subject.on
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is chainable' do
|
23
|
+
expect(subject.on).to eq subject
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#off' do
|
28
|
+
it 'sends the OFF packet for the given group' do
|
29
|
+
expect(commander).to receive(:send_command).with(0x46)
|
30
|
+
subject.off
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'is chainable' do
|
34
|
+
expect(subject.off).to eq subject
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#white' do
|
39
|
+
it 'sends the WHITE packet for the given group' do
|
40
|
+
expect(commander).to receive(:send_command).with(0xC5)
|
41
|
+
subject.white
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is chainable' do
|
45
|
+
expect(subject.white).to eq subject
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#hue' do
|
50
|
+
it 'sends a COLOUR packet for the given group' do
|
51
|
+
expect(commander).to receive(:send_command).with(0x40, 176)
|
52
|
+
subject.hue '#f00'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'is chainable' do
|
56
|
+
expect(subject.hue('#f00')).to eq subject
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#brightness' do
|
61
|
+
it 'sends the BRIGHTNESS packet for the given group' do
|
62
|
+
expect(commander).to receive(:send_command).with(0x4E, 4)
|
63
|
+
subject.brightness 10
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'is chainable' do
|
67
|
+
expect(subject.brightness(10)).to eq subject
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#colour' do
|
72
|
+
before do
|
73
|
+
allow(commander).to receive(:send_command).with(0x40, anything)
|
74
|
+
allow(commander).to receive(:send_command).with(0x4E, anything)
|
75
|
+
allow(commander).to receive(:send_command).with(0x45)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets the hue' do
|
79
|
+
expect(commander).to receive(:send_command).with(0x40, 219)
|
80
|
+
subject.colour '#880088'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'sets the brightness' do
|
84
|
+
expect(commander).to receive(:send_command).with(0x4E, 20)
|
85
|
+
subject.colour '#880088'
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'for a greyscale colour' do
|
89
|
+
it 'sets the light to white' do
|
90
|
+
expect(commander).to receive(:send_command).with(0xC5)
|
91
|
+
subject.colour '#aaa'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'sets the brightness' do
|
95
|
+
expect(commander).to receive(:send_command).with(0x4E, 25)
|
96
|
+
subject.colour '#888'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.coverage_dir 'log/coverage/spec'
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
def production_code
|
7
|
+
spec = caller[0][/spec.+\.rb/]
|
8
|
+
'./' + spec.gsub('_spec', '').gsub(/spec/, 'lib')
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.default_formatter = 'doc' if config.files_to_run.one?
|
13
|
+
|
14
|
+
config.mock_with :rspec do |mocks|
|
15
|
+
mocks.verify_partial_doubles = true
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: milight-easybulb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Whittingham
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A ruby interface for Milight and Easybulb LED lights
|
14
|
+
email: adam.whittingham@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- ".gitignore"
|
20
|
+
- ".rubocop-https---raw-githubusercontent-com-dvmtn-house-style-master-rubocop-yml"
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- ".semver"
|
23
|
+
- Gemfile
|
24
|
+
- Gemfile.lock
|
25
|
+
- Guardfile
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/example
|
29
|
+
- lib/milight.rb
|
30
|
+
- lib/milight/brightness.rb
|
31
|
+
- lib/milight/colour.rb
|
32
|
+
- lib/milight/commander.rb
|
33
|
+
- lib/milight/controller.rb
|
34
|
+
- lib/milight/rgbw_all.rb
|
35
|
+
- lib/milight/rgbw_group.rb
|
36
|
+
- lib/milight/version.rb
|
37
|
+
- milight.gemspec
|
38
|
+
- spec/milight/brightness_spec.rb
|
39
|
+
- spec/milight/colour_spec.rb
|
40
|
+
- spec/milight/commander_spec.rb
|
41
|
+
- spec/milight/controller_spec.rb
|
42
|
+
- spec/milight/rgbw_all_spec.rb
|
43
|
+
- spec/milight/rgbw_group_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
homepage: https://github.com/AdamWhittingham/milight
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.4.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: A ruby interface for Milight and Easybulb LED lights
|
69
|
+
test_files: []
|