cheeky-pi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,90 @@
1
+ # CheekyPi
2
+
3
+ EventMachine friendly library for working with the DreamCheeky Webmail Notifier.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cheeky-pi'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cheeky-pi
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'cheeky-pi'
23
+ require 'eventmachine'
24
+
25
+ light = CheekyPi.new.light
26
+
27
+ EventMachine.run {
28
+ pi.light.color([0, 255, 0])
29
+ }
30
+ ```
31
+
32
+ ### Multiple lights:
33
+
34
+ ```ruby
35
+ light1 = CheekyPi.lights[0]
36
+ light2 = CheekyPi.lights[1]
37
+ ...
38
+ light1.failure
39
+ light2.success
40
+ ```
41
+
42
+ ### Simple animation
43
+
44
+ ```ruby
45
+ CheekyPi.new.light.transition! [255, 0, 255]
46
+ ```
47
+
48
+ ### Throb animation
49
+
50
+ ```ruby
51
+ #Perpetual throb animation, with a 10 second cycle period
52
+ CheekyPi.new.light.throb! [0, 255, 0], [0, 0, 255], 10
53
+ ```
54
+
55
+ ##$ Fun with promises
56
+
57
+ ```ruby
58
+ light = CheekyPi.new.light
59
+ light.transition!(255, 0, 255).then {
60
+ puts 'animation complete'
61
+ light.throb! 10, [255, 0, 255], [255, 255, 255]
62
+ }
63
+ ```
64
+
65
+ ### Build lights
66
+
67
+ ```ruby
68
+ case some_build_status
69
+ when :build
70
+ light.building
71
+ when :failed
72
+ light.failure
73
+ when :successful
74
+ light.success
75
+ else
76
+ light.off
77
+ end
78
+ ```
79
+
80
+ ## Related gems:
81
+
82
+ * [blinky](https://github.com/perryn/blinky)
83
+ * [cheeky_dreams](https://github.com/simojenki/cheeky-dreams)
84
+
85
+ ## Why?
86
+
87
+ My raspberry pi could not interact with the webmail notifier via libusb, so this gem depends instead on
88
+ `/sys/devices/*/red|green|blue` courtesy of the usbled driver.
89
+
90
+ Additionally, I wanted (non-blocking) animation for use with a build monitoring tool.
@@ -0,0 +1,35 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ require 'cheeky-pi/version'
3
+ require 'cheeky-pi/factory'
4
+
5
+ module CheekyPi
6
+ COLOR = {
7
+ off: [0, 0, 0],
8
+ black: [0, 0, 0],
9
+ red: [255, 0, 0],
10
+ green: [0, 255, 0],
11
+ blue: [0, 0, 255],
12
+ yellow: [255,255,0],
13
+ aqua: [0,255,255],
14
+ purple: [255,0,255],
15
+ white: [255,255,255],
16
+ }
17
+
18
+ def self.new
19
+ CheekyPi.new
20
+ end
21
+
22
+ class CheekyPi
23
+ def initialize
24
+ @lights = Factory.find_lights
25
+ end
26
+
27
+ def lights
28
+ @lights
29
+ end
30
+
31
+ def light
32
+ @lights.first
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,77 @@
1
+ require 'eventmachine'
2
+ require 'em-promise'
3
+
4
+ module CheekyPi
5
+ module Animation
6
+
7
+ class Base
8
+
9
+ TICKRATE = 0.1
10
+
11
+ def initialize(duration, light, options={})
12
+ @duration = duration
13
+ @light = light
14
+
15
+ @deferred = EventMachine::Q.defer
16
+ promise.then { handle_complete }
17
+
18
+ start
19
+ end
20
+
21
+ def handle_complete
22
+ end
23
+
24
+ def promise
25
+ @promise ||= @deferred.promise
26
+ end
27
+
28
+ def start
29
+ @timer.cancel if @timer
30
+ @start_time = Time.now.to_f
31
+ @timer = EventMachine.add_periodic_timer(TICKRATE) { tick }
32
+
33
+ self
34
+ end
35
+
36
+ def percent
37
+ return 0 if @duration.nil?
38
+ (Time.now.to_f - @start_time) / @duration
39
+ end
40
+
41
+ def safe_percent
42
+ [percent, 1].min
43
+ end
44
+
45
+ def halt
46
+ @timer.cancel
47
+ @deferred.reject
48
+
49
+ self
50
+ end
51
+
52
+ def complete
53
+ @timer.cancel
54
+ @deferred.resolve
55
+
56
+ self
57
+ end
58
+
59
+ def tick
60
+ complete if percent >=1
61
+ animate
62
+ end
63
+
64
+ def animate
65
+ end
66
+
67
+ #convert [0-0.5-1] to [0-1-0]
68
+ def triangle(percent)
69
+ (0.5 - (percent % 1 - 0.5).abs) * 2
70
+ end
71
+
72
+ def sine(percent)
73
+ Math::sin(percent * 2 * Math::PI - 0.5 * Math::PI) * 0.5 + 0.5
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,20 @@
1
+ require 'cheeky-pi/animation/base'
2
+
3
+ module CheekyPi
4
+ module Animation
5
+ class Cycle < Base
6
+
7
+ def initialize(duration, light, options={})
8
+ super
9
+
10
+ @to = options[:to]
11
+ @from = options[:from]
12
+ @period = options[:period]
13
+ end
14
+
15
+ def cycle_percent
16
+ ((Time.now.to_f - @start_time) % @period) / @period
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'cheeky-pi/animation/base'
2
+
3
+ module CheekyPi
4
+ module Animation
5
+ class Fade < Base
6
+
7
+ def initialize(duration, light, options={})
8
+ super
9
+
10
+ @to = options[:to]
11
+ @from = options[:from]
12
+ end
13
+
14
+ def handle_complete
15
+ @light.set @to
16
+ end
17
+
18
+ def animate
19
+ @light.set(@from.blend(@to, safe_percent))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require 'cheeky-pi/animation/cycle'
2
+
3
+ module CheekyPi
4
+ module Animation
5
+ class Throb < Cycle
6
+
7
+ def initialize(duration, light, options={})
8
+ super
9
+
10
+ @to = options[:to]
11
+ @from = options[:from]
12
+ @wave = options[:type] || :triangle
13
+ end
14
+
15
+
16
+ def animate
17
+ blend_percent = send(@wave, cycle_percent)
18
+ @light.set(@from.blend(@to, blend_percent))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ module CheekyPi
2
+ class Color < Struct.new(:r, :g, :b)
3
+
4
+ # Lighten color by percent
5
+ def lighten(percent)
6
+ blend(self.class.create(COLOR[:white]), percent)
7
+ end
8
+
9
+ # Darken a color by a percent
10
+ def darken(percent)
11
+ blend(self.class.create(COLOR[:off]), percent)
12
+ end
13
+
14
+ # Returns color distance (maximum of rgb distances individually) from a target color
15
+ def distance(color)
16
+ [(self.r - color.r).abs, (self.g - color.g).abs, (self.b - color.b).abs].max
17
+ end
18
+
19
+ # Blend with another color
20
+ def blend(color, opacity)
21
+ self.class.new(
22
+ self.r * (1 - opacity) + color.r * opacity,
23
+ self.g * (1 - opacity) + color.g * opacity,
24
+ self.b * (1 - opacity) + color.b * opacity
25
+ )
26
+ end
27
+
28
+ # Create a new color from [r,g,b] array
29
+ def self.create(rgb)
30
+ new *rgb
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,10 @@
1
+ require 'cheeky-pi/light'
2
+
3
+ module CheekyPi
4
+ module Factory
5
+ def self.find_lights
6
+ reds = Dir.glob('/sys/devices/**/red')
7
+ reds.collect {|red| Light.new(File.dirname(red)) }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,99 @@
1
+ require 'cheeky-pi/light_interface'
2
+ require 'cheeky-pi/color'
3
+ require 'cheeky-pi/animation/fade'
4
+ require 'cheeky-pi/animation/throb'
5
+
6
+ module CheekyPi
7
+ class Light
8
+ MAX = 90
9
+
10
+ # Initialize with a path and optional color
11
+ def initialize(path)
12
+ @light = LightInterface.new(path)
13
+ @last_color = @light.fetch
14
+ end
15
+
16
+ def animation
17
+ @animation
18
+ end
19
+
20
+ def animation=(new_animation)
21
+ stop_animation
22
+ @animation = new_animation
23
+ end
24
+
25
+ def stop_animation
26
+ self.animation.halt if self.animation
27
+ end
28
+
29
+ def building
30
+ start_color = [6, 6, 25]
31
+ end_color = [6, 6, MAX]
32
+
33
+ transition!(start_color).then do
34
+ throb!(3, start_color, end_color)
35
+ end
36
+ end
37
+
38
+ def failure
39
+ start_color = [30, 0, 0]
40
+ end_color = [MAX, 30, 0]
41
+
42
+ transition!(start_color).then do
43
+ throb!(1, start_color, end_color)
44
+ end
45
+ end
46
+
47
+ def success
48
+ transition!([0, MAX, 0])
49
+ end
50
+
51
+ def off
52
+ transition!(COLOR[:black])
53
+ end
54
+
55
+ def throb (period, from, to)
56
+ Animation::Throb.new nil, self, {
57
+ from: Color.create(from),
58
+ to: Color.create(to),
59
+ period: period,
60
+ type: :sine
61
+ }
62
+ end
63
+
64
+ def throb! (period, from, to)
65
+ self.animation = throb(period, from, to)
66
+
67
+ self.animation.promise
68
+ end
69
+
70
+
71
+ def transition(to, duration=2)
72
+ to = Color.create(to)
73
+
74
+ #Set color transition time based on distance, with a maximum of 2 seconds
75
+ #(number from 0-255)
76
+ distance = @last_color.distance(to)
77
+ seconds = duration * [distance, MAX].max / MAX
78
+
79
+ Animation::Fade.new seconds, self, { from: @last_color, to: to }
80
+ end
81
+
82
+
83
+ def transition!(to)
84
+ self.animation = transition to
85
+
86
+ self.animation.promise
87
+ end
88
+
89
+ def color
90
+ set Color.create(color)
91
+ end
92
+
93
+ # Set light to a given color
94
+ def set(color)
95
+ @last_color = color
96
+ @light.set(color)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,26 @@
1
+ require 'cheeky-pi/color'
2
+
3
+ module CheekyPi
4
+ class LightInterface
5
+ def initialize(path)
6
+ @r = "#{path}/red"
7
+ @g = "#{path}/green"
8
+ @b = "#{path}/blue"
9
+ end
10
+
11
+ def fetch
12
+ r = IO.read(@r).to_f
13
+ g = IO.read(@g).to_f
14
+ b = IO.read(@b).to_f
15
+
16
+ Color.create [r, g, b]
17
+ end
18
+
19
+
20
+ def set(color)
21
+ IO.write(@r, color.r.round)
22
+ IO.write(@g, color.g.round)
23
+ IO.write(@b, color.b.round)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module CheekyPi
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cheeky-pi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Billiam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: em-promise
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.1.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.1.1
46
+ description: Simple usbled ruby library
47
+ email: billiamthesecond@gmail.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/cheeky-pi/animation/base.rb
53
+ - lib/cheeky-pi/animation/cycle.rb
54
+ - lib/cheeky-pi/animation/fade.rb
55
+ - lib/cheeky-pi/animation/throb.rb
56
+ - lib/cheeky-pi/color.rb
57
+ - lib/cheeky-pi/factory.rb
58
+ - lib/cheeky-pi/light.rb
59
+ - lib/cheeky-pi/light_interface.rb
60
+ - lib/cheeky-pi/version.rb
61
+ - lib/cheeky-pi.rb
62
+ - README.md
63
+ homepage: http://github.com/billiam/cheeeky-pi
64
+ licenses: []
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.24
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: PI led helper
87
+ test_files: []