cheeky-pi 0.0.1

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.
@@ -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: []