# Note: This talk is not due to be presented until 2003-06-18 at # Birmingham Perl Mongers, so no peeking, m'kay =meta consider_old 100 =meta link_base /~richardc/talks/dea/ =head1 Cool tricks with AT codes =head2 With the power of my mind =over =item L =item I shall make music play =item I shall make it sunny outside =item I shall make this page magically change =back =head2 Okay, so I precanned my mind =over =item I wrote a script which calls on L to allow me to trigger things from my phone. =item L L L Images stolen from L =back =head2 Clicker / Romeo =over =item Mac OS X applications L =item 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 =back =head2 Two Drawbacks =over =item This machine is not running Mac OS X L =item I don't speak applescript =back =head2 BlueXMMS =over =item Ruby program that remote controls XMMS =item Grokable, but very entwined code =item So for the first version of my code I stole mostly from that, splitting up the AT-slinging code from the actions =back =head2 A simple XMMS remote =over =item #!/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; =back =head2 Slight return: The unholy power of Perl =over =item $music_player, kindly play the next track. =item 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 =item Perl use Xmms::Remote; my $xmms = Xmms::Remote->new; $xmms->playlist_next; =back =head2 Sliders =over =item We can invoke other kinds of widgets too, like this volume slider L =item 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; }, =back =head2 Event Loops =over =item L =item Everything likes to have its own event loop; L, L, L certainly do. =item 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. =back =head2 Mmmm, Statey =over =item L =item Since we expect the phone to send different messages when it's displaying a C than when it's displaying a C we can either: =item Continue to kludge tests into a really big if/then/else statement. Eventually go insane. =item Do something stateful. =back =head2 I like pie =over =item L =item I also like state machines =item C we push a new state onto the stack, and call its C method =item C - we pop the top, call it's C method, and the new tops C =item in C we call the topmost states C method =back =head2 Inside the Text state =over =item C 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 ) ); C 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 C gets, it means I sub handle { my $self = shift; $self->exit_state; } 1; That's all there is to it. =back =head2 Bob's your Uncle =over =item L =back =head2 What you can control =over =item XMMS L is your underdocumented friend. =item Galeon You just write bookmarklets and invoke them via C =item Everything else Well, anything you can write Perl for at least. Check out L in the L distribution. It's the remote I'm using for this presentation, and already includes XMMS and Galeon menus. =back =head2 X11 =over =item It's fairly straightforward to use the C 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 ) }, ], ], ); =item Being Perl this naturally has a real and practical application. =back =head2 What's next =over =item Couple more dialog types to add, if they're useful. =item With luck someone will write a nice frontend for building menus, possibly shipping a bunch of precanned menus =item More pie L =back =head1 Footer More than you ever needed to know about L. E 2003, Richard Clamp