Cool tricks with AT codes

With the power of my mind

Mind Control

I shall make music play

I shall make it sunny outside

I shall make this page magically change

Okay, so I precanned my mind

I wrote a script which calls on Device::Ericsson::AccessoryMenu to allow me to trigger things from my phone.

Connect Accessories XMMS Remote

Images stolen from http://www.linuxbrit.net/bluexmms/

Clicker / Romeo

Mac OS X applications

Clicker

You write your actions in applescript

 try
   tell application "iTunes"
     try
       set the current_track to get current track
     on error
       error "No current track."
     end try
     set the track_count to (the count of tracks of the current playlist) as string
     next track
     try
       set this_track to get current track
     on error
       play current_track
       set this_track to the current_track
       play track 1 of current playlist
       set this_track to get current track
     end try
     set the track_index to (the index of this_track) as string
     set this_title to the name of this_track as string
     return (track_index & "/" & track_count & "  " & this_title)
   end tell
 on error error_message
   return error_message
 end try

That's how you tell iTunes to play the next track

Two Drawbacks

This machine is not running Mac OS X

imacquarium

I don't speak applescript

BlueXMMS

Ruby program that remote controls XMMS

Grokable, but very entwined code

So for the first version of my code I stole mostly from that, splitting up the AT-slinging code from the actions

A simple XMMS remote

 #!/usr/local/bin/perl -w
 use strict;
 use Device::Ericsson::AccessoryMenu;
 use Device::SerialPort;
 use Xmms::Remote;
 
 my $xmms = Xmms::Remote->new;
 my $menu = Device::Ericsson::AccessoryMenu->new(
     port => Device::SerialPort->new( '/dev/rfcomm0' ) || die,
     menu => [ XMMS => [
                  "Play/Pause" => sub {
                       $xmms->is_playing ? $xmms->pause : $xmms->play;
                   },
                   Back => sub { $xmms->playlist_prev },
                   Next => sub { $xmms->playlist_next },
                   Stop => sub { $xmms->stop },
                 ],
	     ],
 );
 $menu->register_menu;
 $menu->control while 1;

Slight return: The unholy power of Perl

$music_player, kindly play the next track.

applescript

 try
   tell application "iTunes"
     try
       set the current_track to get current track
     on error
       error "No current track."
     end try
     set the track_count to (the count of tracks of the current playlist) as string
     next track
     try
       set this_track to get current track
     on error
       play current_track
       set this_track to the current_track
       play track 1 of current playlist
       set this_track to get current track
     end try
     set the track_index to (the index of this_track) as string
     set this_title to the name of this_track as string
     return (track_index & "/" & track_count & "  " & this_title)
   end tell
 on error error_message
   return error_message
 end try

Perl

 use Xmms::Remote;
 my $xmms = Xmms::Remote->new;
 $xmms->playlist_next;

Sliders

We can invoke other kinds of widgets too, like this volume slider

Volume control

              Volume => sub {
                  my $r = shift;
                  $r->percent_slider(
                      title    => 'Volume',
                      value    => $xmms->get_main_volume,
                      steps    => 10,
                      callback => sub {
                          $xmms->set_main_volume( shift )
                      },
                  );
                  return;
	      },

Event Loops

red arrows

Everything likes to have its own event loop; Tk, GTK, POE certainly do.

As a lowly utility module we want to stay out of the way of those, and not trap the user into a(nother) busy loop.

Mmmm, Statey

states

Since we expect the phone to send different messages when it's displaying a Slider than when it's displaying a Menu we can either:

Continue to kludge tests into a really big if/then/else statement. Eventually go insane.

Do something stateful.

I like pie

Pie!

I also like state machines

enter_state we push a new state onto the stack, and call its on_enter method

exit_state - we pop the top, call it's on_exit method, and the new tops on_enter

in control we call the topmost states handle method

Inside the Text state

Text is probably the simplest state:

Set up the parentage, and add some accessors

 use strict;
 package Device::Ericsson::AccessoryMenu::Text;
 use base 'Device::Ericsson::AccessoryMenu::State';
 __PACKAGE__->mk_accessors( qw( title lines ) );

on_enter we just send the dialog over

 sub on_enter {
     my $self = shift;
     my $title = $self->title;
     $self->send( join ',',
                  qq{AT*EAID=14,2,"$title"},
                  map { qq{"$_"} } @{ $self->lines }
                 );
     $self->expect( 'OK' );
 }

And whatever value our handler gets, it means leave this state

 sub handle {
     my $self = shift;
     $self->exit_state;
 }
 
 1;

That's all there is to it.

Bob's your Uncle

Bob Holness

What you can control

XMMS

Xmms::Remote is your underdocumented friend.

Galeon

You just write bookmarklets and invoke them via system

Everything else

Well, anything you can write Perl for at least.

Check out examples/remote in the Device::Ericsson::AccessoryMenu distribution. It's the remote I'm using for this presentation, and already includes XMMS and Galeon menus.

X11

It's fairly straightforward to use the Mouse state and remap the events into X11 keypresses.

 #!/usr/local/bin/perl -w
 use lib qw(lib);
 use strict;
 use Device::Ericsson::AccessoryMenu;
 use Device::SerialPort;
 use X11::GUITest qw(PressKey ReleaseKey);
 my $port = shift || '/dev/rfcomm0';
 my %keymap1 = ( ... );
 my %keymap2 = ( ... );
 sub translate_keys {
     my $remote = shift;
     my %keymap = @_;
     $remote->mouse_mode( callback => sub {
                              my ($key, $updown) = @_;
                              return unless exists $keymap{$key};
                              $updown ? PressKey($keymap{$key})
                                      : ReleaseKey($keymap{$key});
                          });
 }
 my $menu = Device::Ericsson::AccessoryMenu->new(
     port => Device::SerialPort->new( $port ) || die,
     menu => [ XKeys => [
         Map1 => sub { translate_keys( shift, %keymap1 ) },
         Map2 => sub { translate_keys( shift, %keymap2 ) },
        ],
     ],
    );

Being Perl this naturally has a real and practical application.

What's next

Couple more dialog types to add, if they're useful.

With luck someone will write a nice frontend for building menus, possibly shipping a bunch of precanned menus

More pie

Pie!