cheeky-dreams 0.0.4 → 0.0.5

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.5
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "cheeky-dreams"
8
- s.version = "0.0.4"
8
+ s.version = "0.0.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["simon"]
12
- s.date = "2011-11-02"
12
+ s.date = "2011-11-17"
13
13
  s.description = "For controlling dream cheeky usb light"
14
14
  s.email = "simojenki@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -28,6 +28,8 @@ Gem::Specification.new do |s|
28
28
  "cheeky-dreams.gemspec",
29
29
  "lib/cheeky-dreams.rb",
30
30
  "spec/cheeky-dreams_spec.rb",
31
+ "spec/off.rb",
32
+ "spec/small-test.rb",
31
33
  "spec/spec_helper.rb",
32
34
  "spec/support/collecting_auditor.rb",
33
35
  "spec/support/stub_driver.rb",
data/lib/cheeky-dreams.rb CHANGED
@@ -1,11 +1,18 @@
1
1
  require 'thread'
2
2
 
3
3
  module CheekyDreams
4
-
5
- def rgb r, g, b
6
- [r, g, b].each { |c| raise "Invalid rgb value #{r}, #{g}, #{b}" if c < 0 || c > 255}
7
- [r, g, b]
8
- end
4
+
5
+ COLOURS = {
6
+ :off => [0, 0, 0],
7
+ :red => [255, 0, 0],
8
+ :green => [0, 255, 0],
9
+ :blue => [0, 0, 255],
10
+ :yellow => [255,255,0],
11
+ :aqua => [0,255,255],
12
+ :purple => [255,0,255],
13
+ :grey => [192,192,192],
14
+ :white => [255,255,255]
15
+ }
9
16
 
10
17
  def rgb_between a, b, ratio
11
18
  [
@@ -15,58 +22,84 @@ module CheekyDreams
15
22
  ]
16
23
  end
17
24
 
25
+ def sleep_until time
26
+ zzz_time = time - Time.now
27
+ sleep(zzz_time) if zzz_time > 0
28
+ end
29
+
18
30
  def position_between a, b, ratio
19
31
  return b if ratio >= 1.0
20
32
  (((b - a) * ratio) + a).floor
21
33
  end
22
34
 
23
- COLOURS = {
24
- :off => [0, 0, 0],
25
- :red => [255, 0, 0],
26
- :green => [0, 255, 0],
27
- :blue => [0, 0, 255],
28
- :yellow => [255,255,0],
29
- :aqua => [0,255,255],
30
- :purple => [255,0,255],
31
- :grey => [192,192,192],
32
- :white => [255,255,255]
33
- }
34
-
35
- def rgb_for colour
36
- case colour
37
- when Symbol
38
- raise "Unknown colour '#{colour}'" unless COLOURS.has_key?(colour)
39
- COLOURS[colour]
40
- when Array
41
- raise "Invalid rgb #{colour}" unless colour.length == 3 && colour.all? { |c| c.is_a? Fixnum }
42
- rgb(colour[0], colour[1], colour[2])
35
+ def rgb *rgb_args
36
+ raise 'Cannot give rgb for nil!' unless rgb_args
37
+ args = rgb_args.flatten
38
+ if args.length == 1 && args[0].is_a?(Symbol)
39
+ raise "Unknown colour '#{args[0]}'" unless COLOURS.has_key?(args[0])
40
+ COLOURS[args[0]]
41
+ elsif (args.length == 3 && args.all? { |c| c.is_a? Numeric })
42
+ r, g, b = args[0].floor, args[1].floor, args[2].floor
43
+ [r, g, b].each { |c| raise "Invalid rgb value #{r}, #{g}, #{b}" if c < 0 || c > 255}
44
+ [r, g, b]
43
45
  else
44
- raise "Unsupported colour type #{colour}"
46
+ raise "Invalid rgb #{args}"
45
47
  end
46
48
  end
47
-
48
- def stderr_auditor
49
- Class.new do
50
- def unhandled_error e
51
- STDERR.puts e.message
52
- STDERR.puts e.backtrace.join("\n")
49
+
50
+ class FilteringAuditor
51
+ def initialize auditor, including
52
+ @auditor, @including = auditor, Set.new(including)
53
+ end
54
+
55
+ def audit type, message
56
+ @auditor.audit(type, message) if @including.include?(type)
57
+ end
58
+ end
59
+
60
+ class StdIOAuditor
61
+ def initialize out = STDOUT, err = STDERR
62
+ @out, @err = out, err
63
+ end
64
+
65
+ def audit type, message
66
+ case type
67
+ when :error
68
+ @err.puts "#{type} - #{message}"
69
+ else
70
+ @out.puts "#{type} - #{message}"
53
71
  end
54
- end.new
72
+ end
55
73
  end
56
74
 
57
- def dev_null_auditor
58
- Class.new do
59
- def unhandled_error e
60
- end
61
- end.new
75
+ def filtering including, auditor
76
+ FilteringAuditor.new auditor, including
77
+ end
78
+
79
+ def audit_to *auditors
80
+ CompositeAuditor.new *auditors
81
+ end
82
+
83
+ class CompositeAuditor
84
+ def initialize *auditors
85
+ @auditors = auditors
86
+ end
87
+
88
+ def audit type, message
89
+ @auditors.each { |auditor| auditor.audit type, message }
90
+ end
91
+ end
92
+
93
+ def stdio_audit out = STDOUT, err = STDERR
94
+ StdIOAuditor.new(out, err)
95
+ end
96
+
97
+ def dev_null
98
+ Dev::Null.new
62
99
  end
63
100
 
64
101
  def stdout_driver
65
- Class.new do
66
- def go rgb
67
- puts rgb
68
- end
69
- end.new
102
+ Dev::IO.new
70
103
  end
71
104
 
72
105
  def ansi_driver
@@ -80,10 +113,58 @@ module CheekyDreams
80
113
  end
81
114
 
82
115
  def find_dream_cheeky_usb_device
83
- Device::DreamCheeky.new(File.dirname(Dir.glob('/sys/devices/**/red').first))
116
+ Dev::DreamCheeky.new(File.dirname(Dir.glob('/sys/devices/**/red').first))
117
+ end
118
+
119
+ def off
120
+ solid :off
121
+ end
122
+
123
+ def solid colour
124
+ Effects::Solid.new(colour)
125
+ end
126
+
127
+ def cycle colours, freq = 1
128
+ Effects::Cycle.new(colours, freq)
129
+ end
130
+
131
+ def fade from, to, steps = 10, freq = 1
132
+ Effects::Fade.new from, to, steps, freq
133
+ end
134
+
135
+ def fade_to to, steps = 10, freq = 1
136
+ Effects::FadeTo.new to, steps, freq
137
+ end
138
+
139
+ def func freq = 1, &block
140
+ Effects::Func.new freq, &block
141
+ end
142
+
143
+ def throb freq, from, to
144
+ Effects::Throb.new freq, from, to
84
145
  end
85
146
 
86
- module Device
147
+ def crazy freq = 1, new_effect_freq = 2
148
+ Effects::Crazy.new(freq, new_effect_freq)
149
+ end
150
+
151
+ module Dev
152
+ class Null
153
+ def audit type, message
154
+ end
155
+ end
156
+
157
+ class IO
158
+ def initialize io = $stdout
159
+ @io, @last = io, nil
160
+ end
161
+
162
+ def go rgb
163
+ @io.puts "[#{rgb.join(',')}]" unless rgb == @last
164
+ @last = rgb
165
+ end
166
+ end
167
+
87
168
  class DreamCheeky
88
169
  attr_reader :path
89
170
  def initialize path, max_threshold = 50
@@ -105,7 +186,7 @@ module CheekyDreams
105
186
  end
106
187
  end
107
188
 
108
- module Effect
189
+ module Effects
109
190
  class Effect
110
191
  include CheekyDreams
111
192
  include Math
@@ -138,11 +219,12 @@ module CheekyDreams
138
219
  @r_centre, @r_amp = centre_and_amp from[0], to[0]
139
220
  @g_centre, @g_amp = centre_and_amp from[1], to[1]
140
221
  @b_centre, @b_amp = centre_and_amp from[2], to[2]
141
- @count = 1
222
+ @sin_freq = 3.14 / freq.to_f
223
+ @count = (1.57 / @sin_freq).floor
142
224
  end
143
225
 
144
226
  def next current_colour
145
- x = sin(freq * @count)
227
+ x = sin(@sin_freq * @count)
146
228
  r = x * r_amp + r_centre
147
229
  g = x * g_amp + g_centre
148
230
  b = x * b_amp + b_centre
@@ -151,7 +233,7 @@ module CheekyDreams
151
233
  # [v, 0, 0]
152
234
 
153
235
  @count += 1
154
- [r, g, b]
236
+ [r.floor, g.floor, b.floor]
155
237
  end
156
238
 
157
239
  private
@@ -166,6 +248,21 @@ module CheekyDreams
166
248
  end
167
249
  end
168
250
 
251
+ class Crazy < Effect
252
+ def initialize freq, new_effect_freq
253
+ super freq
254
+ @new_effect_freq = new_effect_freq
255
+ @count, @effect = 0, nil
256
+ end
257
+
258
+ def next current_colour
259
+ if @count % @new_effect_freq == 0
260
+ @effect = FadeTo.new([rand(255), rand(255), rand(255)], @new_effect_freq, freq)
261
+ end
262
+ @count += 1
263
+ @effect.next current_colour
264
+ end
265
+ end
169
266
 
170
267
  class Func < Effect
171
268
  def initialize freq, &block
@@ -174,14 +271,14 @@ module CheekyDreams
174
271
  end
175
272
 
176
273
  def next current_colour = nil
177
- rgb_for(@block.yield(current_colour))
274
+ rgb(@block.yield(current_colour))
178
275
  end
179
276
  end
180
277
 
181
278
  class Solid < Effect
182
279
  def initialize colour
183
- super 1
184
- @rgb = rgb_for(colour)
280
+ super 0.1
281
+ @rgb = rgb(colour)
185
282
  end
186
283
 
187
284
  def next current_colour = nil
@@ -196,14 +293,14 @@ module CheekyDreams
196
293
  end
197
294
 
198
295
  def next current_colour = nil
199
- rgb_for(@cycle.next)
296
+ rgb(@cycle.next)
200
297
  end
201
298
  end
202
299
 
203
300
  class Fade < Effect
204
301
  def initialize from, to, steps, freq
205
302
  super freq
206
- @rgb_from, @rgb_to = rgb_for(from), rgb_for(to)
303
+ @rgb_from, @rgb_to = rgb(from), rgb(to)
207
304
  @fade = [@rgb_from]
208
305
  (1..(steps-1)).each { |i| @fade << rgb_between(@rgb_from, @rgb_to, i / steps.to_f) }
209
306
  @fade << @rgb_to
@@ -226,10 +323,7 @@ module CheekyDreams
226
323
  end
227
324
 
228
325
  def next current_colour
229
- unless @fade
230
- @fade = Fade.new(current_colour, @to, @steps, freq)
231
- @fade.next current_colour
232
- end
326
+ @fade = Fade.new(current_colour, @to, @steps, freq) unless @fade
233
327
  @fade.next current_colour
234
328
  end
235
329
  end
@@ -238,85 +332,59 @@ end
238
332
 
239
333
  class Light
240
334
 
241
- include CheekyDreams
242
-
243
335
  attr_accessor :freq, :auditor
244
336
 
337
+ include CheekyDreams
338
+
245
339
  def initialize driver
246
- @driver, @freq, @auditor = driver, 100, dev_null_auditor
247
- @lock = Mutex.new
248
- @effect = nil
340
+ @driver, @freq, @auditor, @effect = driver, 50, dev_null, solid(:off)
341
+ @lock, @wake_up = Mutex.new, ConditionVariable.new
249
342
  @on = false
250
343
  end
251
344
 
252
- def cycle colours, freq = 1
253
- go(Effect::Cycle.new(colours, freq))
254
- end
255
-
256
- def solid colour
257
- go(Effect::Solid.new(colour))
258
- end
259
-
260
- def fade from, to, steps = 10, freq = 1
261
- go Effect::Fade.new from, to, steps, freq
262
- end
263
-
264
- def fade_to to, steps = 10, freq = 1
265
- go Effect::FadeTo.new to, steps, freq
266
- end
267
-
268
- def func freq = 1, &block
269
- go Effect::Func.new freq, &block
270
- end
271
-
272
- def throb freq, amplitude, centre
273
- go Effect::Throb.new freq, amplitude, centre
274
- end
275
-
276
345
  def go effect
277
346
  @lock.synchronize {
278
347
  case effect
279
348
  when Symbol
280
- @effect = Effect::Solid.new(effect)
349
+ @effect = solid(effect)
281
350
  when Array
282
- @effect = Effect::Solid.new(effect)
283
- when Effect::Effect
351
+ @effect = solid(effect)
352
+ when CheekyDreams::Effects::Effect
284
353
  @effect = effect
285
354
  else
286
355
  raise "Im sorry dave, I'm afraid I can't do that. #{effect}"
287
356
  end
288
357
  }
358
+ wakeup
289
359
  turn_on unless @on
290
360
  end
291
361
 
362
+ def off
363
+ @on = false
364
+ end
365
+
292
366
  private
293
367
  def turn_on
294
- @on = true
295
- Thread.new do
296
- current_effect = nil
297
- last_colour = nil
298
- next_colour_time = nil
368
+ @on, current_effect = true, nil
369
+ @run_thread = Thread.new do
370
+ last_colour = COLOURS[:off]
299
371
  while @on
372
+ start = Time.now
373
+ @lock.synchronize { current_effect = @effect }
300
374
  begin
301
- @lock.synchronize {
302
- if @effect && current_effect != @effect
303
- current_effect = @effect
304
- next_colour_time = Time.at(0)
305
- end
306
- }
307
- if current_effect
308
- if Time.now > next_colour_time
309
- new_colour = current_effect.next(last_colour)
310
- @driver.go new_colour
311
- last_colour = new_colour
312
- next_colour_time = Time.now + (1 / current_effect.freq.to_f)
313
- end
314
- end
375
+ new_colour = current_effect.next last_colour
376
+ @driver.go new_colour
377
+ @auditor.audit :colour_change, new_colour.to_s
378
+ last_colour = new_colour
315
379
  rescue => e
316
- auditor.unhandled_error e
380
+ auditor.audit :error, e.message
317
381
  end
318
- sleep (1 / freq.to_f)
319
- end
382
+ sleep_until (start + (1 / current_effect.freq.to_f))
383
+ end
320
384
  end
321
385
  end
386
+
387
+ def wakeup
388
+ @run_thread.run if @run_thread
389
+ end
322
390
  end