hue-lib 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ RubyHue
2
+ ================
3
+
4
+ This is a very early attempt to create a Ruby library for controlling the [Philips Hue](http://www.meethue.com) lighting system. The API has not yet been released, but there are [several](http://www.nerdblog.com/2012/10/a-day-with-philips-hue.html) [people](http://rsmck.co.uk/hue) working to figure it out.
5
+
6
+ # WARNING
7
+ All of this is very experimental and could permanently damage your awesome (but ridiculously expensive) lightbulbs. As such, exercise extreme caution.
8
+
9
+ ## Getting Started
10
+ You can get a [great overview](http://rsmck.co.uk/hue) of the options and limitations of the lights from Ross McKillop.
11
+
12
+ You will need to find the IP address of your bridge unit and also generate a unique ID (UUID works great) for your controlling application and add them to the top of the `hue.rb` file.
13
+
14
+ You will need to use this information to register your app with the controller. This library does not do that at this time, so you will need to manually do that. I suggest following the *Registering Your Application* section of [Ross's overview](http://rsmck.co.uk/hue).
15
+
16
+ ## Usage
17
+ To begin using, fire up the irb console and load the `hue.rb` file.
18
+
19
+ ```ruby
20
+ >> load 'hue.rb'
21
+ => true
22
+ ```
23
+
24
+ You can see all of the lights attached to your controller by querying the bridge.
25
+
26
+ ```ruby
27
+ >> Hue::Bridge.identities
28
+ => {"1"=>"Master Bedroom Dresser", "2"=>"Wife Bedside", "3"=>"Bedside (front)", "4"=>"Bedside (back)", "5"=>"Family Room Desk", "6"=>"Family Room", "7"=>"Living Room Square"}
29
+ ```
30
+
31
+ If you know the ID number of a particular lamp, you can access it directly.
32
+
33
+ ```ruby
34
+ >> b = Hue::Bulb.new(5)
35
+ => #<Hue::Bulb:0x007fe35a3586b8 @id=5, @hub=#<Hue::Bridge:0x007fe35a358690 @light_id="5">>
36
+
37
+ # on/off
38
+ >> b.on?
39
+ => false
40
+
41
+ >> b.on
42
+ => true
43
+
44
+ >> b.on?
45
+ => true
46
+
47
+ # settings
48
+ >> b.settings
49
+ => {"ct"=>343, "on"=>true, "bri"=>240}
50
+
51
+ >> b.brightness = 128
52
+ => 128
53
+
54
+ >> b.update hue: 45000, sat: 180
55
+ => true
56
+
57
+ >> b.settings
58
+ => {"hue"=>45000, "sat"=>180, "on"=>true, "bri"=>128}
59
+
60
+ # blinking
61
+ >> b.blinking?
62
+ => false
63
+
64
+ >> b.blink
65
+ => nil
66
+
67
+ >> b.blinking?
68
+ => true
69
+
70
+ >> b.blink false
71
+ => nil
72
+
73
+ >> b.blinking?
74
+ => false
75
+ ```
76
+
77
+ ## Experimental
78
+ There is an experimental mode that attempts to simulate a candle flicker. This defaults to only flickering 15 times as it's really not the way the bridge or bulbs were designed to work. Additionally, this operates on the main thread so you really can't do anything else while it's running.
79
+
80
+ ```ruby
81
+ >> b.candle
82
+ => nil
83
+ ```
84
+
85
+ The candle makes use of temporarily stashing the lamp's current settings before it starts and then restoring them upon completion. You can use this yourself with the `stash` and `restore` commands.
86
+
87
+ ## Going Forward
88
+ There is still a lot of work to be done figuring out the various timer options of the hub, etc. Hopefully, the official API will be released in the near future and expose even more goodies that we're unaware of.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new('spec') do |t|
7
+ t.rspec_opts = ["-fd", "-c"]
8
+ t.ruby_opts = ["-Ispec,lib"]
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ end
11
+
12
+ rescue LoadError
13
+ desc 'Spec rake task not available'
14
+ task :spec do
15
+ abort 'Spec rake task is not available. Be sure to install rspec as a gem or plugin'
16
+ end
17
+ end
18
+
19
+ task :default => [:spec]
data/hue-lib.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "hue-lib"
6
+ s.version = '0.5.0'
7
+ s.authors = ["Birkir A. Barkarson", ""]
8
+ s.email = ["birkirb@stoicviking.net"]
9
+ s.homepage = "https://github.com/birkirb/hue-lib"
10
+ s.summary = %q{Ruby library for controlling Phillips Hue light bridge.}
11
+ s.description = s.summary
12
+
13
+ s.rubyforge_project = "hue-lib"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency("json")
21
+ s.add_development_dependency("rspec", '>= 2.6.0')
22
+ s.add_development_dependency("mocha", '>= 0.9.0')
23
+ s.add_development_dependency("webmock", '>= 1.8.0')
24
+ end
data/lib/hue.rb ADDED
@@ -0,0 +1,59 @@
1
+ APP_NAME = 'ruby_hue'
2
+ BASE = 'http://10.10.10.20/api'
3
+ # UUID = 'd79713a3433df3d972ba7c22cb1cc23e'
4
+ # Digest::MD5.hexdigest('aa4f6bc0-2045-0130-8cf0-0018de9ecdd0')
5
+
6
+ require 'net/http'
7
+ require 'json'
8
+ require 'matrix'
9
+
10
+ RGB_MATRIX = Matrix[
11
+ [3.233358361244897, -1.5262682428425947, 0.27916711262124544],
12
+ [-0.8268442148395835, 2.466767560486707, 0.3323241608108406],
13
+ [0.12942207487871885, 0.19839858329512317, 2.0280912276039635]
14
+ ]
15
+
16
+ require 'hue/bridge.rb'
17
+ require 'hue/bulb.rb'
18
+ require 'hue/config.rb'
19
+
20
+ module Hue
21
+
22
+ DEVICE_TYPE = "RubyHue"
23
+
24
+ def self.device_type
25
+ DEVICE_TYPE
26
+ end
27
+
28
+ def self.config
29
+ Hue::Config.default
30
+ end
31
+
32
+ class Error < StandardError
33
+ attr_accessor :original_error
34
+
35
+ def initialize(message, original_error = nil)
36
+ super(message)
37
+ @original_error = original_error
38
+ end
39
+
40
+ def to_s
41
+ if @original_error.nil?
42
+ super
43
+ else
44
+ "#{super}\nCause: #{@original_error.to_s}"
45
+ end
46
+ end
47
+ end
48
+
49
+ module API
50
+ class Error < ::Hue::Error
51
+ def initialize(api_error)
52
+ @type = api_error['type']
53
+ @address = api_error['address']
54
+ super(api_error['description'])
55
+ end
56
+ end
57
+ end
58
+
59
+ end
data/lib/hue/bridge.rb ADDED
@@ -0,0 +1,147 @@
1
+ require 'digest/md5'
2
+ require 'uuid'
3
+ # require 'singleton'
4
+
5
+ module Hue
6
+ class Bridge
7
+ # include Singleton
8
+
9
+ # # Remove
10
+ # def self.method_missing(method, *args, &block)
11
+ # if args.empty?
12
+ # self.instance.send method
13
+ # else
14
+ # self.instance.send method, *args
15
+ # end
16
+ # end
17
+
18
+ # Move to APP class
19
+ def self.register(host = BASE)
20
+ # TODO: Look for default config.
21
+ puts "Please press the button on bridge before continuing."
22
+ puts "Once done, press Enter to continue."
23
+ gets
24
+ secret = Digest::MD5.hexdigest(UUID.generate) # one time UUID
25
+ puts "Registering app...(#{secret})"
26
+ config = Hue::Config.new(host, secret)
27
+ instance.create(
28
+ URI.parse(config.base_uri),
29
+ {"username" => config.base_uri, "devicetype" => Hue.device_type}
30
+ )
31
+ config.write
32
+ end
33
+
34
+ # Move to APP class
35
+ def self.remove
36
+ config = Config.default
37
+ instance.delete(
38
+ URI.parse(config.base_uri),
39
+ {"username" => config.identifier}
40
+ )
41
+ config.delete
42
+ end
43
+
44
+ public
45
+
46
+ attr_reader :hue_config
47
+
48
+ def initialize(hue_config = Hue.config)
49
+ @hue_config = hue_config
50
+ end
51
+
52
+ def status
53
+ index(uri)
54
+ end
55
+
56
+ def lights
57
+ index(uri('lights'))
58
+ end
59
+
60
+ def light_names
61
+ lights.map { |k,v| "#{k}. #{v['name']}" }.join("\n")
62
+ end
63
+
64
+ def config
65
+ index(uri('config'))
66
+ end
67
+
68
+ def schedules
69
+ index(uri('schedules'))
70
+ end
71
+
72
+ def bulbs
73
+ # puts status['lights'].inspect
74
+ @bulbs ||= lights.keys.map { |b| Bulb.new(self, b) }
75
+ end
76
+
77
+ # def remove_schedule(schedule_id)
78
+ # delete uri('schedules', schedule_id)
79
+ # puts "Removed schedule #{schedule_id}"
80
+ # end
81
+
82
+ # def remove_all_schedules
83
+ # ids = schedules.keys.map(&:to_i).sort.reverse
84
+ # puts "Removing #{ids.size} schedule#{'s' if ids.size != 1}..."
85
+ # ids.each{|x| remove_schedule x}
86
+ # end
87
+
88
+ def get_light_state(id)
89
+ index(uri('lights', id))
90
+ end
91
+
92
+ def set_light_state(id, state)
93
+ update(uri('lights', id, 'state'), state)
94
+ end
95
+
96
+ private
97
+
98
+ def uri(*args)
99
+ URI [hue_config.base_uri, hue_config.identifier, args].flatten.reject{|x| x.to_s.strip == ''}.join('/')
100
+ end
101
+
102
+ def index(url)
103
+ # json = Net::HTTP.get(url)
104
+ # JSON.parse(json)
105
+ request = Net::HTTP::Get.new(url.request_uri, initheader = {'Content-Type' =>'application/json'})
106
+ response = Net::HTTP.new(url.host, url.port).start {|http| http.request(request) }
107
+ display(response)
108
+ json = JSON.parse(response.body)
109
+ if json.is_a?(Array) && error = json.first['error']
110
+ raise Hue::API::Error.new(error)
111
+ else
112
+ json
113
+ end
114
+ end
115
+
116
+ def update(url, settings = {})
117
+ request = Net::HTTP::Put.new(url.request_uri, initheader = {'Content-Type' =>'application/json'})
118
+ request.body = settings.to_json
119
+ display Net::HTTP.new(url.host, url.port).start {|http| http.request(request) }
120
+ end
121
+
122
+ def delete(url, settings = {})
123
+ request = Net::HTTP::Delete.new(url.request_uri, initheader = {'Content-Type' =>'application/json'})
124
+ request.body = settings.to_json
125
+ display Net::HTTP.new(url.host, url.port).start{|http| http.request(request)}
126
+ end
127
+
128
+ def create(url, settings = {})
129
+ request = Net::HTTP::Post.new(url.request_uri, initheader = {'Content-Type' =>'application/json'})
130
+ request.body = settings.to_json
131
+ display Net::HTTP.new(url.host, url.port).start {|http| http.request(request) }
132
+ end
133
+
134
+ def display(response = nil)
135
+ if response and response.code.to_s != '200'
136
+ # Output to logger
137
+ # puts "Response #{response.code} #{response.message}: #{JSON.parse(response.body).first}"
138
+ false
139
+ else
140
+ # Output to logger
141
+ # puts "Response #{response.code} #{response.message}: #{JSON.parse(response.body).first}"
142
+ true
143
+ end
144
+ end
145
+
146
+ end
147
+ end
data/lib/hue/bulb.rb ADDED
@@ -0,0 +1,458 @@
1
+ module Hue
2
+ class Bulb
3
+
4
+ public
5
+
6
+ attr_reader :id, :bridge
7
+ attr_accessor :options
8
+
9
+ def initialize(bridge, light_id, options = {})
10
+ @bridge = bridge
11
+ @id = light_id
12
+ @options = options
13
+ end
14
+
15
+ def refresh!
16
+ @status = bridge.get_light_state(id)
17
+ end
18
+
19
+ def info
20
+ status.select do |k, value|
21
+ value.is_a?(String)
22
+ end
23
+ end
24
+
25
+ def state
26
+ status['state']
27
+ end
28
+
29
+ def [](item)
30
+ state[item.to_s]
31
+ end
32
+
33
+ def name
34
+ status['name']
35
+ end
36
+
37
+ def name=(_name)
38
+ update(name: _name)
39
+ end
40
+
41
+ def on?
42
+ self[:on]
43
+ end
44
+
45
+ def off?
46
+ !on?
47
+ end
48
+
49
+ def on
50
+ update(on: true)
51
+ on?
52
+ end
53
+
54
+ def off
55
+ update(on: false)
56
+ off?
57
+ end
58
+
59
+ def brightness
60
+ self[:bri]
61
+ end
62
+
63
+ alias :bri :brightness
64
+
65
+ def brightness=(bri)
66
+ update(bri: bri)
67
+ brightness
68
+ end
69
+
70
+ alias :bri= :brightness=
71
+
72
+ def hue
73
+ self[:hue]
74
+ end
75
+
76
+ HUE_MAX = 65536.0
77
+ HUE_DEGREES = 360
78
+ HUE_SCALE = HUE_MAX / HUE_DEGREES
79
+
80
+ def hue=(_hue)
81
+ _hue = (_hue * HUE_SCALE).to_i
82
+ update(hue: _hue)
83
+ hue
84
+ end
85
+
86
+ def saturation
87
+ self[:sat]
88
+ end
89
+
90
+ alias :sat :saturation
91
+
92
+ def saturation=(_sat)
93
+ update(sat: _sat)
94
+ sat
95
+ end
96
+
97
+ alias :sat= :saturation=
98
+
99
+ def color_temperature
100
+ self[:ct]
101
+ end
102
+
103
+ alias :ct :color_temperature
104
+
105
+ def color_temperature=(_ct)
106
+ update(ct: [[_ct, 154].max, 500].min)
107
+ colortemp
108
+ end
109
+
110
+ alias :ct= :color_temperature=
111
+
112
+ def color_mode
113
+ self[:colormode]
114
+ end
115
+
116
+ alias :colormode :color_mode
117
+
118
+ def blinking?
119
+ !solid?
120
+ end
121
+
122
+ def solid?
123
+ 'none' == self['alert']
124
+ end
125
+
126
+ def blink
127
+ update(alert: 'lselect')
128
+ end
129
+
130
+ def solid
131
+ update(alert: 'none')
132
+ end
133
+
134
+ def flash
135
+ update(alert: 'select')
136
+ update(alert: 'none')
137
+ end
138
+
139
+ def transition_time
140
+ # transition time in seconds
141
+ (options[:transitiontime] || 1).to_f / 10
142
+ end
143
+
144
+ def transition_time=(time)
145
+ # transition time in seconds
146
+ self.options[:transitiontime] = (time * 10).to_i
147
+ end
148
+
149
+ protected
150
+
151
+ def settings
152
+ options.merge case state['colormode']
153
+ when 'ct'
154
+ {'ct' => state['ct']}
155
+ when 'xy'
156
+ {'xy' => state['xy']}
157
+ when 'hs'
158
+ {'hue' => state['hue'], 'sat' => state['sat']}
159
+ end.merge('on' => state['on'], 'bri' => state['bri'])
160
+ end
161
+
162
+ def rgb
163
+ send %(#{colormode}_to_rgb)
164
+ end
165
+
166
+ def red
167
+ rgb[:red]
168
+ end
169
+
170
+ def green
171
+ rgb[:green]
172
+ end
173
+
174
+ def blue
175
+ rgb[:blue]
176
+ end
177
+
178
+ def red=(_red)
179
+ self.rgb = [_red, green, blue]
180
+ end
181
+
182
+ def green=(_green)
183
+ self.rgb = [red, _green, blue]
184
+ end
185
+
186
+ def blue=(_blue)
187
+ self.rgb = [red, green, _blue]
188
+ end
189
+
190
+ def kelvin
191
+ # convert colortemp setting to Kelvin
192
+ 1000000 / self['ct']
193
+ end
194
+
195
+ def kelvin=(_temp)
196
+ self.colortemp = 1000000 / [_temp, 1].max
197
+ end
198
+
199
+ def ct_to_rgb
200
+ # using method described at
201
+ # http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
202
+ temp = kelvin / 100
203
+
204
+ red = temp <= 66 ? 255 : 329.698727446 * ((temp - 60) ** -0.1332047592)
205
+
206
+ green = if temp <= 66
207
+ 99.4708025861 * Math.log(temp) - 161.1195681661
208
+ else
209
+ 288.1221695283 * ((temp - 60) ** -0.0755148492)
210
+ end
211
+
212
+ blue = if temp >= 66
213
+ 255
214
+ elsif temp <= 19
215
+ 0
216
+ else
217
+ 138.5177312231 * Math.log(temp - 10) - 305.0447927307
218
+ end
219
+
220
+ { red: [[red, 0].max, 255].min.to_i,
221
+ green: [[green, 0].max, 255].min.to_i,
222
+ blue: [[blue, 0].max, 255].min.to_i
223
+ }
224
+
225
+ end
226
+
227
+ def xyz
228
+ vals = state['xy']
229
+ vals + [1 - vals.first - vals.last]
230
+ end
231
+
232
+ def xy_to_rgb
233
+ values = (RGB_MATRIX * Matrix[xyz].transpose).to_a.flatten.map{|x| [[x * 255, 0].max, 255].min.to_i}
234
+ { red: values[0],
235
+ green: values[1],
236
+ blue: values[2]
237
+ }
238
+ end
239
+
240
+ def hue_in_degrees
241
+ self['hue'].to_f / HUE_SCALE
242
+ end
243
+
244
+ def hue_as_decimal
245
+ hue_in_degrees / HUE_DEGREES
246
+ end
247
+
248
+ def sat_as_decimal
249
+ self['sat'] / 255.0
250
+ end
251
+
252
+ def brightness_as_decimal
253
+ brightness / 255.0
254
+ end
255
+
256
+ def hs_to_rgb
257
+ h, s, v = hue_as_decimal, sat_as_decimal, brightness_as_decimal
258
+ if s == 0 #monochromatic
259
+ red = green = blue = v
260
+ else
261
+
262
+ v = 1.0 # We are setting the value to 1. Don't count brightness here
263
+ i = (h * 6).floor
264
+ f = h * 6 - i
265
+ p = v * (1 - s)
266
+ q = v * (1 - f * s)
267
+ t = v * (1 - (1 - f) * s)
268
+
269
+ case i % 6
270
+ when 0
271
+ red, green, blue = v, t, p
272
+ when 1
273
+ red, green, blue = q, v, p
274
+ when 2
275
+ red, green, blue = p, v, t
276
+ when 3
277
+ red, green, blue = p, q, v
278
+ when 4
279
+ red, green, blue = t, p, v
280
+ when 5
281
+ red, green, blue = v, p, q
282
+ end
283
+ end
284
+
285
+ { red: [[red * 255, 0].max, 255].min.to_i,
286
+ green: [[green * 255, 0].max, 255].min.to_i,
287
+ blue: [[blue * 255, 0].max, 255].min.to_i
288
+ }
289
+ end
290
+
291
+ def rgb=(colors)
292
+ red, green, blue = colors[0] / 255.0, colors[1] / 255.0, colors[2] / 255.0
293
+
294
+ max = [red, green, blue].max
295
+ min = [red, green, blue].min
296
+ h, s, l = 0, 0, ((max + min) / 2 * 255)
297
+
298
+ d = max - min
299
+ s = max == 0 ? 0 : (d / max * 255)
300
+
301
+ h = case max
302
+ when min
303
+ 0 # monochromatic
304
+ when red
305
+ (green - blue) / d + (green < blue ? 6 : 0)
306
+ when green
307
+ (blue - red) / d + 2
308
+ when blue
309
+ (red - green) / d + 4
310
+ end * 60 # / 6 * 360
311
+
312
+ h = (h * HUE_SCALE).to_i
313
+ update hue: h, sat: s.to_i#, bri: l.to_i
314
+ [h, s, 1.0]
315
+ end
316
+
317
+ def candle(repeat = 15)
318
+ # 0-65536 for hue, 182 per deg. Ideal 30-60 deg (5460-10920)
319
+ stash!
320
+ on if off?
321
+
322
+ repeat.times do
323
+ hue = ((rand * 3460) + 5460).to_i
324
+ sat = rand(64) + 170
325
+ bri = rand(32) + 16
326
+
327
+ delay = (rand * 0.35) + (@delay ||= 0)
328
+ update(hue: hue, sat: sat, bri: bri, transitiontime: (delay * 10).to_i)
329
+ sleep delay
330
+ end
331
+ restore!
332
+ end
333
+
334
+ private
335
+
336
+ def stash!
337
+ @stash ||= settings
338
+ end
339
+
340
+ def restore!
341
+ if stash
342
+ update(@stash)
343
+ unstash!
344
+ end
345
+ end
346
+
347
+ def unstash!
348
+ @stash = nil
349
+ end
350
+
351
+ def status
352
+ @status || refresh!
353
+ end
354
+
355
+ def update(settings = {})
356
+ if bridge.set_light_state(id, options.merge(settings))
357
+ settings.each do |key, value|
358
+ @status['state'][key.to_s] = value # or refresh!
359
+ end
360
+ end
361
+ end
362
+
363
+ # Experimental Sunrise/Sunset action
364
+ # this will transition from off and warm light to on and daytime light
365
+ # in a curve that mimics the actual sunrise.
366
+
367
+ def perform_sunrise(total_time_in_minutes = 18)
368
+ # total_time / 18 steps == time_per_step
369
+ # the multiplier should be 600 * time per step
370
+ minutes_per_step = total_time_in_minutes / 18.0
371
+ multiplier = (minutes_per_step * 60 * 10).to_i
372
+
373
+ perform_sun_transition total_time_in_minutes, sunrise_steps(multiplier)
374
+ end
375
+
376
+ def perform_sunrise(total_time_in_minutes = 18)
377
+ multiplier = sunrise_multiplier total_time_in_minutes
378
+ steps = sunrise_steps(multiplier)
379
+ if on?
380
+ puts "ON! #{steps[0][:bri]} :: #{brightness} :: #{brightness > steps[0][:bri]}"
381
+ while brightness >= steps[0][:bri]
382
+ steps.shift
383
+ end
384
+ end
385
+ steps.each_with_index do |step, i|
386
+ update step.merge(on: true)
387
+ sleep(step[:transitiontime] / 10.0)
388
+ end
389
+ end
390
+
391
+ def perform_sunset(total_time_in_minutes = 18)
392
+ multiplier = sunrise_multiplier total_time_in_minutes
393
+ steps = sunset_steps(multiplier)
394
+ if on?
395
+ puts "ON! #{steps[0][:bri]} :: #{brightness} :: #{brightness > steps[0][:bri]}"
396
+ while brightness <= steps[0][:bri]
397
+ steps.shift
398
+ end
399
+ end
400
+ steps.each_with_index do |step, i|
401
+ update step.merge(on: true)
402
+ sleep(step[:transitiontime] / 10.0)
403
+ end
404
+ off
405
+ end
406
+
407
+ SUN_STEPS = [ 1.5, 2, 3, 1, 4, 2.5 ]
408
+ SUN_TIMES = [ 3, 3, 3, 1, 2, 1]
409
+
410
+ def sunrise_multiplier(total_time_in_minutes)
411
+ # total_time / 18 steps == time_per_step
412
+ # the multiplier should be 600 * time per step
413
+ minutes_per_step = total_time_in_minutes / 18.0
414
+ (minutes_per_step * 60 * 10).to_i
415
+ end
416
+
417
+ def sunrise_brightness
418
+ sun_bri_unit = 10
419
+ SUN_STEPS.inject([0]){|all, i| all << ((i * sun_bri_unit) + all[-1]).to_i } << 255
420
+ end
421
+
422
+ def sunrise_temps
423
+ sun_temp_unit = 16
424
+ SUN_STEPS.inject([500]){|all, i| all << (all[-1] - (i * sun_temp_unit)).to_i} << 200
425
+ end
426
+
427
+ def sunrise_times
428
+ [0, SUN_TIMES, 5].flatten
429
+ end
430
+
431
+ def sunset_times
432
+ [0, 5, SUN_TIMES.reverse].flatten
433
+ end
434
+
435
+ def sunrise_steps(multiplier = 600)
436
+ bri_steps = sunrise_brightness
437
+ tmp_steps = sunrise_temps
438
+
439
+ steps = []
440
+ sunrise_times.each_with_index do |t, i|
441
+ steps << {bri: bri_steps[i], ct: tmp_steps[i], transitiontime: (t * multiplier)}
442
+ end
443
+ steps
444
+ end
445
+
446
+ def sunset_steps(multiplier = 600)
447
+ bri_steps = sunrise_brightness.reverse
448
+ tmp_steps = sunrise_temps.reverse
449
+
450
+ steps = []
451
+ sunset_times.each_with_index do |t, i|
452
+ steps << {bri: bri_steps[i], ct: tmp_steps[i], transitiontime: (t * multiplier)}
453
+ end
454
+ steps
455
+ end
456
+
457
+ end
458
+ end