summaryrefslogtreecommitdiff
path: root/bin/efa-m
diff options
context:
space:
mode:
Diffstat (limited to 'bin/efa-m')
-rwxr-xr-xbin/efa-m223
1 files changed, 152 insertions, 71 deletions
diff --git a/bin/efa-m b/bin/efa-m
index 326d2cb..769960a 100755
--- a/bin/efa-m
+++ b/bin/efa-m
@@ -4,25 +4,22 @@ use warnings;
use 5.010;
use utf8;
-no if $] >= 5.018, warnings => 'experimental::smartmatch';
-
-our $VERSION = '1.17';
+our $VERSION = '2.00';
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 List::Util qw(first max none);
use Travel::Status::DE::EFA;
my $efa_url = 'https://efa.vrr.de/vrr/XSLT_DM_REQUEST';
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 ( $date, $time, $input_type, $list_lines, $offset, $relative_times );
+my ( $full_routes, $filter_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 $efa;
@@ -46,7 +43,7 @@ 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] },
+ 'V|track-via=s' => \$filter_via,
'version' => \&show_version,
'devmode' => \$developer_mode,
@@ -56,7 +53,7 @@ if ($list_services) {
show_services();
}
-if ( @ARGV != 2 ) {
+if ( @ARGV < 1 or @ARGV > 2 ) {
show_help(1);
}
@@ -66,20 +63,26 @@ if ( @ARGV != 2 ) {
@grep_mots = split( qr{,}, join( q{,}, @grep_mots ) );
@grep_platforms = split( qr{,}, join( q{,}, @grep_platforms ) );
-my ( $place, $input ) = @ARGV;
+my ( $place, $input );
-if ( $input =~ s{ ^ (?<type> address|poi|stop) : }{}x ) {
+if ( @ARGV == 1 ) {
+ $input = $ARGV[0];
+}
+else {
+ ( $place, $input ) = @ARGV;
+}
+
+if ( $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 'm' ) { $edata{messages} = 1 }
+ else { $edata{$efield} = 1 }
}
if ($filter_via) {
$full_routes = 1;
@@ -87,7 +90,7 @@ if ($filter_via) {
if ($service) {
my $service_ref = first { lc( $_->{shortname} ) eq lc($service) }
- Travel::Status::DE::EFA::get_efa_urls();
+ Travel::Status::DE::EFA::get_efa_urls();
if ( not $service_ref ) {
printf STDERR (
"Error: Unknown service '%s'. See 'efa-m --list' for a "
@@ -121,7 +124,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>] [place] <station>\n"
. "See also: man efa-m\n";
exit $code;
@@ -142,6 +145,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,23 +163,35 @@ 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, );
- }
- elsif ( not defined $stop->dep_time ) {
- $output .= sprintf( "%5s %40s %s\n",
- $stop->arr_time, $stop->name, $stop->platform, );
+ if ( defined $stop->arr and defined $stop->dep ) {
+ if ( $stop->arr->epoch == $stop->dep->epoch ) {
+ $output .= sprintf(
+ " %5s %40s %s\n",
+ $stop->arr->strftime('%H:%M'),
+ $stop->name, $stop->platform,
+ );
+ }
+ else {
+ $output .= sprintf(
+ "%5s → %5s %40s %s\n",
+ $stop->arr->strftime('%H:%M'),
+ $stop->dep->strftime('%H:%M'),
+ $stop->name, $stop->platform,
+ );
+ }
}
- 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 ) {
+ $output .= sprintf(
+ "%5s %40s %s\n",
+ $stop->arr->strftime('%H:%M'),
+ $stop->name, $stop->platform,
+ );
}
- else {
+ elsif ( defined $stop->dep ) {
$output .= sprintf(
- "%5s → %5s %40s %s\n",
- $stop->arr_time, $stop->dep_time,
- $stop->name, $stop->platform,
+ " %5s %40s %s\n",
+ $stop->dep->strftime('%H:%M'),
+ $stop->name, $stop->platform,
);
}
}
@@ -190,7 +213,7 @@ sub display_result {
for my $line (@lines) {
- if ( length( $line->[5] ) ) {
+ if ( $edata{messages} and length( $line->[5] ) ) {
$line->[5] =~ tr{\n\x0d}{ }s;
chomp $line->[5];
print "\n";
@@ -217,13 +240,13 @@ sub show_lines {
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->name 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;
}
@@ -240,6 +263,17 @@ sub show_lines {
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;
+ }
+
for my $d ( $efa->results ) {
my @output_line;
@@ -247,7 +281,7 @@ sub show_results {
my $dtime = (
$relative_times
? sprintf( '%2d min', $d->countdown )
- : $d->sched_time
+ : $d->datetime->strftime('%H:%M')
);
if ( $d->platform_db ) {
@@ -255,10 +289,10 @@ sub show_results {
}
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,29 +307,46 @@ 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 );
+ }
+
+ my $line = $d->line;
+ if ( length($line) > 10 and $d->train_type and $d->train_no ) {
+ $line = $d->train_type . ' ' . $d->train_no;
}
@output_line
- = ( $dtime, $platform, $d->line, q{}, $d->destination, $d->info );
+ = ( $dtime, $platform, $line, q{}, $d->destination, $d->info );
if ( $edata{route} ) {
$output_line[3]
= join( q{ }, map { $_->name_suf } $d->route_interesting );
}
+ elsif ( $d->occupancy ) {
+ if ( $d->occupancy eq 'MANY_SEATS' ) {
+ $output_line[3] = '_';
+ }
+ elsif ( $d->occupancy eq 'FEW_SEATS' ) {
+ $output_line[3] = '*';
+ }
+ elsif ( $d->occupancy eq 'STANDING_ONLY' ) {
+ $output_line[3] = '!';
+ }
+ else {
+ $output_line[3] = '?';
+ }
+ }
if ( $edata{fullroute} ) {
$output_line[6]
@@ -371,17 +422,38 @@ efa-m - Unofficial interface to the efa.vrr.de departure monitor
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>
+[I<city>] [I<type>B<:>]I<name>
=head1 VERSION
-version 1.17
+version 2.00
=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> lists scheduled tram, bus and train departures at the location I<name>.
+
+For each departure, it shows
+
+=over
+
+=item * scheduled departure time,
+
+=item * delay in minutes,
+
+=item * platform,
+
+=item * line,
+
+=item * expected occupation (from _ to !, 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 Essen Hbf >> and
+C<< efa "Essen Hbf" >> are valid. Note, however, than C<< efa E Hbf >> works,
+but C<< efa "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).
@@ -471,6 +543,12 @@ 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>
@@ -507,15 +585,10 @@ value to disable it.
=item B<-v>, B<--via> I<station>
-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>
@@ -555,9 +628,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 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE