nabaztag 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,26 @@
1
+ rake build
2
+
3
+ gem install pkg/*.gem
4
+
5
+ MacBook-Air ~/Projects/nabaztag[master*]$ nabaztag ears
6
+ ---> check ears... http://api.nabaztag.com/vl/FR/api.jsp?ears=ok&sn=0C&token=137
7
+ 13 (0.1s)
8
+
9
+ MacBook-Air ~/Projects/nabaztag[master*]$ nabaztag ears 0 10
10
+ ---> move ears... http://api.nabaztag.com/vl/FR/api.jsp?posleft=0&posright=10&sn=0C&token=137
11
+ #<Nabaztag::Response:0x102334598> (0.1s)
12
+
13
+ cat ~/.nabaztag
14
+ NABAZTAG_MAC = '0XXXXXXXXXCC'
15
+ NABAZTAG_TOKEN = '1XXXXXXX7'
16
+
17
+
18
+ -----------------------------------------------------------------------------------------------------------------------------
19
+
20
+ Hybrid of
21
+
22
+ http://github.com/lukeredpath/nabaztag-ruby/tree/master/bin/
23
+
24
+ and
25
+
26
+ http://adamblog.heroku.com/past/2009/8/28/sumo_oneoff_ec2_instance_lanching/
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'jeweler'
2
+
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "nabaztag"
5
+ s.description = %q{nabaztag commandline}
6
+ s.summary = s.description
7
+ s.author = "ELC"
8
+ s.email = "info@elctech.com"
9
+ s.homepage = "http://github.com/elcgit/nabaztag"
10
+ s.rubyforge_project = "nabaztag"
11
+ s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
12
+ s.executables = %w(nabaztag)
13
+ s.add_dependency "thor"
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+
17
+ task :default => :release
18
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
data/bin/nabaztag ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require File.dirname(__FILE__) + '/../lib/nabaztag'
5
+
6
+ require 'thor'
7
+
8
+ require 'nabaztag'
9
+ load ENV['HOME'] + '/.nabaztag'
10
+
11
+ class CLI < Thor
12
+ desc "ears [left] [right]", "move ears to position left,right (0-16) or omit to read positions"
13
+ def ears(left=nil, right=nil)
14
+ # id = task("Launch instance") { elc.launch }
15
+ # host = task("Acquire hostname") { elc.wait_for_hostname(id) }
16
+ if(left)
17
+ task("move ears") { nabaztag.move_ears!(left, right) }
18
+ else
19
+ task("check ears") { nabaztag.ear_positions }
20
+ end
21
+ end
22
+ def say(text)
23
+ task("say text") {nabaztag.say!(text)}
24
+ end
25
+
26
+ =begin
27
+ desc "list", "list running instances"
28
+ def list
29
+ elc.list.each do |inst|
30
+ printf "%-50s %-12s %s\n", inst[:hostname], inst[:instance_id], inst[:status]
31
+ end
32
+ end
33
+
34
+ desc "detach [<volume_id>]", "detach volume from instance"
35
+ def detach(volume=nil)
36
+ vol_id = (elc.find_volume(volume) || elc.attached_volumes.first || abort("No attached volumes"))[:volume_id]
37
+ task("Detach #{vol_id}") { elc.detach(vol_id) }
38
+ end
39
+ =end
40
+
41
+ no_tasks do
42
+ def nabaztag
43
+ @nabaztag ||= Nabaztag.new(NABAZTAG_MAC, NABAZTAG_TOKEN)
44
+ end
45
+
46
+ def task(msg, &block)
47
+ # printf "---> %-24s ", "#{msg}..."
48
+ start = Time.now
49
+ result = block.call || 'done'
50
+ finish = Time.now
51
+ time = sprintf("%0.1f", finish - start)
52
+ # puts "#{result} (#{time}s)"
53
+ result
54
+ end
55
+ end
56
+ end
57
+
58
+ CLI.start
data/lib/nabaztag.rb ADDED
@@ -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
@@ -0,0 +1,75 @@
1
+ require 'cgi'
2
+ require 'iconv'
3
+ require 'open-uri'
4
+ require 'nabaztag/response'
5
+
6
+ class Nabaztag
7
+ class Message
8
+
9
+ SERVICE_ENCODING = 'iso-8859-1'
10
+ API_URI = 'http://api.nabaztag.com/vl/FR/api.jsp?'
11
+ FIELDS = [
12
+ :idmessage, :posright, :posleft, :idapp, :tts, :chor, :chortitle, :ears, :nabcast,
13
+ :ttlive, :voice, :speed, :pitch, :action
14
+ ]
15
+ ACTIONS = {
16
+ :preview => 1,
17
+ :friend_list => 2,
18
+ :list_messages => 3,
19
+ :get_timezone => 4,
20
+ :get_signature => 5,
21
+ :get_blacklist => 6,
22
+ :get_sleep_state => 7,
23
+ :get_version => 8,
24
+ :get_voices => 9,
25
+ :get_name => 10,
26
+ :get_languages => 11,
27
+ :preview_message => 12,
28
+ :sleep => 13,
29
+ :wake => 14
30
+ }
31
+
32
+ FIELDS.each do |field|
33
+ attr_accessor field
34
+ end
35
+
36
+ def initialize(mac, token)
37
+ @mac, @token = mac, token
38
+ @expected_identifiers = []
39
+ end
40
+
41
+ def send
42
+ puts request_uri
43
+ Response.new(open(request_uri){ |io| io.read })
44
+ end
45
+
46
+ def expect(identifier)
47
+ @expected_identifiers << identifier
48
+ end
49
+
50
+ def action=(name)
51
+ @action = ACTIONS[name] or raise "unknown action #{name}"
52
+ end
53
+
54
+ private
55
+
56
+ def request_uri
57
+ API_URI + parameters.sort_by{ |k,v| k.to_s }.map{ |k,v|
58
+ value = CGI.escape(v.to_s)
59
+ "#{k}=#{value}"
60
+ }.join('&')
61
+ end
62
+
63
+ def parameters
64
+ FIELDS.inject({
65
+ :sn => @mac,
66
+ :token => @token
67
+ }){ |hash, element|
68
+ value = __send__(element)
69
+ hash[element] = value if value
70
+ hash
71
+ }
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,78 @@
1
+ require 'rexml/document'
2
+
3
+ class Nabaztag
4
+ class Response
5
+ include REXML
6
+
7
+ ERROR_MESSAGES = /^NO|^ABUSE|NOTSENT$/
8
+
9
+ attr_reader :raw
10
+
11
+ def initialize(xml)
12
+ @raw = xml
13
+ @doc = Document.new(xml)
14
+ end
15
+
16
+ def messages
17
+ lookup('/rsp/message')
18
+ end
19
+
20
+ def comments
21
+ lookup('/rsp/comment')
22
+ end
23
+
24
+ def friends
25
+ lookup('/rsp/friend@name')
26
+ end
27
+
28
+ def timezone
29
+ lookup('/rsp/timezone').first
30
+ end
31
+
32
+ def rabbit_version
33
+ lookup('/rsp/rabbitVersion').first
34
+ end
35
+
36
+ def rabbit_name
37
+ lookup('/rsp/rabbitName').first
38
+ end
39
+
40
+ def voices
41
+ XPath.match(@doc, '/rsp/voice').inject({}){ |h, n|
42
+ lang = n.attributes['lang']
43
+ command = n.attributes['command']
44
+ (h[lang] ||= []) << command
45
+ h
46
+ }
47
+ end
48
+
49
+ def languages
50
+ lookup('/rsp/myLang@lang')
51
+ end
52
+
53
+ def left_ear
54
+ position = lookup('/rsp/leftposition').first
55
+ position && position.to_i
56
+ end
57
+
58
+ def right_ear
59
+ position = lookup('/rsp/rightposition').first
60
+ position && position.to_i
61
+ end
62
+
63
+ def signature
64
+ lookup('/rsp/signature')
65
+ end
66
+
67
+ def success?
68
+ !messages.any?{ |m| m =~ ERROR_MESSAGES }
69
+ end
70
+
71
+ private
72
+
73
+ def lookup(xpath)
74
+ XPath.match(@doc, xpath).map{ |n| n.text }
75
+ end
76
+
77
+ end
78
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nabaztag
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - ELC
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-12 00:00:00 +00:00
13
+ default_executable: nabaztag
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thor
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: nabaztag commandline
26
+ email: info@elctech.com
27
+ executables:
28
+ - nabaztag
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - bin/nabaztag
38
+ - lib/nabaztag.rb
39
+ - lib/nabaztag/choreography.rb
40
+ - lib/nabaztag/message.rb
41
+ - lib/nabaztag/response.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/elcgit/nabaztag
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: nabaztag
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: nabaztag commandline
70
+ test_files: []
71
+