Jon Udell Speakable Web Services

by Jon Udell

When Scotty tried to talk to a Macintosh through its mouse, in Star Trek IV (1986), the joke was on Apple. Why couldn't this famously easy-to-use computer accept the most natural form of input? Over the years, I dabbled now and then with voice command systems, but they never seemed worth the trouble -- until now. I've been exploring the speech technologies in Mac OS X on an 800MHz TiBook, and I'm really impressed. Apple has done a marvelous job with the recognition and control systems, and now that you can script the Internet so easily in OS X, it's straightforward to build useful voice-driven commands that invoke external as well as local services. Consider this dialog:

Me: "Temperature"

Computer: "36 degrees"

There are, of course, a million ways to look up the temperature on the Web. Most of them start with the browser. You fire it up and go to a bookmark, which in my case is eather/local/03431. There are at least two problems with this scenario. First, you have to translate the request into an application context (the browser) and a procedure (go to bookmarks, select Local Weather). Second, you destroy your original context. For example, I'm typing these words in emacs. I'd like to keep on typing, and reading what I am writing, even as I ask for and receive the temperature. Speaking the request and hearing the response is an ideal solution. Here are a few ways to implement it.

Perl and AppleScript Working Together

I started with a Perl script that uses SOAP::Lite to hit a Web service at XMethods, like so:

#! /usr/bin/perl -w
use strict;
use SOAP::Lite;
my $temp = SOAP::Lite
   -> service('')
   -> getTemp('03431') . " degrees";
`osascript -e 'say "$temp"'`;

Here we're using Perl's backtick evaluation to run a command-line tool, osascript, which runs AppleScript code -- in this case, to speak the result of the SOAP call. Use of the text-to-speech engine introduces some fascinating subtleties. For example, if you omit the leading space in "degrees," the answer will sound like:

three six period zero dee eee gee are eee eee ess

It would be handy if you could just save this as a file called Temperature in the Speakable Items folder (for example, /Users/jon/Library/Speech/Speakable Items) and launch it by speaking the name "Temperature." But so far as I've been able to determine, scripted Speakable Items (as opposed to those which invoke key-driven commands) have to be written in AppleScript and, further, saved from the Script Editor as type Application (not Text or Compiled Script). Fortunately, AppleScript can invoke the Unix shell, which can invoke the Perl script. Let's refactor slightly, and have the Perl script simply return a bare value, suitable for downstream use in any kind of application, whether voice-enabled or not:

#! /usr/bin/perl -w
use strict;
use SOAP::Lite;
print SOAP::Lite
   -> service('')
   -> getTemp('03431');

I saved that script as /Users/jon/Temperature, and then saved the following AppleScript Application as /Users/jon/Library/Speech/Speakable Items/Temperature:

set theResult to do shell script "/Users/jon/Temperature"
say theResult & " degrees" as string

Now the textual result of the Temperature script is spoken by AppleScript. You can, alternatively, do the whole thing in AppleScript, like so:

tell application ""
      set theResult to call soap {method name:"getTemp", \
      parameters:{zipcode:"03431"}, method namespace \
      uri:"urn:xmethods-Temperature", SOAPAction:"/TemperatureService"}
end tell

say theResult & " degrees" as string

This is easier in one way, harder in another. It's easier if you're not a Perl programmer, or if you haven't added SOAP::Lite and its required substrate (expat, XML::Parser) to the Perl kit that comes with Mac OS X. But when a Web service is described by a WSDL file, it's easier to use SOAP::Lite than AppleScript, since the former can use the WSDL file to simplify access.

It's ideal when there's a Web service that will give you the answer you're looking for, but when that's not the case, there's always good old HTML screen-scraping. In that case, a language like Perl or Python will run rings around AppleScript. Here's a script that speaks my Weblog's current rank and pageview count for today:

#! /usr/bin/perl -w
use strict;
use LWP::Simple;
my $res = get "";
$res =~ m#(.+)Jon's Radio</a></td><td><td align="right">(\d+)&nbsp;#;
my $preface = $1;
my $count = $2;
$preface =~m#">(\d+)\.&nbsp;</td><td>#;
my $rank = $1;
`osascript -e 'say "Rank $rank, count $count"'`;

In this case, it's more trouble than it's worth to return raw results from Perl and format them for speech output in AppleScript.

Pages: 1, 2

Next Pagearrow