integrity-nabaztag 0.0.0

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/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "integrity-nabaztag"
5
+ gemspec.summary = "A Nabaztag notifier for integrity"
6
+ gemspec.description = "A Nabaztag notifier for integrity"
7
+ gemspec.email = "daniel@collectiveidea.com"
8
+ gemspec.homepage = "http://github.com/collectiveidea/integrity-nabaztag"
9
+ gemspec.authors = ["Daniel Morrison"]
10
+ end
11
+ rescue LoadError
12
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
13
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,8 @@
1
+ %p.normal
2
+ %label{ :for => "nabaztag_mac" } MAC Address
3
+ %input.text#nabaztag_mac{ :name => "notifiers[Nabaztag][mac]", :type => "text", :value => config["mac"] }
4
+
5
+ %p.normal
6
+ %label{ :for => "nabaztab_token" } Token
7
+ %input.text#nabaztab_token{ :name => "notifiers[Nabaztag][token]", :type => "text", :value => config["token"] }
8
+
@@ -0,0 +1,37 @@
1
+ require 'integrity'
2
+ $:.unshift(File.dirname(__FILE__) + '/../../../vendor/nabaztag-ruby/lib')
3
+ require 'nabaztag'
4
+
5
+ module Integrity
6
+ class Notifier
7
+ class Nabaztag < Notifier::Base
8
+ attr_reader :config
9
+
10
+ def self.to_haml
11
+ File.read File.dirname(__FILE__) / "config.haml"
12
+ end
13
+
14
+ def deliver!
15
+ nabaztag.voice = 'US-Liberty'
16
+ nabaztag.say(message)
17
+ if commit.failed?
18
+ nabaztag.move_ears(10, 10)
19
+ else
20
+ nabaztag.move_ears(0, 0)
21
+ end
22
+ nabaztag.send
23
+ end
24
+
25
+ private
26
+ def nabaztag
27
+ @nabaztag ||= ::Nabaztag.new(config['mac'], config['token'])
28
+ end
29
+
30
+ def message
31
+ "Build of #{commit.project.name} #{commit.successful? ? "was successful" : "failed"}"
32
+ end
33
+ end
34
+
35
+ register Nabaztag
36
+ end
37
+ end
@@ -0,0 +1 @@
1
+ pkg/Nabaztag-0.3.1.gem
@@ -0,0 +1,18 @@
1
+ = Changes
2
+
3
+ == 0.3.0
4
+
5
+ * Understands new XML responses from API
6
+ * Significant refactoring of response handling
7
+
8
+ == 0.2.0
9
+
10
+ * Version 2 API support
11
+ * Can speak French or English
12
+ * Added command-line utility, nabaztag-say
13
+ * Added gem
14
+
15
+ == 0.1
16
+
17
+ * Initial release
18
+ * Version 1 API only
@@ -0,0 +1,22 @@
1
+ = Nabaztag
2
+
3
+ == About
4
+
5
+ Nabaztag communication library for Ruby.
6
+
7
+ The Nabaztag is a small electronic rabbit with lights, motorised ears, and speech.
8
+
9
+ http://www.nabaztag.com/
10
+
11
+ == Copying
12
+
13
+ Copyright Revieworld Ltd. 2006
14
+
15
+ You may use, copy and redistribute this library under the same terms as Ruby itself
16
+ (http://www.ruby-lang.org/en/LICENSE.txt).
17
+
18
+ == Installation
19
+
20
+ To install (requires root/admin privileges):
21
+
22
+ # ruby setup.rb
@@ -0,0 +1,49 @@
1
+ require "rubygems"
2
+ require "rake/gempackagetask"
3
+ require "zlib"
4
+ require "rake/clean"
5
+
6
+ CLEAN.include("doc", "pkg")
7
+
8
+ SOURCES = FileList["lib/**/*.rb"]
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = "Nabaztag"
12
+ s.version = "0.3.1"
13
+ s.author = "Paul Battley"
14
+ s.email = "paulbattley@reevoo.com"
15
+ s.summary = "Nabaztag communication library for Ruby."
16
+ s.files = SOURCES
17
+ s.require_path = "lib"
18
+ s.autorequire = "nabaztag"
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = %w[README CHANGES]
21
+ s.executables << "nabaztag-say"
22
+ end
23
+
24
+ task :default => :package
25
+
26
+ Rake::GemPackageTask.new(spec) do |pkg|
27
+ pkg.need_tar_gz = true
28
+ end
29
+
30
+ task :package => %W[
31
+ pkg/#{spec.name}-#{spec.version}.gem
32
+ pkg/#{spec.name}-#{spec.version}.tar.gz
33
+ ] do
34
+ puts "packaged version #{spec.version}"
35
+ end
36
+
37
+ file "doc" => SOURCES + spec.extra_rdoc_files do |t|
38
+ p t.prerequisites
39
+ rm_rf t.name
40
+ sh "rdoc #{t.prerequisites.join(" ")}"
41
+ end
42
+
43
+ task :tag do
44
+ unless (trunk = `svn info`[%r!URL: (.+/trunk)!, 1])
45
+ raise RuntimeError, "Couldn't determine trunk URL"
46
+ end
47
+ tag = trunk.sub(/trunk$/, "tags/#{spec.name.downcase}-#{spec.version}")
48
+ sh "svn cp #{trunk} #{tag} -m \"Automatically tagging version #{spec.version}\""
49
+ end
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'nabaztag'
4
+ require 'optparse'
5
+ load ENV['HOME'] + '/.nabaztag' rescue nil
6
+
7
+ OPTIONS = {
8
+ :mac => NABAZTAG_MAC,
9
+ :token => NABAZTAG_TOKEN,
10
+ }
11
+
12
+ class NabaztagSay
13
+ def list_voices
14
+ message = nabaztag.new_message
15
+ message.action = :get_voices
16
+ response = message.send
17
+ response.voices.each do |language, voices|
18
+ puts language
19
+ voices.sort.each do |v|
20
+ puts " #{v}"
21
+ end
22
+ end
23
+ end
24
+
25
+ def nabaztag
26
+ @mabaztag ||= Nabaztag.new(OPTIONS[:mac], OPTIONS[:token])
27
+ end
28
+
29
+ def say!(sentence)
30
+ nabaztag.say!(sentence)
31
+ end
32
+
33
+ def voice=(v)
34
+ nabaztag.voice = v
35
+ end
36
+ end
37
+
38
+ n = NabaztagSay.new
39
+
40
+ ARGV.options do |o|
41
+ script_name = File.basename($0)
42
+
43
+ o.set_summary_indent(' ')
44
+ o.banner = "Usage: #{script_name} [OPTIONS] TEXT-TO-SAY"
45
+ o.define_head "Tell Nabaztag to say something."
46
+ o.separator ""
47
+ o.separator "If you create a file in your home directory called .nabaztag"
48
+ o.separator "containing the MAC and API token for your device, like this:"
49
+ o.separator " NABAZTAG_MAC = '00039XXXXXXX'"
50
+ o.separator " NABAZTAG_TOKEN = '1234XXXXXXXXXXX'"
51
+ o.separator "then you need not specify the MAC and token on the command line."
52
+ o.separator ""
53
+ o.separator "Options:"
54
+
55
+ begin
56
+ o.on(
57
+ "-m", "--mac=MAC", String,
58
+ "MAC address of device",
59
+ "Default is NABAZTAG_MAC in ~/.nabaztag"
60
+ ) { |OPTIONS[:mac]| }
61
+ o.on(
62
+ "-t", "--token=TOKEN", String,
63
+ "API token",
64
+ "Default is NABAZTAG_TOKEN in ~/.nabaztag"
65
+ ) { |OPTIONS[:token]| }
66
+ o.on(
67
+ "-v", "--voice=VOICE", String,
68
+ "Voice to use for speech"
69
+ ) { |OPTIONS[:voice]| }
70
+ o.on(
71
+ "-l", "--list-voices",
72
+ "List available voices and exit"
73
+ ) {
74
+ n.list_voices
75
+ exit
76
+ }
77
+ o.separator ""
78
+ o.on_tail("-h", "--help", "Show this help message.") { puts o; exit }
79
+ o.parse!
80
+
81
+ OPTIONS.each do |k,v|
82
+ if !v || v.empty?
83
+ raise RuntimeError, "Missing parameter #{v}"
84
+ end
85
+ end
86
+ if ARGV.empty?
87
+ raise RuntimeError, "Nothing to say"
88
+ end
89
+ rescue StandardError => e
90
+ puts "Error: #{e.message}", ''
91
+ puts o
92
+ exit 1
93
+ end
94
+ end
95
+
96
+ n.voice = OPTIONS[:voice]
97
+ n.say!(ARGV.join(' '))
@@ -0,0 +1,160 @@
1
+ require 'nabaztag/message'
2
+ require 'nabaztag/response'
3
+ require 'nabaztag/choreography'
4
+
5
+ #
6
+ # Nabaztag allows control of the text-to-speech, ear control, and choreography features of
7
+ # Nabaztag devices.
8
+ #
9
+ # To use this library, you need to know the MAC of the device (written on the base) and its
10
+ # API token. The token must be obtained from http://www.nabaztag.com/vl/FR/api_prefs.jsp .
11
+ #
12
+ # The API allows different commands to be dispatched simultaneously; in order to achieve this,
13
+ # this library queues commands until they are sent.
14
+ #
15
+ # E.g.
16
+ # nabaztag = Nabaztag.new(mac, token)
17
+ # nabaztag.say('bonjour') # Nothing sent yet
18
+ # nabaztag.move_ears(4, 4) # Still not sent
19
+ # nabaztag.send # Messages sent
20
+ #
21
+ # This also means that if two conflicting commands are issued without an intervening send,
22
+ # only the latter will be carried out.
23
+ #
24
+ # However, beware! The API doesn't seem to respond well if multiple commands are sent in
25
+ # a short period of time: it can become confused and send erroneous commands to the device.
26
+ #
27
+ # In addition, the choreography command does not seem to play well with other commands: if
28
+ # text-to-speech and choreography are sent in one request, only the speech will get through
29
+ # to the rabbit.
30
+ #
31
+ # With version 2 of the API, it is now possible to specify a voice for the message. The
32
+ # default is determined by the rabbit's language (claire22s for French; heather22k for
33
+ # English). The voice's language overrides that of the rabbit: i.e. a French rabbit will
34
+ # speak in English when told to use an English voice.
35
+ #
36
+ # The known voices are grouped by language in the Nabaztag::VOICES constant, but no attempt
37
+ # is made to validate against this list, as Violet may introduce additional voices in future.
38
+ #
39
+ class Nabaztag
40
+
41
+ class ServiceError < RuntimeError ; end
42
+
43
+ attr_reader :mac, :token, :message
44
+ attr_accessor :voice
45
+
46
+ #
47
+ # Create a new Nabaztag instance to communicate with the device with the given MAC address and
48
+ # service token (see class overview for explanation of token).
49
+ #
50
+ def initialize(mac, token)
51
+ @mac, @token = mac, token
52
+ @message = new_message
53
+ @ear_positions = [nil, nil]
54
+ end
55
+
56
+ #
57
+ # Send all pending messages
58
+ #
59
+ def send
60
+ @message.voice = voice
61
+ response = @message.send
62
+ @message = new_message
63
+ unless response.success?
64
+ raise ServiceError, response.raw
65
+ end
66
+ return response
67
+ end
68
+
69
+ #
70
+ # Send a message immediately to get the ear positions.
71
+ #
72
+ def ear_positions
73
+ ear_message = new_message
74
+ ear_message.ears = 'ok'
75
+ response = ear_message.send
76
+ return [response.left_ear, response.right_ear]
77
+ end
78
+
79
+ #
80
+ # Say text.
81
+ #
82
+ def say(text)
83
+ message.tts = text
84
+ nil
85
+ end
86
+
87
+ #
88
+ # Say text immediately.
89
+ #
90
+ def say!(text)
91
+ say(text)
92
+ send
93
+ end
94
+
95
+ #
96
+ # Make the rabbit bark.
97
+ #
98
+ def bark
99
+ say('ouah ouah')
100
+ nil
101
+ end
102
+
103
+ #
104
+ # Bark immediately.
105
+ #
106
+ def bark!
107
+ bark
108
+ send
109
+ end
110
+
111
+ #
112
+ # Set the position of the left and right ears between 0 and 16. Use nil to avoid moving an ear.
113
+ # Note that these positions are not given in degrees, and that it is not possible to specify the
114
+ # direction of movement. For more precise ear control, use choreography instead.
115
+ #
116
+ def move_ears(left, right)
117
+ message.posleft = left if left
118
+ message.posright = right if right
119
+ nil
120
+ end
121
+
122
+ #
123
+ # Move ears immediately.
124
+ #
125
+ def move_ears!(left, right)
126
+ move_ears(left, right)
127
+ send
128
+ end
129
+
130
+ #
131
+ # Creates a new choreography message based on the actions instructed in the block. The commands
132
+ # are evaluated in the context of a new Choreography instance.
133
+ #
134
+ # E.g.
135
+ # nabaztag.choreography do
136
+ # together { led :middle, :green ; led :left, :red }
137
+ # led :right, :yellow
138
+ # together { led :left, :off ; led :right, :off}
139
+ # ...
140
+ # end
141
+ #
142
+ def choreography(title=nil, &blk)
143
+ message.chortitle = title
144
+ message.chor = Choreography.new(&blk).build
145
+ nil
146
+ end
147
+
148
+ #
149
+ # Creates choreography and sends it immediately.
150
+ #
151
+ def choreography!(title=nil, &blk)
152
+ choreography(title, &blk)
153
+ send
154
+ end
155
+
156
+ def new_message
157
+ return Message.new(mac, token)
158
+ end
159
+
160
+ end
@@ -0,0 +1,119 @@
1
+ class Nabaztag
2
+
3
+ #
4
+ # The Choreography class uses class methods to implement a simple DSL. These build API choreography
5
+ # messages based on instructions to move the ears and light the LEDs.
6
+ #
7
+ class Choreography
8
+
9
+ LED_COLORS = {
10
+ :red => [255, 0, 0],
11
+ :orange => [255, 127, 0],
12
+ :yellow => [255, 255, 0],
13
+ :green => [ 0, 255, 0],
14
+ :blue => [ 0, 0, 255],
15
+ :purple => [255, 0, 255],
16
+ :dim_red => [127, 0, 0],
17
+ :dim_orange => [127, 63, 0],
18
+ :dim_yellow => [127, 127, 0],
19
+ :dim_green => [ 0, 127, 0],
20
+ :dim_blue => [ 0, 0, 127],
21
+ :dim_purple => [127, 0, 127],
22
+ :off => [ 0, 0, 0]
23
+ }
24
+ EARS = {:left => [1], :right => [0], :both => [0,1]}
25
+ LEDS = {:bottom => 0, :left => 1, :middle => 2, :right => 3, :top => 4}
26
+ EAR_DIRECTIONS = {:forward => 0, :backward => 1}
27
+
28
+ def initialize(&blk)
29
+ @messages = []
30
+ @tempo = 10
31
+ @time_stamp = 0
32
+ instance_eval(&blk) if block_given?
33
+ end
34
+
35
+ def build
36
+ return (['%d' % @tempo] + @messages).join(',')
37
+ end
38
+
39
+ #
40
+ # Set the tempo of the choreography in Hz (i.e. events per secod). The default is 10
41
+ # events per second.
42
+ #
43
+ def tempo(t)
44
+ @tempo = t
45
+ end
46
+
47
+ #
48
+ # Move :left, :right, or :both ears to angle degrees (0-180) in direction
49
+ # :forward (default) or :backward.
50
+ #
51
+ def ear(which_ear, angle, direction=:forward)
52
+ direction_number = EAR_DIRECTIONS[direction]
53
+ EARS[which_ear].each do |ear_number|
54
+ append_message('motor', ear_number, angle, 0, direction_number)
55
+ end
56
+ advance
57
+ end
58
+
59
+ #
60
+ # Change colour of an led (:top, :right:, middle, :left, :bottom) to a specified colour.
61
+ # The colour may be specified either as RGB values (0-255) or by using one of the named colours
62
+ # in LED_COLORS.
63
+ #
64
+ # E.g.
65
+ # led :middle, :red
66
+ # led :top, 0, 0, 255
67
+ # led :bottom, :off
68
+ #
69
+ def led(which_led, c1, c2=nil, c3=nil)
70
+ led_number = LEDS[which_led]
71
+ if (c1 && c2 && c3)
72
+ red, green, blue = c1, c2, c3
73
+ else
74
+ red, green, blue = LED_COLORS[c1]
75
+ end
76
+ append_message('led', led_number, red, green, blue)
77
+ advance
78
+ end
79
+
80
+ #
81
+ # Group several actions into a single chronological step via a block.
82
+ #
83
+ # E.g.
84
+ # event { led :top, :yellow ; ear :both, 0 }
85
+ #
86
+ def event(duration=1, &blk)
87
+ length(duration, &blk)
88
+ end
89
+
90
+ alias_method :together, :event
91
+
92
+ #
93
+ # Perform one or more actions for n chronological steps
94
+ #
95
+ # E.g.
96
+ # length 3 do
97
+ # led :top, :red ; led :middle, :yellow
98
+ # end
99
+ #
100
+ def length(duration, &blk)
101
+ old_in_event = @in_event
102
+ @in_event = true
103
+ yield
104
+ @in_event = old_in_event
105
+ advance duration
106
+ end
107
+
108
+ private
109
+
110
+ def append_message(*params)
111
+ @messages << ("%d,%s,%d,%d,%d,%d" % ([@time_stamp] + params))
112
+ end
113
+
114
+ def advance(duration=1)
115
+ @time_stamp += duration unless @in_event
116
+ end
117
+
118
+ end
119
+ end