diff options
Diffstat (limited to 'bin/efa-m')
-rwxr-xr-x | bin/efa-m | 587 |
1 files changed, 457 insertions, 130 deletions
@@ -4,28 +4,38 @@ use warnings; use 5.010; use utf8; -no if $] >= 5.018, warnings => 'experimental::smartmatch'; - -our $VERSION = '1.17'; +our $VERSION = '3.13'; binmode( STDOUT, ':encoding(utf-8)' ); -use Encode qw(decode); +use Encode qw(decode); use Getopt::Long qw(:config no_ignore_case bundling); -use List::Util qw(first max); +use JSON; +use List::Util qw(first max none); use Travel::Status::DE::EFA; -my $efa_url = 'https://efa.vrr.de/vrr/XSLT_DM_REQUEST'; +my $service = 'VRR'; +my $efa_url; my $efa_encoding; -my ( $date, $time, $input_type, $list_lines, $offset, $relative_times ); -my ($full_routes); -my ( $filter_via, $track_via ); -my ( $timeout, $developer_mode ); -my ( @grep_lines, @grep_platforms, @grep_mots ); -my ( %edata, @edata_pre ); -my ( $list_services, $service, $discover_and_print, $discover ); +my $use_cache = 1; +my $cache; +my ( $json_output, $raw_json_output ); +my ( $date, $time, $input_type, $list_lines, $offset, $relative_times ); +my ( $full_routes, $filter_via, $show_jid ); +my ( $timeout, $developer_mode ); +my ( @grep_lines, @grep_platforms, @grep_mots ); +my ( %edata, @edata_pre ); +my ( $list_services, $discover_and_print, $discover ); my $efa; +my %occupancy_map = ( + MANY_SEATS => '.', + FEW_SEATS => 'o', + STANDING_ONLY => '*', + FULL => '!', + unknown => '?', +); + @ARGV = map { decode( 'UTF-8', $_ ) } @ARGV; GetOptions( @@ -33,6 +43,7 @@ GetOptions( 'd|date=s' => \$date, 'D|discover' => \$discover, 'h|help' => sub { show_help(0) }, + 'j|with-jid' => \$show_jid, 'l|line=s@' => \@grep_lines, 'L|linelist' => \$list_lines, 'list' => \$list_services, @@ -46,9 +57,12 @@ GetOptions( 'timeout=i' => \$timeout, 'u|efa-url=s' => \$efa_url, 'v|via=s' => \$filter_via, - 'V|track-via=s' => sub { $filter_via = $track_via = $_[1] }, - 'version' => \&show_version, + 'V|track-via=s' => \$filter_via, + 'cache!' => \$use_cache, + 'json' => \$json_output, + 'raw-json' => \$raw_json_output, 'devmode' => \$developer_mode, + 'version' => \&show_version, ) or show_help(1); @@ -56,38 +70,87 @@ if ($list_services) { show_services(); } -if ( @ARGV != 2 ) { +if ( @ARGV < 1 or @ARGV > 2 ) { show_help(1); } +if ($use_cache) { + my $cache_path = ( $ENV{XDG_CACHE_HOME} // "$ENV{HOME}/.cache" ) + . '/Travel-Status-DE-EFA'; + eval { + require Cache::File; + $cache = Cache::File->new( + cache_root => $cache_path, + default_expires => '90 seconds', + lock_level => Cache::File::LOCK_LOCAL(), + ); + }; + if ($@) { + $cache = undef; + } +} + # --line=foo,bar support @edata_pre = split( qr{,}, join( q{,}, @edata_pre ) ); @grep_lines = split( qr{,}, join( q{,}, @grep_lines ) ); @grep_mots = split( qr{,}, join( q{,}, @grep_mots ) ); @grep_platforms = split( qr{,}, join( q{,}, @grep_platforms ) ); -my ( $place, $input ) = @ARGV; +my ( $place, $input, $coord, $stopseq, $stopfinder ); + +if ( @ARGV == 1 ) { + if ( $ARGV[0] + =~ m{ ^ ([^@]*) @ ([^@]*) [(] ([^T]*) T ([^)]*) [)] (.*) $ }x ) + { + $stopseq = { + stateless => $1, + stop_id => $2, + date => $3, + time => $4, + key => $5 + }; + } + elsif ( $ARGV[0] =~ m{ ^ [?] (?<name> .*) $ }x ) { + $stopfinder = { + name => $+{name}, + }; + } + elsif ( $ARGV[0] =~ m{ ^ (?<lat> [0-9.]+ ) : (?<lon> [0-9].+ ) $ }x ) { + $coord = { + lat => $+{lat}, + lon => $+{lon}, + }; + } + else { + $input = $ARGV[0]; + } +} +else { + ( $place, $input ) = @ARGV; +} -if ( $input =~ s{ ^ (?<type> address|poi|stop) : }{}x ) { +if ( $input and $input =~ s{ ^ (?<type> address|poi|stop|stopID) : }{}x ) { $input_type = $+{type}; } for my $efield (@edata_pre) { - given ($efield) { - when ('a') { $edata{route_after} = 1; $full_routes = 1 } - when ('b') { $edata{route_before} = 1; $full_routes = 1 } - when ('f') { $edata{fullroute} = 1; $full_routes = 1 } - when ('r') { $edata{route} = 1; $full_routes = 1 } - default { $edata{$efield} = 1 } - } + if ( $efield eq 'a' ) { $edata{route_after} = 1; $full_routes = 1 } + elsif ( $efield eq 'b' ) { $edata{route_before} = 1; $full_routes = 1 } + elsif ( $efield eq 'f' ) { $edata{fullroute} = 1; $full_routes = 1 } + elsif ( $efield eq 'r' ) { $edata{route} = 1; $full_routes = 1 } + elsif ( $efield eq 'i' ) { $edata{info} = 1 } + elsif ( $efield eq 'm' ) { $edata{messages} = 1 } + else { $edata{$efield} = 1 } } if ($filter_via) { $full_routes = 1; } -if ($service) { - my $service_ref = first { lc( $_->{shortname} ) eq lc($service) } - Travel::Status::DE::EFA::get_efa_urls(); +if ($efa_url) { + $service = undef; +} +elsif ($service) { + my $service_ref = Travel::Status::DE::EFA::get_service($service); if ( not $service_ref ) { printf STDERR ( "Error: Unknown service '%s'. See 'efa-m --list' for a " @@ -96,20 +159,25 @@ if ($service) { ); exit 1; } - $efa_url = $service_ref->{url}; $efa_encoding = $service_ref->{encoding}; + $efa_url = undef; } -sub new_efa_by_url { - my ($url) = @_; +sub new_efa { + my ( $s, $u ) = @_; my $res = Travel::Status::DE::EFA->new( + service => $s, + efa_url => $u, + cache => $cache, date => $date, developer_mode => $developer_mode, - efa_url => $url, efa_encoding => $efa_encoding, full_routes => $full_routes, place => $place, name => $input, + coord => $coord, + stopfinder => $stopfinder, + stopseq => $stopseq, time => $time, type => $input_type, timeout => $timeout, @@ -121,7 +189,7 @@ sub new_efa_by_url { sub show_help { my ($code) = @_; - print "Usage: efa-m [-d <dd.mm.yyyy>] [-t <hh:mm>] <city> <station>\n" + print "Usage: efa-m [-d <dd.mm.yyyy>] [-t <hh:mm>] <stop>\n" . "See also: man efa-m\n"; exit $code; @@ -129,8 +197,10 @@ sub show_help { sub show_services { printf( "%-45s %-14s %s\n\n", 'service', 'abbr. (-s)', 'url (-u)' ); - for my $service ( Travel::Status::DE::EFA::get_efa_urls() ) { - printf( "%-45s %-14s %s\n", @{$service}{qw(name shortname url)} ); + for my $shortname ( Travel::Status::DE::EFA::get_service_ids() ) { + my $service = Travel::Status::DE::EFA::get_service($shortname); + printf( "%-45s %-14s %s\n", + $service->{name}, $shortname, $service->{url} ); } exit 0; @@ -142,6 +212,14 @@ sub show_version { exit 0; } +sub format_delay { + my ( $delay, $len ) = @_; + if ( $delay and $len ) { + return sprintf( "(%+${len}d)", $delay ); + } + return q{}; +} + sub format_route { my (@route) = @_; @@ -152,25 +230,60 @@ sub format_route { say 'BUG'; next; } - if ( not defined $stop->arr_time ) { - $output .= sprintf( " %5s %40s %s\n", - $stop->dep_time, $stop->name, $stop->platform, ); + my $occupancy + = $stop->occupancy ? format_occupancy( $stop->occupancy ) : q{ }; + my $delay = q{ }; + if ( $stop->delay ) { + $delay = sprintf( '(%+3d)', $stop->delay ); } - elsif ( not defined $stop->dep_time ) { - $output .= sprintf( "%5s %40s %s\n", - $stop->arr_time, $stop->name, $stop->platform, ); + if ( $stop->is_cancelled ) { + $output .= sprintf( + " --:-- %s %s %35s %s\n", + $delay, $occupancy, $stop->full_name, $stop->platform // q{}, + ); } - elsif ( $stop->arr_time eq $stop->dep_time ) { - $output .= sprintf( " %5s %40s %s\n", - $stop->dep_time, $stop->name, $stop->platform, ); + elsif ( defined $stop->arr and defined $stop->dep ) { + if ( $stop->arr->epoch == $stop->dep->epoch ) { + $output .= sprintf( + " %5s %s %s %35s %s\n", + $stop->arr->strftime('%H:%M'), + $delay, $occupancy, $stop->full_name, + $stop->platform // q{}, + ); + } + else { + $output .= sprintf( + "%5s → %5s %s %s %35s %s\n", + $stop->arr->strftime('%H:%M'), + $stop->dep->strftime('%H:%M'), + $delay, + $occupancy, + $stop->full_name, + $stop->platform // q{}, + ); + } } - else { + elsif ( defined $stop->arr ) { $output .= sprintf( - "%5s → %5s %40s %s\n", - $stop->arr_time, $stop->dep_time, - $stop->name, $stop->platform, + "%5s %s %s %35s %s\n", + $stop->arr->strftime('%H:%M'), + $delay, $occupancy, $stop->full_name, $stop->platform, ); } + elsif ( defined $stop->dep ) { + $output .= sprintf( + " %5s %s %s %35s %s\n", + $stop->dep->strftime('%H:%M'), + $delay, $occupancy, $stop->full_name, $stop->platform, + ); + } + elsif ( $stop->full_name ) { + $output .= sprintf( " %s %s %35s %s\n", + $delay, $occupancy, $stop->full_name, $stop->platform, ); + } + else { + $output .= "?\n"; + } } return $output; } @@ -190,12 +303,12 @@ sub display_result { for my $line (@lines) { - if ( length( $line->[5] ) ) { - $line->[5] =~ tr{\n\x0d}{ }s; - chomp $line->[5]; + if ( $edata{messages} and $line->[5]->hints ) { print "\n"; - for my $info_line ( split( qr{\n}, $line->[5] ) ) { - say "# ${info_line}"; + for my $hint ( $line->[5]->hints ) { + $hint =~ tr{\n\x0d}{ }s; + chomp $hint; + say "# ${hint}"; } } @@ -212,23 +325,96 @@ sub display_result { return; } +sub show_coord { + my $max_len = max map { length( $_->full_name ) } $efa->results; + for my $stop ( $efa->results ) { + printf( + "%5.1f km %-${max_len}s %s\n", + $stop->distance_m * 1e-3, + $stop->full_name, $stop->id_code + ); + } +} + +sub show_stopfinder { + my $max_len = max map { length( $_->full_name ) } $efa->results; + for my $stop ( $efa->results ) { + printf( "%-${max_len}s %s %s\n", + $stop->full_name, $stop->id_num, $stop->id_code ); + } +} + +sub show_stopseq { + my $trip = $efa->result; + + printf( "%s %s → %s\n", $trip->type, $trip->line // q{}, $trip->dest_name ); + + printf( + "Fahrt %s am %s\n", + $trip->number || $stopseq->{stateless}, + ( $trip->route )[0]->sched_dep->strftime('%d.%m.%Y'), + ); + say q{}; + + my $occupancy_len = 0; + my $delay_len = 0; + my $inner_delay_len = 0; + my $max_delay = max map { abs( $_->delay // 0 ) } $trip->route; + if ($max_delay) { + $inner_delay_len = length($max_delay) + 1; + $delay_len = length( sprintf( '(%+d)', $max_delay ) ) + 1; + } + if ( first { $_->occupancy } $trip->route ) { + $occupancy_len = 2; + } + + if ( first { $_->is_cancelled } $trip->route and $delay_len < 3 ) { + $delay_len = 3; + } + + for my $stop ( $trip->route ) { + printf( + "%s → %s%${delay_len}s %-${occupancy_len}s%s (%s) %s\n", + $stop->arr ? $stop->arr->strftime('%H:%M') + : q{ }, + $stop->dep ? $stop->dep->strftime('%H:%M') + : q{ }, + $stop->is_cancelled ? 'XX' + : ( + $stop->delay + ? sprintf( " (%+${inner_delay_len}d)", $stop->delay ) + : q{} + ), + $stop->occupancy ? format_occupancy( $stop->occupancy ) : q{}, + $stop->full_name, + $stop->niveau, + $stop->platform + ); + } +} + sub show_lines { my @output; for my $l ( $efa->lines ) { - if ( ( @grep_lines and not( $l->name ~~ \@grep_lines ) ) - or ( @grep_mots and not( $l->mot_name ~~ \@grep_mots ) ) ) + if ( ( @grep_lines and none { $l->number eq $_ } @grep_lines ) + or ( @grep_mots and none { $l->mot_name eq $_ } @grep_mots ) ) { next; } - if ( @grep_mots and not( $l->mot_name ~~ \@grep_mots ) ) { + if ( @grep_mots and none { $l->mot_name eq $_ } @grep_mots ) { next; } - push( @output, - [ $l->type, $l->name, $l->direction // q{}, q{}, $l->route // q{} ] + push( + @output, + [ + $l->type, $l->number, + $l->direction // q{}, q{}, + $l->route // q{} + ] ); } @@ -237,9 +423,35 @@ sub show_lines { return; } +sub format_occupancy { + my ($occupancy) = @_; + + return $occupancy_map{$occupancy} // $occupancy_map{unknown}; +} + sub show_results { my @output; + my $delay_len = 0; + my $delay_fmt = 0; + for my $d ( $efa->results ) { + if ( $d->delay ) { + $delay_len = max( $delay_len, length( $d->delay ) + 1 ); + } + } + if ($delay_len) { + $delay_fmt = $delay_len + 3; + } + + if ( scalar $efa->stops > 1 ) { + for my $stop ( $efa->stops ) { + say $stop->full_name; + } + } + elsif ( $efa->stop->full_name ) { + say $efa->stop->full_name; + } + for my $d ( $efa->results ) { my @output_line; @@ -247,18 +459,14 @@ sub show_results { my $dtime = ( $relative_times ? sprintf( '%2d min', $d->countdown ) - : $d->sched_time + : $d->datetime->strftime('%H:%M') ); - if ( $d->platform_db ) { - $platform .= ' (DB)'; - } - if ( - ( @grep_lines and not( $d->line ~~ \@grep_lines ) ) - or ( @grep_mots and not( $d->mot_name ~~ \@grep_mots ) ) + ( @grep_lines and none { $d->line eq $_ } @grep_lines ) + or ( @grep_mots and none { $d->mot_name eq $_ } @grep_mots ) or ( @grep_platforms - and not( $platform ~~ \@grep_platforms ) ) + and none { $platform eq $_ } @grep_platforms ) or ( $offset and $d->countdown < $offset ) or ( $filter_via and @@ -273,28 +481,40 @@ sub show_results { next; } else { - $dtime .= ' CANCELED'; + $dtime = '--:--'; } } - elsif ($track_via) { + elsif ($filter_via) { my $via = first { $_->name =~ m{$filter_via}io } $d->route_post; - $dtime .= ' → ' . $via->arr_time; + $dtime + .= ' → ' + . $via->arr->clone->add( minutes => $d->delay // 0 ) + ->strftime('%H:%M'); } if ( $d->delay ) { - if ($relative_times) { - $dtime .= ' (+' . $d->delay . ')'; - } - else { - $dtime .= ' +' . $d->delay; - } + $dtime .= ' ' . format_delay( $d->delay, $delay_len ); } - @output_line - = ( $dtime, $platform, $d->line, q{}, $d->destination, $d->info ); + my $line = $d->line; + if ( ( length($line) > 10 or not $line ) + and $d->train_type + and $d->train_no ) + { + $line = $d->train_type . ' ' . $d->train_no; + } + + @output_line = ( $dtime, $platform, $line, q{}, $d->destination, $d ); + + if ($show_jid) { + $output_line[2] .= ' ' . $d->id; + } if ( $edata{route} ) { $output_line[3] - = join( q{ }, map { $_->name_suf } $d->route_interesting ); + = join( q{ }, map { $_->name } $d->route_interesting ); + } + elsif ( $d->occupancy ) { + $output_line[3] = format_occupancy( $d->occupancy ); } if ( $edata{fullroute} ) { @@ -315,20 +535,33 @@ sub show_results { display_result(@output); + if ( $edata{info} ) { + for my $info ( $efa->infos ) { + say q{}; + if ( $info->subject and $info->subtitle ne $info->subject ) { + printf( "# %s\n%s\n", $info->subtitle, $info->subject ); + } + else { + printf( "# %s\n", $info->subtitle ); + } + } + } + return; } if ( $discover or $discover_and_print ) { - for my $service_ref ( Travel::Status::DE::EFA::get_efa_urls() ) { - $efa = new_efa_by_url( $service_ref->{url} ); + for my $shortname ( Travel::Status::DE::EFA::get_service_ids() ) { + $efa = new_efa($shortname); if ( $efa and not $efa->errstr ) { if ($discover_and_print) { last; } + my $service_ref = Travel::Status::DE::EFA::get_service($shortname); printf( "%s / %s (%s)\n -> efa-m -s %s %s\n\n", - @{$service_ref}{qw(name shortname url shortname)}, - join( q{ }, map { "'$_'" } @ARGV ), + $service_ref->{name}, $shortname, $service_ref->{url}, + $shortname, join( q{ }, map { "'$_'" } @ARGV ), ); } } @@ -337,24 +570,48 @@ if ( $discover or $discover_and_print ) { } } -$efa = new_efa_by_url($efa_url); +$efa = new_efa( $service, $efa_url ); if ( my $err = $efa->errstr ) { say STDERR "Request error: ${err}"; if ( $efa->place_candidates ) { say 'You might want to try one of the following places:'; - say join( "\n", $efa->place_candidates ); + for my $candidate ( $efa->place_candidates ) { + printf( "%d %s\n", $candidate->id_num, $candidate->name ); + } } elsif ( $efa->name_candidates ) { say 'You might want to try one of the following names:'; - say join( "\n", $efa->name_candidates ); + for my $candidate ( $efa->name_candidates ) { + printf( "%d %s\n", $candidate->id_num, $candidate->name ); + } } exit 2; } -if ($list_lines) { +if ($json_output) { + if ($stopseq) { + say JSON->new->convert_blessed->encode( $efa->result ); + } + else { + say JSON->new->convert_blessed->encode( [ $efa->results ] ); + } +} +elsif ($raw_json_output) { + say JSON->new->convert_blessed->encode( $efa->{response} ); +} +elsif ($coord) { + show_coord(); +} +elsif ($stopfinder) { + show_stopfinder(); +} +elsif ($stopseq) { + show_stopseq(); +} +elsif ($list_lines) { show_lines(); } else { @@ -365,58 +622,117 @@ __END__ =head1 NAME -efa-m - Unofficial interface to the efa.vrr.de departure monitor +efa-m - Unofficial interface to the efa.vrr.de departure and trip monitor =head1 SYNOPSIS -B<efa-m> [B<-Lr>] [B<-d> I<dd.mm.yyyy>] [B<-t> I<hh:mm>] -[B<-l> I<lines>] [B<-p> I<platforms>] [B<-u> I<url>] -I<city> [I<type>B<:>]I<name> +B<efa-m> [B<-jLr>] [B<-d> I<dd.mm.yyyy>] [B<-t> I<hh:mm>] +[B<-l> I<lines>] [B<-p> I<platforms>] [B<-s> I<service>] +[I<city>] [I<type>B<:>]I<name> + +B<efa-m> [B<-s> I<service>] B<?>I<query>|I<lat>B<:>I<lon> + +B<efa-m> [B<-s> I<service>] I<tripid> =head1 VERSION -version 1.17 +version 3.13 =head1 DESCRIPTION -B<efa-m> lists scheduled tram, bus and train departures at the location I<name> -in I<city>. Realtime data (i.e. delays) is included if available, it's -visible in the output as a "+x" remark (meaning a delay of x minutes). +B<efa-m> is an interface to EFA public transport services. + +It can serve as a departure monitor, request details about a specific +trip/journey, and look up public transport stops by name or geolocation. +The operating mode depends on the contents of its mandatory argument. + +=head2 Departure Monitor (I<name>) + +Shows departures at I<name> or I<city> I<name>; I<name> may also be a stop ID +number or code. For each departure, B<efa-m> shows + +=over + +=item * estimated departure time (including delay, if available), + +=item * delay in minutes, + +=item * platform, + +=item * line, + +=item * expected occupation (range . o * !, if available), and + +=item * destination. + +=back + +If I<city> is specified, I<name> refers to a location within I<city>. +Otherwise, I<name> must be self-contained. I.e., both C<< efa-m Essen Hbf >> +and C<< efa-m "Essen Hbf" >> are valid. Note, however, than C<< efa-m E Hbf >> +works, but C<< efa-m "E Hbf" >> does not. By default, I<name> refers to a stop, this can be changed by specifying I<type>. Supported types are B<address> and B<poi> (point of interest). +=head2 Location Search (B<?>I<query>|I<lat>B<:>I<lon>) + +List stops that match I<query> or that are located in the vicinity of +I<lat>B<:>I<lon> geocoordinates. In addition to stop names, the output also +includes stop ID codes (both modes) and numbers (only available in I<query> +mode). + +=head2 Trip Details (I<JourneyID>) + +List trip information including arrival and departure time, name, and platform +of each stop on the trip's route. + =head1 OPTIONS +Values in brackets indicate options that only apply to the corresponding +operating mode(s). + =over -=item B<-A>, B<--auto-url>, B<--discover-and-print> +=item B<-A>, B<--auto-url>, B<--discover-and-print> (monitor) -Probe all known EFA entry points for the specified stop. Print the first -result which was not an error. +Probe all known EFA services for the specified stop. Print the first result +which was not an error. Note that this may take a while and will not necessarily return the best result. Also, using thi option by default is not recommended, as it puts EFA services under considerable additional load. -=item B<-d>, B<--date> I<dd.mm.yyyy> +=item B<-d>, B<--date> I<dd.mm.yyyy> (monitor) Show departures for I<date> instead of today. May also be specified as I<dd.mm.> -=item B<-D>, B<--discover> +=item B<-D>, B<--discover> (monitor) + +Probe all known EFA services for the specified stop. Print the URLs and names +of all services which did not return an error. + +=item B<-j>, B<--with-jid> (monitor) + +Show journey ID for each departure. +The ID can be used to query details with a subsequent B<efa-m> invocation +(trip details mode). + +=item B<--json> -Probe all known EFA entry points for the specified stop. Print the URLs and -names of all entry points which did not return an error. +Print result(s) as JSON and exit. This is a dump of internal data structures +and not guaranteed to remain stable between minor versions. Please use the +Travel::Status::DE::EFA(3pm) module if you need a proper API. -=item B<-L>, B<--linelist> +=item B<-L>, B<--linelist> (monitor) 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<efa-m> call or at the time specifed using B<--date> and B<--time> are guaranteed to be included. -=item B<-l>, B<--line> I<lines> +=item B<-l>, B<--line> I<lines> (monitor) Only show departures of I<lines> (comma-separatad list, option may be repeated) @@ -426,7 +742,7 @@ repeated) List supported EFA services with their URLs (see B<-u>) and abbreviations (see B<-s>). -=item B<-m>, B<--mot> I<motlist> +=item B<-m>, B<--mot> I<motlist> (monitor) Only show departures whose type appears in I<motlist> (comma-separated list, this option may be repeated). @@ -435,11 +751,11 @@ The following departure types ("modes of transport") are supported: zug, s-bahn, u-bahn, stadtbahn, tram, stadtbus, regionalbus, schnellbus, seilbahn, schiff, ast, sonstige -=item B<-o>, B<--offset> I<minutes> +=item B<-o>, B<--offset> I<minutes> (monitor) Ignore departures which are less than I<minutes> from now. -=item B<-O>, B<--output> I<outputtypes> +=item B<-O>, B<--output> I<outputtypes> (monitor) For each result, show additional information as specified by I<outputtypes>. I<outputtypes> is a comma-separated list, the B<-O>/B<--output> option may @@ -465,20 +781,37 @@ requested station. Show each departure's full route (timestamps and stop names) before and after the requested station. +=item i / info + +Show station-specific information messages. These typically relate to +construction work, broken elevators or escalators, or special announcements +for large-scale events. + =item r / route Show up to three stops between the requested station and the departure's destination. B<efa-m> tries to display the three most important stops, however these are heuristically determined and may not be optimal. +=item m / messages + +Show free-text messages associated with individual departures. These can +include generic information such is bicycle transportation options or Wi-Fi +availability, delay reasons, and more. + =back -=item B<-p>, B<--platform> I<platforms> +=item B<-p>, B<--platform> I<platforms> (monitor) Only show departures at I<platforms> (comma-separated list, option may be repeated). Note that the C<< Bstg. >> / C<< Gleis >> prefix must be omitted. -=item B<-r>, B<--relative> +=item B<--raw-json> + +Print unprocessed EFA response as JSON and exit. +Useful for debugging and development purposes. + +=item B<-r>, B<--relative> (monitor) Show relative departure times in minutes (i.e. the time difference between the departure and the time of the request). In this case, realtime data is @@ -486,36 +819,24 @@ already included. =item B<-s>, B<--service> I<name> -Short name of the EFA entry point. See Travel::Status::DE::EFA(3pm) and the +Short name of the EFA service. See Travel::Status::DE::EFA(3pm) and the B<--list> option for a list of services. -=item B<-t>, B<--time> I<hh:mm> +=item B<-t>, B<--time> I<hh:mm> (monitor) Show departures starting at I<time> instead of now. -=item B<-u>, B<--efa-url> I<url> - -URL to the EFA entry point, defaults to -L<http://efa.vrr.de/vrr/XSLT_DM_REQUEST>. Depending on your location, some -I<url>s may contain more specific data than others. See -Travel::Status::DE::EFA(3pm) and the B<--list> option for alternatives. - =item B<--timeout> I<seconds> Set timeout for HTTP requests. Default: 10 seconds. Set to 0 or a negative value to disable it. -=item B<-v>, B<--via> I<station> +=item B<-v>, B<--via> I<station> (monitor) -Only show trains serving I<station> after the requseted stop. I<station> -is matched against the "I<city> I<stop>" fields in each line's route. -Regular expressions are also supported. - -=item B<-V>, B<--track-via> I<station> - -Lik B<--via>: Only show trains serving I<station> after the requseted stop. -Also, show the arrival time at I<station> after the departure time at the -current stop. +Only show trains serving I<station> after the requseted stop, and show the +arrival time at I<station> after the departure time at the current stop. +I<station> is matched against the "I<city> I<stop>" fields in each line's +route. Regular expressions are also supported. =item B<--version> @@ -542,8 +863,6 @@ None. =item * Travel::Status::DE::EFA(3pm) -=item * XML::LibXML(3pm) - =back =head1 BUGS AND LIMITATIONS @@ -555,9 +874,17 @@ choose the appropriate EFA URL for these by itself. In these cases, you should find an appropriate EFA service using the B<-D>/B<--discover> option and then use B<-s> I<service> when making requests. +=over + +=item * EFA does not provide real-time data for the routes of requested +departures. Hence, B<--via> estimates the arrival time from scheduled +departure and departure delay + +=back + =head1 AUTHOR -Copyright (C) 2011-2020 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt> +Copyright (C) 2011-2023 Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt> =head1 LICENSE |