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.
- data/README.md +90 -0
- data/lib/cheeky-pi.rb +35 -0
- data/lib/cheeky-pi/animation/base.rb +77 -0
- data/lib/cheeky-pi/animation/cycle.rb +20 -0
- data/lib/cheeky-pi/animation/fade.rb +23 -0
- data/lib/cheeky-pi/animation/throb.rb +22 -0
- data/lib/cheeky-pi/color.rb +34 -0
- data/lib/cheeky-pi/factory.rb +10 -0
- data/lib/cheeky-pi/light.rb +99 -0
- data/lib/cheeky-pi/light_interface.rb +26 -0
- data/lib/cheeky-pi/version.rb +3 -0
- metadata +87 -0
data/README.md
ADDED
@@ -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.
|
data/lib/cheeky-pi.rb
ADDED
@@ -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,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
|
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: []
|