149 lines
3.4 KiB
Perl
Executable File
149 lines
3.4 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
# Inspired by "ansiweather".
|
|
|
|
use English qw(-no_match_vars);
|
|
use strict;
|
|
use File::Basename;
|
|
use Getopt::Long;
|
|
use JSON;
|
|
use WWW::Mechanize;
|
|
use POSIX qw(strftime);
|
|
use Time::Piece;
|
|
use utf8;
|
|
|
|
my $verbose = 0;
|
|
|
|
sub show_help {
|
|
my ($exit_code) = @_;
|
|
my $name = basename($PROGRAM_NAME);
|
|
print <<EOF;
|
|
usage: $name [OPTION] LOCATION
|
|
Print a weather report for LOCATION using data from
|
|
openweathermap.org.
|
|
|
|
OPTIONS
|
|
--verbose Be verbose and print the data structure returned
|
|
by openweathermap.
|
|
|
|
--help Print this help.
|
|
|
|
LOCATION
|
|
A city name or "city,xx" where xx is the two-letter country code.
|
|
|
|
Examples
|
|
\$ $name --verbose "Paris,FR"
|
|
\$ $name "Asunción,PY"
|
|
EOF
|
|
exit($exit_code);
|
|
}
|
|
|
|
sub tstamp { strftime("%F %T %Z ", localtime); }
|
|
sub vprint { print(@_) if ($verbose); }
|
|
sub vprintf { printf(@_) if ($verbose); }
|
|
sub vtprint { vprint(tstamp()); vprint(@_); }
|
|
sub vtprintf { vprint(tstamp()); vprintf(@_); }
|
|
|
|
STDERR->autoflush(1);
|
|
STDOUT->autoflush(1);
|
|
binmode(STDOUT, ":utf8");
|
|
|
|
GetOptions(
|
|
"help" => \my $help,
|
|
"verbose" => \$verbose,
|
|
);
|
|
|
|
show_help(0) if ($help);
|
|
|
|
my $location = do {
|
|
1 == (scalar @ARGV) or show_help(1);
|
|
$ARGV[0];
|
|
};
|
|
|
|
# Return the weather report as a character string.
|
|
sub get_response {
|
|
vtprint("get_response begins\n");
|
|
my $mech = WWW::Mechanize->new();
|
|
|
|
vprint("Getting weather report...\n");
|
|
my $wthr_response = $mech->get(
|
|
'http://api.openweathermap.org/data/2.5/weather?q='
|
|
. $location . '&units=metric&appid=85a4e3c55b73909f42c6a23ec35b7147'
|
|
);
|
|
my $wthr_str = $wthr_response->content(raw => 1);
|
|
die("Didn't find content in http response.\n") if (! $wthr_str);
|
|
utf8::decode($wthr_str) or die("failed to decode response\n");
|
|
|
|
vprint("get_response returning\n");
|
|
return $wthr_str;
|
|
}
|
|
|
|
sub format_time {
|
|
my ($r) = @_;
|
|
my $t = localtime($r->{dt});
|
|
return $t->strftime('%H:%M %Z');
|
|
}
|
|
|
|
sub format_speed {
|
|
my ($r) = @_;
|
|
return sprintf("%.0fKm/h", 3.6 * $r->{wind}->{speed});
|
|
}
|
|
|
|
my @directions = qw(N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW);
|
|
my $point_count = scalar(@directions);
|
|
|
|
sub format_direction {
|
|
my ($r) = @_;
|
|
my $deg = $r->{wind}->{deg};
|
|
if (0 == $r->{wind}->{speed}) {
|
|
return "";
|
|
} else {
|
|
my $idx = (
|
|
($deg + (365 / (2 * $point_count))) / (365 / $point_count)
|
|
) % $point_count;
|
|
return sprintf(" %s", $directions[$idx]);
|
|
}
|
|
}
|
|
|
|
sub format_wind {
|
|
my ($r) = @_;
|
|
return sprintf("%s%s", format_speed($r), format_direction($r));
|
|
}
|
|
|
|
sub format_temperature {
|
|
my ($r) = @_;
|
|
return sprintf("%.1f°C", $r->{main}->{temp});
|
|
}
|
|
|
|
sub format_humidity {
|
|
my ($r) = @_;
|
|
return sprintf("%d%% RH", $r->{main}->{humidity});
|
|
}
|
|
|
|
sub format_sky {
|
|
my ($r) = @_;
|
|
return sprintf("%s", $r->{weather}->[0]->{main});
|
|
}
|
|
|
|
sub format_response {
|
|
my ($r) = @_;
|
|
return sprintf(
|
|
"Weather at %s ► %s ◆ %s ◆ %s ◆ %s\n",
|
|
format_time($r),
|
|
format_temperature($r),
|
|
format_sky($r),
|
|
format_wind($r),
|
|
format_humidity($r),
|
|
);
|
|
}
|
|
|
|
my $wthr_str = get_response();
|
|
vprint("turning the response string into a hashref...\n");
|
|
my $json = JSON->new;
|
|
my $wthr_hashref = $json->decode($wthr_str);
|
|
vprintf("pretty-printing the hashref...\n");
|
|
printf("%s\n", $json->pretty->encode($wthr_hashref)) if ($verbose);
|
|
vprint("formatting weather report...\n");
|
|
print format_response($wthr_hashref);
|
|
|
|
exit(0);
|