nabaztag 0.2.2

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.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
+