usblamp 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.md +674 -0
- data/MANIFEST +24 -0
- data/README.md +60 -0
- data/Rakefile +10 -0
- data/bin/usblamp +5 -0
- data/lib/usblamp/cli/blink.rb +20 -0
- data/lib/usblamp/cli/fadein.rb +20 -0
- data/lib/usblamp/cli.rb +57 -0
- data/lib/usblamp/color.rb +67 -0
- data/lib/usblamp/controller.rb +110 -0
- data/lib/usblamp/version.rb +5 -0
- data/lib/usblamp.rb +15 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/usblamp/cli_spec.rb +44 -0
- data/spec/usblamp/color_spec.rb +90 -0
- data/spec/usblamp/controller_spec.rb +152 -0
- data/task/manifest.rake +9 -0
- data/task/rspec.rake +6 -0
- data/task/rubocop.rake +5 -0
- data/usblamp.gemspec +27 -0
- metadata +153 -0
data/MANIFEST
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
.gitignore
|
2
|
+
.rubocop.yml
|
3
|
+
.travis.yml
|
4
|
+
Gemfile
|
5
|
+
LICENSE.md
|
6
|
+
MANIFEST
|
7
|
+
README.md
|
8
|
+
Rakefile
|
9
|
+
bin/usblamp
|
10
|
+
lib/usblamp.rb
|
11
|
+
lib/usblamp/cli.rb
|
12
|
+
lib/usblamp/cli/blink.rb
|
13
|
+
lib/usblamp/cli/fadein.rb
|
14
|
+
lib/usblamp/color.rb
|
15
|
+
lib/usblamp/controller.rb
|
16
|
+
lib/usblamp/version.rb
|
17
|
+
spec/spec_helper.rb
|
18
|
+
spec/usblamp/cli_spec.rb
|
19
|
+
spec/usblamp/color_spec.rb
|
20
|
+
spec/usblamp/controller_spec.rb
|
21
|
+
task/manifest.rake
|
22
|
+
task/rspec.rake
|
23
|
+
task/rubocop.rake
|
24
|
+
usblamp.gemspec
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#WebMail Notifier with Ruby (Dream Cheeky)
|
2
|
+
|
3
|
+
[](https://travis-ci.org/PierreRambaud/usblamp)
|
4
|
+
|
5
|
+
Ruby script to power the Dreamcheeky USB webmail notifier gadget. <http://www.dreamcheeky.com/webmail-notifier>
|
6
|
+
|
7
|
+
##Requirements
|
8
|
+
|
9
|
+
- Ruby 1.9.2 or newer
|
10
|
+
|
11
|
+
##Installation
|
12
|
+
|
13
|
+
Assuming RubyGems isn't down you can install the Gem as following:
|
14
|
+
|
15
|
+
```
|
16
|
+
$ gem install usblamp
|
17
|
+
```
|
18
|
+
|
19
|
+
##Usage
|
20
|
+
|
21
|
+
```
|
22
|
+
Usage: usblamp [COMMAND] [OPTIONS]
|
23
|
+
|
24
|
+
Options:
|
25
|
+
|
26
|
+
-v, --version Shows the current version
|
27
|
+
-r, --red Red
|
28
|
+
-g, --green Green
|
29
|
+
-b, --blue Blue
|
30
|
+
-c, --color Color
|
31
|
+
-h, --help Display this help message.
|
32
|
+
|
33
|
+
Available commands:
|
34
|
+
|
35
|
+
fadein Fade in effect
|
36
|
+
blink Blink effect
|
37
|
+
```
|
38
|
+
|
39
|
+
##Troubleshooting
|
40
|
+
|
41
|
+
Should be run as root unless the necessary udev rules are set.
|
42
|
+
Create the file `/etc/udev/rules.d/42-usblamp.rules`
|
43
|
+
And add this content by replacing `got` by your username:
|
44
|
+
```
|
45
|
+
SUBSYSTEM !="usb_device", ACTION !="add", GOTO="datalogger_rules_end"
|
46
|
+
SYSFS{idVendor} =="1d34", SYSFS{idProduct} =="0004", SYMLINK+="datalogger"
|
47
|
+
MODE="0666", OWNER="got", GROUP="root"
|
48
|
+
LABEL="datalogger_rules_end"
|
49
|
+
```
|
50
|
+
|
51
|
+
## Running tests
|
52
|
+
|
53
|
+
Install dependencies:
|
54
|
+
|
55
|
+
`$ bundle install`
|
56
|
+
|
57
|
+
To run tests:
|
58
|
+
`$ bundle exec rake`
|
59
|
+
|
60
|
+
|
data/Rakefile
ADDED
data/bin/usblamp
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
Usblamp::CLI.slop.command 'blink' do
|
3
|
+
banner 'Usage: usblamp blink [OPTIONS]'
|
4
|
+
description 'Blink effect'
|
5
|
+
separator "\nOptions:\n"
|
6
|
+
|
7
|
+
Usblamp::CLI.slop.options.each do |c|
|
8
|
+
next if c.long == 'version'
|
9
|
+
options << c
|
10
|
+
end
|
11
|
+
|
12
|
+
on :t=, :times=, 'Times', as: Integer, default: 2
|
13
|
+
|
14
|
+
run do |opts, _args|
|
15
|
+
lamp = Usblamp::Controller.new
|
16
|
+
lamp.open
|
17
|
+
lamp.switch_off
|
18
|
+
lamp.blink(opts[:t], lamp.parse(opts))
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
Usblamp::CLI.slop.command 'fadein' do
|
3
|
+
banner 'Usage: usblamp fadein [OPTIONS]'
|
4
|
+
description 'Fade in effect'
|
5
|
+
separator "\nOptions:\n"
|
6
|
+
|
7
|
+
Usblamp::CLI.slop.options.each do |c|
|
8
|
+
next if c.long == 'version'
|
9
|
+
options << c
|
10
|
+
end
|
11
|
+
|
12
|
+
on :d=, :delay=, 'Delay', as: Integer, default: 2
|
13
|
+
|
14
|
+
run do |opts, _args|
|
15
|
+
lamp = Usblamp::Controller.new
|
16
|
+
lamp.open
|
17
|
+
lamp.switch_off
|
18
|
+
lamp.fade_in(opts[:d], lamp.parse(opts))
|
19
|
+
end
|
20
|
+
end
|
data/lib/usblamp/cli.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Usblamp
|
3
|
+
# CLI mode
|
4
|
+
module CLI
|
5
|
+
##
|
6
|
+
# Hash containing the default Slop options.
|
7
|
+
#
|
8
|
+
# @return [Hash]
|
9
|
+
#
|
10
|
+
SLOP_OPTIONS = {
|
11
|
+
strict: true,
|
12
|
+
help: true,
|
13
|
+
banner: 'Usage: usblamp [COMMAND] [OPTIONS]'
|
14
|
+
}
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Slop]
|
18
|
+
#
|
19
|
+
def self.slop
|
20
|
+
@slop ||= default_slop
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @return [Slop]
|
25
|
+
#
|
26
|
+
def self.default_slop
|
27
|
+
Slop.new(SLOP_OPTIONS.dup) do
|
28
|
+
separator "\nOptions:\n"
|
29
|
+
|
30
|
+
on :v, :version, 'Shows the current version' do
|
31
|
+
puts CLI.version_information
|
32
|
+
end
|
33
|
+
|
34
|
+
on :r=, :red=, 'Red', as: Integer
|
35
|
+
on :g=, :green=, 'Green', as: Integer
|
36
|
+
on :b=, :blue=, 'Blue', as: Integer
|
37
|
+
on :c=, :color=, 'Color'
|
38
|
+
|
39
|
+
run do |opts, _args|
|
40
|
+
lamp = Controller.new
|
41
|
+
lamp.open
|
42
|
+
lamp.switch_off
|
43
|
+
lamp.send_color(lamp.parse(opts))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Returns a String containing some platform/version related information.
|
50
|
+
#
|
51
|
+
# @return [String]
|
52
|
+
#
|
53
|
+
def self.version_information
|
54
|
+
"usblamp v#{VERSION} on #{RUBY_DESCRIPTION}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Usblamp module
|
3
|
+
module Usblamp
|
4
|
+
# Color class
|
5
|
+
class Color
|
6
|
+
attr_accessor :red, :green, :blue
|
7
|
+
MAX_VALUE = 0x40
|
8
|
+
|
9
|
+
def initialize(red = nil, green = nil, blue = nil)
|
10
|
+
set(red, green, blue)
|
11
|
+
end
|
12
|
+
|
13
|
+
def set(red, green = nil, blue = nil)
|
14
|
+
if red.is_a?(String) && green.nil? && blue.nil?
|
15
|
+
if ['#', '_'].include?(red[0])
|
16
|
+
return define_from_hex(red)
|
17
|
+
else
|
18
|
+
return define_from_string(red)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
red = 0 if red.nil?
|
23
|
+
green = 0 if green.nil?
|
24
|
+
blue = 0 if blue.nil?
|
25
|
+
|
26
|
+
@red = define_value(red)
|
27
|
+
@green = define_value(green)
|
28
|
+
@blue = define_value(blue)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def define_value(integer)
|
34
|
+
[[0, integer.to_i].max, MAX_VALUE].min
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_from_hex(string)
|
38
|
+
string = string.gsub(/[#_]/, '')
|
39
|
+
fail ArgumentError, 'Wrong length for hexadecimal string' unless
|
40
|
+
[3, 6].include?(string.length.to_i)
|
41
|
+
|
42
|
+
return set(string[0..1].to_i(16),
|
43
|
+
string[2..3].to_i(16),
|
44
|
+
string[4..5].to_i(16)
|
45
|
+
) if string.length == 6
|
46
|
+
|
47
|
+
set((string[0] * 2).to_i(16),
|
48
|
+
(string[1] * 2).to_i(16),
|
49
|
+
(string[2] * 2).to_i(16)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def define_from_string(string)
|
54
|
+
return set(MAX_VALUE, 0, 0) if string == 'red'
|
55
|
+
return set(0, MAX_VALUE, 0) if string == 'green'
|
56
|
+
return set(0, 0, MAX_VALUE) if string == 'blue'
|
57
|
+
return set(MAX_VALUE, MAX_VALUE, MAX_VALUE) if string == 'white'
|
58
|
+
return set(MAX_VALUE, 0, MAX_VALUE) if string == 'magenta'
|
59
|
+
return set(0, MAX_VALUE, MAX_VALUE) if string == 'cyan'
|
60
|
+
return set(MAX_VALUE, MAX_VALUE, 0) if string == 'yellow'
|
61
|
+
return set(MAX_VALUE / 2, MAX_VALUE / 2, MAX_VALUE / 2) if
|
62
|
+
string == 'purple'
|
63
|
+
|
64
|
+
set(0, 0, 0)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Usblamp module
|
3
|
+
module Usblamp
|
4
|
+
# Controller class
|
5
|
+
class Controller
|
6
|
+
attr_accessor :device, :color
|
7
|
+
|
8
|
+
VENDOR_ID = 0x1d34
|
9
|
+
PRODUCT_ID = 0x0004
|
10
|
+
INIT_PACKET1 = [0x1F, 0x01, 0x29, 0x00, 0xB8, 0x54, 0x2C, 0x03]
|
11
|
+
INIT_PACKET2 = [0x00, 0x01, 0x29, 0x00, 0xB8, 0x54, 0x2C, 0x04]
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@device = nil
|
15
|
+
@color = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def open
|
19
|
+
usb = LIBUSB::Context.new
|
20
|
+
device = usb.devices(idVendor: VENDOR_ID, idProduct: PRODUCT_ID).first
|
21
|
+
|
22
|
+
fail 'Could no find a supported device.' if device.nil?
|
23
|
+
@device = device.open
|
24
|
+
@device.detach_kernel_driver(0) if @device.kernel_driver_active?(0)
|
25
|
+
@device.claim_interface(0)
|
26
|
+
send(INIT_PACKET1)
|
27
|
+
send(INIT_PACKET2)
|
28
|
+
end
|
29
|
+
|
30
|
+
def send(data)
|
31
|
+
return true if @device
|
32
|
+
.control_transfer(
|
33
|
+
bmRequestType: LIBUSB::REQUEST_TYPE_CLASS |
|
34
|
+
LIBUSB::RECIPIENT_INTERFACE,
|
35
|
+
bRequest: LIBUSB::REQUEST_SET_CONFIGURATION,
|
36
|
+
wValue: 0x81,
|
37
|
+
wIndex: LIBUSB::ENDPOINT_OUT,
|
38
|
+
dataOut: data.map(&:chr).reduce(:+),
|
39
|
+
timeout: 10) == 8
|
40
|
+
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_color(color)
|
45
|
+
fail TypeError, 'Parameter must be an instance of Color' unless
|
46
|
+
color.is_a?(Color)
|
47
|
+
|
48
|
+
@color = color
|
49
|
+
data = [color.red,
|
50
|
+
color.green,
|
51
|
+
color.blue,
|
52
|
+
0x00,
|
53
|
+
0x00,
|
54
|
+
0x00,
|
55
|
+
0x00,
|
56
|
+
0x05]
|
57
|
+
send(data)
|
58
|
+
end
|
59
|
+
|
60
|
+
def switch_off
|
61
|
+
send_color(Color.new('black'))
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(args)
|
65
|
+
color = Color.new(args[:c]) unless args[:c].nil?
|
66
|
+
color = Color.new(args[:r], args[:g], args[:b]) if args[:c].nil?
|
67
|
+
color
|
68
|
+
end
|
69
|
+
|
70
|
+
def fade_in(delay, new_color)
|
71
|
+
delay = delay.to_i
|
72
|
+
color = Color.new
|
73
|
+
max_value = [new_color.red, new_color.green, new_color.blue].max
|
74
|
+
(0..max_value).each do |i|
|
75
|
+
sleep((delay.to_f * 1000.to_f / max_value.to_f + 1.to_f) / 1000.to_f)
|
76
|
+
color.red = transition(i,
|
77
|
+
@color.red,
|
78
|
+
new_color.red,
|
79
|
+
max_value)
|
80
|
+
color.green = transition(i,
|
81
|
+
@color.green,
|
82
|
+
new_color.green,
|
83
|
+
max_value)
|
84
|
+
color.blue = transition(i,
|
85
|
+
@color.blue,
|
86
|
+
new_color.blue,
|
87
|
+
max_value)
|
88
|
+
send_color(color)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def blink(times, new_color)
|
93
|
+
(1..times).each do
|
94
|
+
send_color(new_color)
|
95
|
+
sleep 0.5
|
96
|
+
switch_off
|
97
|
+
sleep 0.5
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def transition(index, start_point, end_point, maximum)
|
104
|
+
(((start_point.to_f + (end_point.to_f - start_point.to_f)) *
|
105
|
+
(index.to_f + 1.to_f)
|
106
|
+
) / maximum.to_f
|
107
|
+
).to_i
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/usblamp.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'slop'
|
3
|
+
require 'libusb'
|
4
|
+
|
5
|
+
unless $LOAD_PATH.include?(File.expand_path('../', __FILE__))
|
6
|
+
$LOAD_PATH.unshift(File.expand_path('../', __FILE__))
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'usblamp/color'
|
10
|
+
require 'usblamp/controller'
|
11
|
+
require 'usblamp/version'
|
12
|
+
|
13
|
+
require 'usblamp/cli'
|
14
|
+
require 'usblamp/cli/fadein'
|
15
|
+
require 'usblamp/cli/blink'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'usblamp/cli'
|
4
|
+
require 'slop'
|
5
|
+
|
6
|
+
# Usblamp tests
|
7
|
+
module Usblamp
|
8
|
+
# CLI tests
|
9
|
+
module CLI
|
10
|
+
describe 'CLI' do
|
11
|
+
it 'should return options' do
|
12
|
+
options = CLI.slop
|
13
|
+
expect(options).to be_a(::Slop)
|
14
|
+
expect(options.config[:strict]).to be_truthy
|
15
|
+
expect(options.config[:banner])
|
16
|
+
.to eq('Usage: usblamp [COMMAND] [OPTIONS]')
|
17
|
+
expect(options.to_s)
|
18
|
+
.to match(/-v, --version(\s+)Shows the current version/)
|
19
|
+
expect(options.to_s)
|
20
|
+
.to match(/-h, --help(\s+)Display this help message./)
|
21
|
+
|
22
|
+
version = options.fetch_option(:v)
|
23
|
+
expect(version.short).to eq('v')
|
24
|
+
expect(version.long).to eq('version')
|
25
|
+
expect { version.call }.to output(/usblamp v.* on ruby/).to_stdout
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should retrieve version information' do
|
29
|
+
expect(CLI.version_information).to eq(
|
30
|
+
"usblamp v#{VERSION} on #{RUBY_DESCRIPTION}"
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should change color' do
|
35
|
+
controller = double(open: true,
|
36
|
+
switch_off: true,
|
37
|
+
parse: { c: 'blue' },
|
38
|
+
send_color: true)
|
39
|
+
allow(Controller).to receive(:new).and_return(controller)
|
40
|
+
expect(CLI.slop.parse(c: 'blue')).to eq(c: 'blue')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'usblamp/color'
|
4
|
+
|
5
|
+
# Usblamp module
|
6
|
+
module Usblamp
|
7
|
+
describe 'Color' do
|
8
|
+
before(:each) do
|
9
|
+
@color = Color.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should test initialize with color' do
|
13
|
+
color = Color.new(10, 11, 12)
|
14
|
+
expect(color.red).to eq(10)
|
15
|
+
expect(color.green).to eq(11)
|
16
|
+
expect(color.blue).to eq(12)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should test const' do
|
20
|
+
expect(Color::MAX_VALUE).to eq(0x40)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should set' do
|
24
|
+
@color.set(10, 11, 12)
|
25
|
+
expect(@color.red).to eq(10)
|
26
|
+
expect(@color.green).to eq(11)
|
27
|
+
expect(@color.blue).to eq(12)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should set from hex' do
|
31
|
+
@color.set('#112233')
|
32
|
+
expect(@color.red).to eq(17)
|
33
|
+
expect(@color.green).to eq(34)
|
34
|
+
expect(@color.blue).to eq(51)
|
35
|
+
@color.set('_112233')
|
36
|
+
expect(@color.red).to eq(17)
|
37
|
+
expect(@color.green).to eq(34)
|
38
|
+
expect(@color.blue).to eq(51)
|
39
|
+
@color.set('#123')
|
40
|
+
expect(@color.red).to eq(17)
|
41
|
+
expect(@color.green).to eq(34)
|
42
|
+
expect(@color.blue).to eq(51)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should set from string' do
|
46
|
+
@color.set('red')
|
47
|
+
expect(@color.red).to eq(64)
|
48
|
+
expect(@color.green).to eq(0)
|
49
|
+
expect(@color.blue).to eq(0)
|
50
|
+
@color.set('green')
|
51
|
+
expect(@color.red).to eq(0)
|
52
|
+
expect(@color.green).to eq(64)
|
53
|
+
expect(@color.blue).to eq(0)
|
54
|
+
@color.set('blue')
|
55
|
+
expect(@color.red).to eq(0)
|
56
|
+
expect(@color.green).to eq(0)
|
57
|
+
expect(@color.blue).to eq(64)
|
58
|
+
@color.set('white')
|
59
|
+
expect(@color.red).to eq(64)
|
60
|
+
expect(@color.green).to eq(64)
|
61
|
+
expect(@color.blue).to eq(64)
|
62
|
+
@color.set('magenta')
|
63
|
+
expect(@color.red).to eq(64)
|
64
|
+
expect(@color.green).to eq(0)
|
65
|
+
expect(@color.blue).to eq(64)
|
66
|
+
@color.set('cyan')
|
67
|
+
expect(@color.red).to eq(0)
|
68
|
+
expect(@color.green).to eq(64)
|
69
|
+
expect(@color.blue).to eq(64)
|
70
|
+
@color.set('yellow')
|
71
|
+
expect(@color.red).to eq(64)
|
72
|
+
expect(@color.green).to eq(64)
|
73
|
+
expect(@color.blue).to eq(0)
|
74
|
+
@color.set('purple')
|
75
|
+
expect(@color.red).to eq(32)
|
76
|
+
expect(@color.green).to eq(32)
|
77
|
+
expect(@color.blue).to eq(32)
|
78
|
+
@color.set('nothing')
|
79
|
+
expect(@color.red).to eq(0)
|
80
|
+
expect(@color.green).to eq(0)
|
81
|
+
expect(@color.blue).to eq(0)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should raise error when undefined color' do
|
85
|
+
expect { @color.set('#FF') }.to raise_error ArgumentError
|
86
|
+
expect { @color.set('#FFFF') }.to raise_error ArgumentError
|
87
|
+
expect { @color.set('#FFFFF') }.to raise_error ArgumentError
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'libusb'
|
4
|
+
require 'usblamp/controller'
|
5
|
+
|
6
|
+
# Usblamp module
|
7
|
+
module Usblamp
|
8
|
+
describe 'Controller' do
|
9
|
+
before(:each) do
|
10
|
+
@controller = Controller.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should test init' do
|
14
|
+
expect(@controller.device).to be(nil)
|
15
|
+
expect(@controller.color).to be(nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should test const' do
|
19
|
+
expect(Controller::VENDOR_ID).to eq(0x1d34)
|
20
|
+
expect(Controller::PRODUCT_ID).to eq(0x0004)
|
21
|
+
expect(Controller::INIT_PACKET1)
|
22
|
+
.to eq([31, 1, 41, 0, 184, 84, 44, 3])
|
23
|
+
expect(Controller::INIT_PACKET2)
|
24
|
+
.to eq([0, 1, 41, 0, 184, 84, 44, 4])
|
25
|
+
expect(@controller.device).to eq(nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should raise when no supported devices found' do
|
29
|
+
device = double(devices: [])
|
30
|
+
allow(LIBUSB::Context).to receive(:new).and_return(device)
|
31
|
+
expect { @controller.open }.to raise_error(RuntimeError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should open device' do
|
35
|
+
handle = spy(open: true,
|
36
|
+
kernel_driver_active?: true,
|
37
|
+
detach_kernel_driver: true,
|
38
|
+
control_transfer: 8)
|
39
|
+
device = double(devices: [double(open: handle)])
|
40
|
+
allow(LIBUSB::Context).to receive(:new).and_return(device)
|
41
|
+
expect(@controller.open).to be_truthy
|
42
|
+
expect(handle).to have_received(:claim_interface).with(0)
|
43
|
+
expect(handle).to have_received(:control_transfer)
|
44
|
+
.with(bmRequestType: 33,
|
45
|
+
bRequest: 9,
|
46
|
+
wValue: 129,
|
47
|
+
wIndex: 0,
|
48
|
+
dataOut: [31, 1, 41, 0, 184, 84, 44, 3].map(&:chr).reduce(:+),
|
49
|
+
timeout: 10)
|
50
|
+
expect(handle).to have_received(:control_transfer)
|
51
|
+
.with(bmRequestType: 33,
|
52
|
+
bRequest: 9,
|
53
|
+
wValue: 129,
|
54
|
+
wIndex: 0,
|
55
|
+
dataOut: [0, 1, 41, 0, 184, 84, 44, 4].map(&:chr).reduce(:+),
|
56
|
+
timeout: 10)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should fail send transfer' do
|
60
|
+
device = double(control_transfer: 5)
|
61
|
+
@controller.device = device
|
62
|
+
expect(@controller.send([0])).to be_falsy
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should test send_color parameter' do
|
66
|
+
expect { @controller.send_color('fake') }
|
67
|
+
.to raise_error(TypeError, 'Parameter must be an instance of Color')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should send color' do
|
71
|
+
device = spy(control_transfer: 8)
|
72
|
+
@controller.device = device
|
73
|
+
expect(@controller.send_color(Color.new('blue'))).to be_truthy
|
74
|
+
expect(device).to have_received(:control_transfer)
|
75
|
+
.with(bmRequestType: 33,
|
76
|
+
bRequest: 9,
|
77
|
+
wValue: 129,
|
78
|
+
wIndex: 0,
|
79
|
+
dataOut: [0, 0, 64, 0, 0, 0, 0, 5].map(&:chr).reduce(:+),
|
80
|
+
timeout: 10)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should switch off' do
|
84
|
+
device = spy(control_transfer: 8)
|
85
|
+
@controller.device = device
|
86
|
+
expect(@controller.switch_off).to be_truthy
|
87
|
+
expect(device).to have_received(:control_transfer)
|
88
|
+
.with(bmRequestType: 33,
|
89
|
+
bRequest: 9,
|
90
|
+
wValue: 129,
|
91
|
+
wIndex: 0,
|
92
|
+
dataOut: "\x00\x00\x00\x00\x00\x00\x00\x05",
|
93
|
+
timeout: 10)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should parse args to create color' do
|
97
|
+
args = { c: 'red' }
|
98
|
+
result = @controller.parse(args)
|
99
|
+
expect(result).to be_a(Color)
|
100
|
+
expect(result.red).to eq(64)
|
101
|
+
expect(result.green).to eq(0)
|
102
|
+
expect(result.blue).to eq(0)
|
103
|
+
|
104
|
+
args = { r: 10, g: 10, b: 10 }
|
105
|
+
result = @controller.parse(args)
|
106
|
+
expect(result).to be_a(Color)
|
107
|
+
expect(result.red).to eq(10)
|
108
|
+
expect(result.green).to eq(10)
|
109
|
+
expect(result.blue).to eq(10)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should fade in' do
|
113
|
+
device = spy(control_transfer: 8)
|
114
|
+
@controller.color = Color.new('black')
|
115
|
+
@controller.device = device
|
116
|
+
allow(@controller).to receive(:sleep).and_return(true)
|
117
|
+
expect(@controller.fade_in(2, Color.new('blue'))).to be_truthy
|
118
|
+
(1..64).each do |i|
|
119
|
+
expect(device).to have_received(:control_transfer)
|
120
|
+
.with(bmRequestType: 33,
|
121
|
+
bRequest: 9,
|
122
|
+
wValue: 129,
|
123
|
+
wIndex: 0,
|
124
|
+
dataOut: [0, 0, i, 0, 0, 0, 0, 5].map(&:chr).reduce(:+),
|
125
|
+
timeout: 10)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should blink' do
|
130
|
+
device = spy(control_transfer: 8)
|
131
|
+
@controller.device = device
|
132
|
+
allow(@controller).to receive(:sleep).and_return(true)
|
133
|
+
expect(@controller.blink(2, Color.new('blue'))).to be_truthy
|
134
|
+
(1..2).each do
|
135
|
+
expect(device).to have_received(:control_transfer).twice
|
136
|
+
.with(bmRequestType: 33,
|
137
|
+
bRequest: 9,
|
138
|
+
wValue: 129,
|
139
|
+
wIndex: 0,
|
140
|
+
dataOut: [0, 0, 64, 0, 0, 0, 0, 5].map(&:chr).reduce(:+),
|
141
|
+
timeout: 10)
|
142
|
+
expect(device).to have_received(:control_transfer).twice
|
143
|
+
.with(bmRequestType: 33,
|
144
|
+
bRequest: 9,
|
145
|
+
wValue: 129,
|
146
|
+
wIndex: 0,
|
147
|
+
dataOut: [0, 0, 0, 0, 0, 0, 0, 5].map(&:chr).reduce(:+),
|
148
|
+
timeout: 10)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|