hue-lib 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +9 -8
- data/hue-lib.gemspec +4 -3
- data/lib/hue/animations/candle.rb +54 -0
- data/lib/hue/animations/sunrise.rb +106 -0
- data/lib/hue/bridge.rb +23 -28
- data/lib/hue/bulb.rb +33 -332
- data/lib/hue/colors/color.rb +35 -0
- data/lib/hue/colors/color_temperature.rb +85 -0
- data/lib/hue/colors/hue_saturation.rb +104 -0
- data/lib/hue/colors/rgb.rb +102 -0
- data/lib/hue/colors/xy.rb +63 -0
- data/lib/hue/colors.rb +42 -0
- data/lib/hue.rb +29 -8
- data/spec/hue/bulb_spec.rb +25 -15
- data/spec/hue/colors/color_spec.rb +33 -0
- data/spec/hue/colors/color_temperature_spec.rb +106 -0
- data/spec/hue/colors/hue_saturation_spec.rb +91 -0
- data/spec/hue/colors/rgb_spec.rb +64 -0
- data/spec/hue/colors/xy_spec.rb +51 -0
- data/spec/hue/colors_spec.rb +92 -0
- data/spec/hue_spec.rb +8 -0
- metadata +20 -4
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hue
|
2
|
+
module Colors
|
3
|
+
class Color
|
4
|
+
|
5
|
+
ERROR_METHOD_NOT_IMPLEMENTED = 'method-not-implemented'
|
6
|
+
|
7
|
+
def self.ranged(min, val, max)
|
8
|
+
[[min, val].max, max].min
|
9
|
+
end
|
10
|
+
|
11
|
+
public
|
12
|
+
|
13
|
+
def to_hash
|
14
|
+
raise ERROR_METHOD_NOT_IMPLEMENTED
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
raise ERROR_METHOD_NOT_IMPLEMENTED
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_rgb
|
22
|
+
raise ERROR_METHOD_NOT_IMPLEMENTED
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
|
28
|
+
def ranged(min, val, max)
|
29
|
+
# For convinence and polymorphism
|
30
|
+
self.class.ranged(min, val, max)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
module Hue
|
4
|
+
module Colors
|
5
|
+
class ColorTemperature < Color
|
6
|
+
|
7
|
+
MEGA = 1e6
|
8
|
+
KELVIN_MIN = 2000
|
9
|
+
KELVIN_MAX = 6500
|
10
|
+
MIRED_MIN = 153
|
11
|
+
MIRED_MAX = 500
|
12
|
+
|
13
|
+
public
|
14
|
+
|
15
|
+
def initialize(temperature)
|
16
|
+
if scale = Hue.percent_to_unit_interval(temperature)
|
17
|
+
self.mired = unit_to_mired_interval(scale)
|
18
|
+
else
|
19
|
+
# Assume an integer value
|
20
|
+
temperature = temperature.to_i
|
21
|
+
if temperature >= KELVIN_MIN
|
22
|
+
self.kelvin = temperature
|
23
|
+
else
|
24
|
+
self.mired = temperature
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def mired
|
30
|
+
@mired.floor
|
31
|
+
end
|
32
|
+
|
33
|
+
def mired=(t)
|
34
|
+
@mired = ranged(MIRED_MIN, t.to_f, MIRED_MAX)
|
35
|
+
end
|
36
|
+
|
37
|
+
def kelvin
|
38
|
+
ranged(KELVIN_MIN, MEGA / @mired, KELVIN_MAX).round
|
39
|
+
end
|
40
|
+
|
41
|
+
def kelvin=(t)
|
42
|
+
self.mired = (MEGA / ranged(KELVIN_MIN, t.to_f, KELVIN_MAX))
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_hash
|
46
|
+
{ct: mired}
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"Temperature=#{self.kelvin.to_i}°K (#{self.mired} mired)"
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_rgb
|
54
|
+
# using method described at
|
55
|
+
# http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
56
|
+
temp = kelvin / 100
|
57
|
+
|
58
|
+
red = temp <= 66 ? 255 : 329.698727446 * ((temp - 60) ** -0.1332047592)
|
59
|
+
|
60
|
+
green = if temp <= 66
|
61
|
+
99.4708025861 * Math.log(temp) - 161.1195681661
|
62
|
+
else
|
63
|
+
288.1221695283 * ((temp - 60) ** -0.0755148492)
|
64
|
+
end
|
65
|
+
|
66
|
+
blue = if temp >= 66
|
67
|
+
255
|
68
|
+
elsif temp <= 19
|
69
|
+
0
|
70
|
+
else
|
71
|
+
138.5177312231 * Math.log(temp - 10) - 305.0447927307
|
72
|
+
end
|
73
|
+
|
74
|
+
RGB.new(red, green, blue)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def unit_to_mired_interval(unit_interval)
|
80
|
+
unit_interval * (MIRED_MAX - MIRED_MIN) + MIRED_MIN
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Hue
|
2
|
+
module Colors
|
3
|
+
class HueSaturation < Color
|
4
|
+
|
5
|
+
HUE_MIN = 0
|
6
|
+
HUE_MAX = 65536.0
|
7
|
+
HUE_DEGREES = 360
|
8
|
+
HUE_SCALE = HUE_MAX / HUE_DEGREES
|
9
|
+
SATURATION_MIN = 0
|
10
|
+
SATURATION_MAX = 255
|
11
|
+
|
12
|
+
attr_reader :hue, :saturation
|
13
|
+
alias :sat :saturation
|
14
|
+
|
15
|
+
public
|
16
|
+
|
17
|
+
def initialize(hue, saturation)
|
18
|
+
self.hue = hue
|
19
|
+
self.saturation = saturation
|
20
|
+
end
|
21
|
+
|
22
|
+
def hue=(value)
|
23
|
+
if scale = Hue.percent_to_unit_interval(value)
|
24
|
+
@hue = unit_to_hue_interval(scale)
|
25
|
+
else
|
26
|
+
@hue = ranged(HUE_MIN, value.to_i, HUE_MAX)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def hue_in_degrees
|
31
|
+
self.hue.to_f / HUE_SCALE
|
32
|
+
end
|
33
|
+
|
34
|
+
def hue_in_unit_interval
|
35
|
+
hue_in_degrees / HUE_DEGREES
|
36
|
+
end
|
37
|
+
|
38
|
+
def saturation=(value)
|
39
|
+
if scale = Hue.percent_to_unit_interval(value)
|
40
|
+
@saturation = unit_to_saturation_interval(scale)
|
41
|
+
else
|
42
|
+
@saturation = ranged(SATURATION_MIN, value.to_i, SATURATION_MAX)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
alias :sat= :saturation=
|
46
|
+
|
47
|
+
def saturation_in_unit_interval
|
48
|
+
self.saturation / SATURATION_MAX.to_f
|
49
|
+
end
|
50
|
+
alias :sat_in_unit_interval :saturation_in_unit_interval
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"Hue=#{self.hue}, Saturation=#{self.saturation}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_hash
|
57
|
+
{hue: hue, sat: saturation}
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_rgb(brightness_in_unit_interval = 1.0)
|
61
|
+
h, s, v = hue_in_unit_interval, saturation_in_unit_interval, brightness_in_unit_interval
|
62
|
+
if s == 0 #monochromatic
|
63
|
+
red = green = blue = v
|
64
|
+
else
|
65
|
+
|
66
|
+
v = 1.0 # We are setting the value to 1. Don't count brightness here
|
67
|
+
i = (h * 6).floor
|
68
|
+
f = h * 6 - i
|
69
|
+
p = v * (1 - s)
|
70
|
+
q = v * (1 - f * s)
|
71
|
+
t = v * (1 - (1 - f) * s)
|
72
|
+
|
73
|
+
case i % 6
|
74
|
+
when 0
|
75
|
+
red, green, blue = v, t, p
|
76
|
+
when 1
|
77
|
+
red, green, blue = q, v, p
|
78
|
+
when 2
|
79
|
+
red, green, blue = p, v, t
|
80
|
+
when 3
|
81
|
+
red, green, blue = p, q, v
|
82
|
+
when 4
|
83
|
+
red, green, blue = t, p, v
|
84
|
+
when 5
|
85
|
+
red, green, blue = v, p, q
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
RGB.new(red * RGB::MAX, green * RGB::MAX, blue * RGB::MAX)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def unit_to_hue_interval(value)
|
95
|
+
(value * (HUE_MAX - HUE_MIN) + HUE_MIN).round
|
96
|
+
end
|
97
|
+
|
98
|
+
def unit_to_saturation_interval(value)
|
99
|
+
(value * (SATURATION_MAX - SATURATION_MIN) + SATURATION_MIN).round
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
module Hue
|
4
|
+
module Colors
|
5
|
+
class RGB < Color
|
6
|
+
|
7
|
+
MIN = 0
|
8
|
+
MAX = 255
|
9
|
+
|
10
|
+
def self.ranged(value)
|
11
|
+
super(MIN, value, MAX)
|
12
|
+
end
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
attr_reader :red, :green, :blue
|
17
|
+
|
18
|
+
def initialize(*rgb)
|
19
|
+
red, green, blue = rgb
|
20
|
+
self.red = red
|
21
|
+
self.green = green
|
22
|
+
self.blue = blue
|
23
|
+
end
|
24
|
+
|
25
|
+
def red=(value)
|
26
|
+
@red = parse(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def green=(value)
|
30
|
+
@green = parse(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def blue=(value)
|
34
|
+
@blue = parse(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
max = MAX.to_f
|
39
|
+
red, green, blue = self.red / max, self.green / max, self.red / max
|
40
|
+
|
41
|
+
max = [red, green, blue].max
|
42
|
+
min = [red, green, blue].min
|
43
|
+
h, s, l = 0, 0, ((max + min) / 2 * 255)
|
44
|
+
|
45
|
+
d = max - min
|
46
|
+
s = max == 0 ? 0 : (d / max * 255)
|
47
|
+
|
48
|
+
h = case max
|
49
|
+
when min
|
50
|
+
0 # monochromatic
|
51
|
+
when red
|
52
|
+
(green - blue) / d + (green < blue ? 6 : 0)
|
53
|
+
when green
|
54
|
+
(blue - red) / d + 2
|
55
|
+
when blue
|
56
|
+
(red - green) / d + 4
|
57
|
+
end * 60 # / 6 * 360
|
58
|
+
|
59
|
+
h = (h * HueSaturation::HUE_SCALE).to_i
|
60
|
+
{hue: h, sat: s.to_i, bri: Bulb::BRIGHTNESS_MAX}
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
"RGB≈#{rgb}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_rgb
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def ==(rhs)
|
72
|
+
rhs.is_a?(RGB) &&
|
73
|
+
[:red, :green, :blue].all? { |m| self.send(m) == rhs.send(m) }
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def ranged(value)
|
79
|
+
self.class.ranged(value.to_i).round
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def rgb
|
85
|
+
[red, green, blue]
|
86
|
+
end
|
87
|
+
|
88
|
+
def unit_to_rgb_scale(value)
|
89
|
+
(value * (MAX - MIN) + MIN).round
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse(value)
|
93
|
+
if scale = Hue.percent_to_unit_interval(value)
|
94
|
+
unit_to_rgb_scale(scale)
|
95
|
+
else
|
96
|
+
ranged(value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
module Hue
|
3
|
+
module Colors
|
4
|
+
class XY < Color
|
5
|
+
|
6
|
+
MIN = 0.0
|
7
|
+
MAX = 1.0
|
8
|
+
RGB_MATRIX = Matrix[
|
9
|
+
[ 3.233358361244897, -1.5262682428425947, 0.27916711262124544],
|
10
|
+
[-0.8268442148395835, 2.466767560486707, 0.3323241608108406 ],
|
11
|
+
[ 0.12942207487871885, 0.19839858329512317, 2.0280912276039635 ],
|
12
|
+
]
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
attr_reader :x, :y
|
17
|
+
|
18
|
+
def initialize(*xy)
|
19
|
+
self.x = xy.first
|
20
|
+
self.y = xy.last
|
21
|
+
end
|
22
|
+
|
23
|
+
def x=(value)
|
24
|
+
@x = ranged(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def y=(value)
|
28
|
+
@y = ranged(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
{xy: xy}
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
"XY=#{xy}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_rgb
|
40
|
+
z = 1 - x - y
|
41
|
+
xyz = [x, y, z]
|
42
|
+
values = (RGB_MATRIX * Matrix[xyz].transpose).to_a.flatten.map do |x|
|
43
|
+
RGB.ranged(x * RGB::MAX).to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
RGB.new(*values)
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def ranged(val)
|
52
|
+
super(MIN, val.to_f, MAX)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def xy
|
58
|
+
[x,y]
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/hue/colors.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'colors/color'
|
2
|
+
require_relative 'colors/hue_saturation'
|
3
|
+
require_relative 'colors/color_temperature'
|
4
|
+
require_relative 'colors/xy'
|
5
|
+
require_relative 'colors/rgb'
|
6
|
+
|
7
|
+
module Hue
|
8
|
+
module Colors
|
9
|
+
|
10
|
+
def self.parse(*args)
|
11
|
+
case args.size
|
12
|
+
when 1
|
13
|
+
Colors::ColorTemperature.new(args.first)
|
14
|
+
when 2
|
15
|
+
a,b = args.first.to_f, args.last.to_f
|
16
|
+
if a > 1.0
|
17
|
+
Colors::HueSaturation.new(args.first, args.last)
|
18
|
+
else
|
19
|
+
Colors::XY.new(*args)
|
20
|
+
end
|
21
|
+
when 3
|
22
|
+
Colors::RGB.new(*args)
|
23
|
+
else
|
24
|
+
raise Error.new("Unable to parse to color: #{args.inspect}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse_state(state)
|
29
|
+
case state['colormode']
|
30
|
+
when 'ct'
|
31
|
+
Colors::ColorTemperature.new(state['ct'])
|
32
|
+
when 'xy'
|
33
|
+
Colors::XY.new(*state['xy'])
|
34
|
+
when 'hs'
|
35
|
+
Colors::HueSaturation.new(state['hue'], state['sat'])
|
36
|
+
else
|
37
|
+
raise Error.new("Unknown or missing state: #{state.inspect}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/hue.rb
CHANGED
@@ -1,15 +1,8 @@
|
|
1
1
|
require 'net/http'
|
2
|
-
require 'json'
|
3
|
-
require 'matrix'
|
4
2
|
require 'digest/md5'
|
3
|
+
require 'json'
|
5
4
|
require 'uuid'
|
6
5
|
|
7
|
-
RGB_MATRIX = Matrix[
|
8
|
-
[3.233358361244897, -1.5262682428425947, 0.27916711262124544],
|
9
|
-
[-0.8268442148395835, 2.466767560486707, 0.3323241608108406],
|
10
|
-
[0.12942207487871885, 0.19839858329512317, 2.0280912276039635]
|
11
|
-
]
|
12
|
-
|
13
6
|
module Hue
|
14
7
|
|
15
8
|
DEVICE_TYPE = 'hue-lib'
|
@@ -131,10 +124,38 @@ ST: ssdp:all
|
|
131
124
|
end
|
132
125
|
end
|
133
126
|
|
127
|
+
def self.logger
|
128
|
+
if !defined?(@@logger)
|
129
|
+
log_dir_path = File.join('/var', 'log', 'hue')
|
130
|
+
begin
|
131
|
+
FileUtils.mkdir_p(log_dir_path)
|
132
|
+
rescue Errno::EACCES
|
133
|
+
log_dir_path = File.join(ENV['HOME'], ".#{device_type}")
|
134
|
+
FileUtils.mkdir_p(log_dir_path)
|
135
|
+
end
|
136
|
+
|
137
|
+
log_file_path = File.join(log_dir_path, 'hue-lib.log')
|
138
|
+
log_file = File.new(log_file_path, File::WRONLY | File::APPEND | File::CREAT)
|
139
|
+
@@logger = Logger.new(log_file)
|
140
|
+
@@logger.level = Logger::INFO
|
141
|
+
end
|
142
|
+
|
143
|
+
@@logger
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.percent_to_unit_interval(value)
|
147
|
+
if percent = /(\d+)%/.match(value.to_s)
|
148
|
+
percent.captures.first.to_i / 100.0
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
134
154
|
end
|
135
155
|
|
136
156
|
require 'hue/config/abstract'
|
137
157
|
require 'hue/config/application'
|
138
158
|
require 'hue/config/bridge'
|
139
159
|
require 'hue/bridge'
|
160
|
+
require 'hue/colors'
|
140
161
|
require 'hue/bulb'
|
data/spec/hue/bulb_spec.rb
CHANGED
@@ -33,21 +33,20 @@ describe Hue::Bulb do
|
|
33
33
|
bulb.off?.should be_true
|
34
34
|
end
|
35
35
|
|
36
|
-
it "should report the
|
37
|
-
bulb.hue.should == 13234
|
36
|
+
it "should report the brightness and color mode" do
|
38
37
|
bulb.brightness.should == 146
|
39
38
|
bulb.bri.should == 146
|
40
|
-
bulb.saturation.should == 208
|
41
|
-
bulb.sat.should == bulb.saturation
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should report the color temperature and color mode" do
|
45
|
-
bulb.color_temperature.should == 459
|
46
|
-
bulb.ct.should == bulb.color_temperature
|
47
39
|
bulb.color_mode.should == 'ct'
|
48
40
|
bulb.color_mode.should == bulb.colormode
|
49
41
|
end
|
50
42
|
|
43
|
+
it "should report the color" do
|
44
|
+
color = bulb.color
|
45
|
+
color.should be_a(Hue::Colors::ColorTemperature)
|
46
|
+
color.mired.should == 459
|
47
|
+
color.kelvin.should == 2179
|
48
|
+
end
|
49
|
+
|
51
50
|
it "should report the alert state" do
|
52
51
|
bulb.blinking?.should be_false
|
53
52
|
bulb.solid?.should be_true
|
@@ -64,17 +63,28 @@ describe Hue::Bulb do
|
|
64
63
|
end
|
65
64
|
|
66
65
|
it 'should allow setting hue, saturation and brightness' do
|
67
|
-
|
68
|
-
bulb.hue = 120
|
69
|
-
bulb.hue.should == 21845
|
66
|
+
color = Hue::Colors::HueSaturation.new(21845, 1293)
|
70
67
|
|
71
|
-
with_fake_update('lights/1/state', sat:
|
72
|
-
bulb.
|
73
|
-
|
68
|
+
with_fake_update('lights/1/state', hue: 21845, sat: 255)
|
69
|
+
set_color = (bulb.color = color)
|
70
|
+
set_color.hue.should == 21845
|
71
|
+
set_color.saturation.should == 255
|
72
|
+
end
|
74
73
|
|
74
|
+
it 'should allow setting brightness as a number, percentage or string' do
|
75
75
|
with_fake_update('lights/1/state', bri: 233)
|
76
76
|
bulb.brightness = 233
|
77
77
|
bulb.brightness.should == 233
|
78
|
+
|
79
|
+
with_fake_update('lights/1/state', bri: 128)
|
80
|
+
bulb.brightness = "50%"
|
81
|
+
bulb.brightness.should == 128
|
82
|
+
bulb.brightness_in_unit_interval.should == 0.5019607843137255
|
83
|
+
bulb.brightness_percent.should == 50
|
84
|
+
|
85
|
+
with_fake_update('lights/1/state', bri: 128)
|
86
|
+
bulb.brightness = "128"
|
87
|
+
bulb.brightness.should == 128
|
78
88
|
end
|
79
89
|
|
80
90
|
it 'should allow setting blink, solid and flash alerts' do
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe Hue::Colors::Color do
|
4
|
+
|
5
|
+
context 'implements a ranged method: max > value > min' do
|
6
|
+
it 'should return the min when value < min' do
|
7
|
+
described_class.ranged(0, -1, 2).should == 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should return the max when value > max' do
|
11
|
+
described_class.ranged(0, 3, 2).should == 2
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return the value when max > value > min' do
|
15
|
+
described_class.ranged(0, 1, 2).should == 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def abstract_method(method)
|
20
|
+
abstract_color = described_class.new
|
21
|
+
abstract_color.should respond_to(method)
|
22
|
+
lambda do
|
23
|
+
abstract_color.send(method)
|
24
|
+
end.should raise_error(described_class::ERROR_METHOD_NOT_IMPLEMENTED)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'defines but does not implement methods:' do
|
28
|
+
it('#to_hash') { abstract_method(:to_hash) }
|
29
|
+
it('#to_s') { abstract_method(:to_s) }
|
30
|
+
it('#to_rgb') { abstract_method(:to_rgb) }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
require 'spec_helper.rb'
|
3
|
+
|
4
|
+
describe Hue::Colors::ColorTemperature do
|
5
|
+
|
6
|
+
context 'when initialized with a valid value' do
|
7
|
+
color = described_class.new(500)
|
8
|
+
|
9
|
+
it 'should report the temperature in mireds and kelvins' do
|
10
|
+
color.mired.should == 500
|
11
|
+
color.kelvin.should == 2000
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have a string representation' do
|
15
|
+
color.to_s.should == "Temperature=2000°K (500 mired)"
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have a hash representation' do
|
19
|
+
color.to_hash.should == {ct: 500}
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have an RGB representation' do
|
23
|
+
color.to_rgb.should == Hue::Colors::RGB.new(255,136,13)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when allowing change to the temperature value' do
|
27
|
+
it 'should go to the max in kelvins' do
|
28
|
+
color.kelvin = 7000
|
29
|
+
color.kelvin.should == described_class::KELVIN_MAX
|
30
|
+
color.mired.should == described_class::MIRED_MIN
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should go to the max in mireds' do
|
34
|
+
color.mired = 600
|
35
|
+
color.mired.should == described_class::MIRED_MAX
|
36
|
+
color.kelvin.should == described_class::KELVIN_MIN
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should go to the min in mireds' do
|
40
|
+
color.mired = 100
|
41
|
+
color.mired.should == described_class::MIRED_MIN
|
42
|
+
color.kelvin.should == described_class::KELVIN_MAX
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should go to the min in kelvins' do
|
46
|
+
color.mired = 2000
|
47
|
+
color.mired.should == described_class::MIRED_MAX
|
48
|
+
color.kelvin.should == described_class::KELVIN_MIN
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should hit the middle' do
|
52
|
+
mired_middle = described_class::MIRED_MAX/2
|
53
|
+
color.mired = mired_middle
|
54
|
+
color.mired.should == mired_middle
|
55
|
+
color.kelvin.should == 4000
|
56
|
+
|
57
|
+
kelvin_middle = described_class::KELVIN_MAX/2
|
58
|
+
color.kelvin = kelvin_middle
|
59
|
+
color.kelvin.should == kelvin_middle
|
60
|
+
color.mired.should == 307
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when initializing' do
|
67
|
+
it 'should set the temperature depending on the value' do
|
68
|
+
color = described_class.new(100)
|
69
|
+
color.mired.should == described_class::MIRED_MIN
|
70
|
+
|
71
|
+
color = described_class.new(600)
|
72
|
+
color.mired.should == described_class::MIRED_MAX
|
73
|
+
|
74
|
+
color = described_class.new(described_class::KELVIN_MIN)
|
75
|
+
color.kelvin.should == described_class::KELVIN_MIN
|
76
|
+
|
77
|
+
color = described_class.new(described_class::KELVIN_MAX + 3000)
|
78
|
+
color.kelvin.should == described_class::KELVIN_MAX
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should report kelvins as an integer' do
|
82
|
+
color = described_class.new(459)
|
83
|
+
color.kelvin.should == 2179
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should accept a string value for the temperature' do
|
87
|
+
color = described_class.new("100")
|
88
|
+
color.mired.should == described_class::MIRED_MIN
|
89
|
+
|
90
|
+
color = described_class.new((described_class::KELVIN_MAX + 3000).to_s)
|
91
|
+
color.kelvin.should == described_class::KELVIN_MAX
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should allow a percentage value for the mired scale' do
|
95
|
+
color = described_class.new("0%")
|
96
|
+
color.mired.should == described_class::MIRED_MIN
|
97
|
+
|
98
|
+
color = described_class.new("50%")
|
99
|
+
color.mired.should == 326
|
100
|
+
|
101
|
+
color = described_class.new("100%")
|
102
|
+
color.mired.should == described_class::MIRED_MAX
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|