milight-easybulb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,4 @@
1
+ log
2
+ tmp
3
+ *.gem
4
+ notes.md
@@ -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
@@ -0,0 +1,2 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/dvmtn/house_style/master/rubocop.yml
data/.semver ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 0
5
+ :special: ''
6
+ :metadata: ''
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development do
4
+ gem 'guard-rspec'
5
+ gem 'rake-n-bake'
6
+ end
7
+
8
+ group :development, :test do
9
+ gem 'rspec'
10
+ gem 'simplecov'
11
+ gem 'rubocop'
12
+ gem 'pry-byebug'
13
+ end
@@ -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
@@ -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
@@ -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.
@@ -0,0 +1,12 @@
1
+ require 'rake/clean'
2
+ require 'rake-n-bake'
3
+ require 'bundler/gem_tasks'
4
+
5
+ task default: [
6
+ :clean,
7
+ :"bake:code_quality:all",
8
+ :"bake:rspec",
9
+ :"bake:coverage:check_specs",
10
+ :"bake:rubocop",
11
+ :"bake:ok_rainbow"
12
+ ]
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'milight/version'
2
+ require 'milight/controller'
3
+
4
+ module Milight
5
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Milight
2
+ VERSION = '1.0.0'
3
+ end
@@ -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
@@ -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: []