#!/usr/bin/env perl use strict; use warnings; use 5.010; no if $] >= 5.018, warnings => "experimental::smartmatch"; our $VERSION = '1.08'; binmode( STDOUT, ':encoding(utf-8)' ); use Encode qw(decode); use Getopt::Long qw(:config no_ignore_case); use List::Util qw(max); use Travel::Status::DE::EFA; my $efa_url = 'http://efa.vrr.de/vrr/XSLT_DM_REQUEST'; my ( $date, $time, $input_type, $list_lines, $offset, $relative_times ); my ($timeout); my ( @grep_lines, @grep_platforms ); @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV; GetOptions( 'd|date=s' => \$date, 'h|help' => sub { show_help(0) }, 'l|line=s@' => \@grep_lines, 'L|linelist' => \$list_lines, 'o|offset=i' => \$offset, 'p|platform=s@' => \@grep_platforms, 'r|relative' => \$relative_times, 't|time=s' => \$time, 'timeout=i' => \$timeout, 'u|efa-url=s' => \$efa_url, 'V|version' => \&show_version, ) or show_help(1); if ( @ARGV != 2 ) { show_help(1); } # --line=foo,bar support @grep_lines = split( qr{,}, join( q{,}, @grep_lines ) ); @grep_platforms = split( qr{,}, join( q{,}, @grep_platforms ) ); my ( $place, $input ) = @ARGV; if ( $input =~ s{ ^ (? address|poi|stop) : }{}x ) { $input_type = $+{type}; } my $status = Travel::Status::DE::EFA->new( date => $date, efa_url => $efa_url, place => $place, name => $input, time => $time, type => $input_type, timeout => $timeout // 10, ); sub show_help { my ($code) = @_; print "Usage: efa-m [-d ] [-t ] \n" . "See also: man efa-m\n"; exit $code; } sub show_version { say "efa-m version ${VERSION}"; exit 0; } sub display_result { my (@lines) = @_; my @line_length; if ( not @lines ) { die("Nothing to show\n"); } for my $i ( 0 .. 3 ) { $line_length[$i] = max map { length( $_->[$i] ) } @lines; } for my $line (@lines) { if ( length( $line->[4] ) ) { $line->[4] =~ tr{\n\x0d}{ }s; chomp $line->[4]; print "\n"; for my $info_line ( split( qr{\n}, $line->[4] ) ) { say "# ${info_line}"; } } printf( join( q{ }, ( map { "%-${_}s" } @line_length ) ) . "\n", @{$line}[ 0 .. 3 ] ); } return; } sub show_lines { my @output; for my $l ( $status->lines ) { if ( @grep_lines and not( $l->name ~~ \@grep_lines ) ) { next; } push( @output, [ $l->type, $l->name, $l->direction // q{}, $l->route // q{} ] ); } display_result(@output); return; } sub show_results { my @output; for my $d ( $status->results ) { my $platform = $d->platform; my $dtime = ( $relative_times ? sprintf( '%2d min', $d->countdown ) : $d->time ); if ( $d->platform_db ) { $platform .= ' (DB)'; } if ( ( @grep_lines and not( $d->line ~~ \@grep_lines ) ) or ( @grep_platforms and not( $platform ~~ \@grep_platforms ) ) or ( $offset and $d->countdown < $offset ) ) { next; } if ( $d->is_cancelled ) { if ($relative_times) { next; } else { $dtime .= ' CANCELED'; } } if ( $d->delay ) { $dtime .= ' (+' . $d->delay . ')'; } push( @output, [ $dtime, $platform, $d->line, $d->destination, $d->info ] ); } display_result(@output); return; } if ( my $err = $status->errstr ) { say STDERR "Request error: ${err}"; exit 2; } if ($list_lines) { show_lines(); } else { show_results(); } __END__ =head1 NAME efa-m - Unofficial interface to the efa.vrr.de departure monitor =head1 SYNOPSIS B [B<-Lr>] [B<-d> I] [B<-t> I] [B<-l> I] [B<-p> I] [B<-u> I] I [IB<:>]I =head1 VERSION version 1.08 =head1 DESCRIPTION B lists upcoming tram, bus and train departures at the location I in I. By default, I refers to a stop, this can be changed by specifying I. Supported types are B
and B (point of interest). =head1 OPTIONS =over =item B<-d>, B<--date> I Show departures for I instead of today. May also be specified as I =item B<-L>, B<--linelist> Do not show departures. Instead, list all lines serving the specified place. Note that this information may be incomplete -- only lines which are in service either at the time of the B call or at the time specifed using B<--date> and B<--time> are guaranteed to be included. =item B<-l>, B<--line> I Only show departures of I (comma-separatad list, option may be repeated) =item B<-o>, B<--offset> I Ignore departures which are less than I from now. =item B<-p>, B<--platform> I Only show departures at I (comma-separated list, option may be repeated). Note that the C<< Bstg. >> / C<< Gleis >> prefix must be omitted. =item B<-r>, B<--relative> Use relative departure times. =item B<-t>, B<--time> I Show departures starting at I