Schirm, Charme und Melone (Linux-Magazin, Februar 2015)

Ein Perl-Skript, das im Morgengrauen die stündliche Wettervorhersage des aktuellen Tages einholt, hilft bei der Entscheidung, auf Risiko zu spielen oder doch einen Schirm einzupacken.

In der nassen Jahreszeit stellt sich oft die Frage, ob im Laufe des Tages Regen oder Schnee vom Himmel fallen wird und deswegen auf dem Weg zur Arbeit ein Schirm mitzuführen ist. Letztlich wären aber sogar gelegentliche Regenschauer um die Mittagszeit irrelevant, solange sich der Weg zu und von der Arbeit trockenen Fußes bewältigen lässt.

Untergrund-Wetter

Zum Glück bietet der Wetterdienst "Weather Underground" (http://wunderground.com) kostenlose Vorhersagen für die laufenden Stunden des Tages an, und obwohl diese zweifellos nie hundertprozentig zutreffen werden, schlagen sie doch die menschliche Intuition im Schnitt um Längen. Auf dem CPAN finden Perl-Programmierer das Modul WWW::Wunderground::API, das die Wetterdaten mit einem eigens von [3] eingeholten API-Key skriptgesteuert vom Server saugt. Das simple Skript in Listing 1 nutzt das Modul, iteriert über die stündlichen Einträge im zurückkommenden JSON-Datenwust und gibt pro Stunde die Uhrzeit und die Regenwahrscheinlichkeit im Eintrag pop ("Probability of precipitation" [3]) aus. Abbildung 1 zeigt, dass es am Nachmittag mit weniger als 50% Wahrscheinlichkeit regnet, also würde ein knallharter Mathematiker wohl den Schirm zuhause lassen.

Abbildung 1: Anfangend bei der aktuellen Stunde gibt Weather Underground die stündliche Regenwahrscheinlichkeit an.

Listing 1: chance-of-rain

    01 #!/usr/local/bin/perl -w
    02 use strict;
    03 use WWW::Wunderground::API;
    04 use DateTime;
    05 
    06 my $w = WWW::Wunderground::API->new(
    07   location => "Munich, Germany",
    08   api_key  => "0123456789abcdef",
    09   auto_api => 1,
    10 );
    11 
    12 for my $e (@{ $w->hourly }) {
    13   my $dt = DateTime->from_epoch(
    14     epoch => $e->{ FCTTIME }->{ epoch } );
    15 
    16   print "$dt: $e->{ pop }%\n";
    17 }

Mehr als nur Regen

Dabei kommt vom Underground-Server weit mehr zurück als nur die Regenwahrscheinlichkeit. Die in Zeile 12 von Listing 1 aufgerufene Methode hourly() liefert einen Array von stündlichen Wetterelementen des Tages. Wie der Datensalat in Abbildung 2 zeigt, verweist jedes Element auf eine Reihe von Schlüsseln wie humidity (Luftfeuchtigkeit), sky (Bewölkung), und schließlich steht unter FCTTIME das Datum und die Uhrzeit der Vorhersage. Listing 1 schnappt sich den Eintrag epoch aus dem Datumseintrag und wandelt die dort stehenden Unix-Sekunden seit 1970 mit dem Perl-Modul DateTime in ein lesbares und später leicht maschinell manipulierbares Format um.

Abbildung 2: In den stündlichen Wettervorhersagen steht nicht nur die Regenwahrscheinlichkeit (pop), sondern auch Luftfeuchtigkeit, Temperatur, Windgeschwindkeit und weitere Daten.

API-Key gegen Email-Adresse

Ganz offen aufs Internet legt der Wunderground-Server die Daten nicht, aber wer sich auf der Developer-Seite ([3]) mit einer gültigen Email-Adresse registriert, bekommt einen 16-stelligen hexadezimalen API-Key, mit dem der Service eine beschränkte Anzahl von täglichen Abfragen freischaltet. Wer den kostenlosen "Cumulus"-Plan wählt, und versichert, die Daten nicht zu kommerziellen Zwecken zu verwenden (Abbildung 3), muss auch keine Zahlungsmodalitäten angeben. In den vorgestellten Listings 1 und 2 ist dann der mit $api_key definierte String vor Gebrauch durch den eingeholten API-Key zu ersetzen (Abbildung 4).

Abbildung 3: Den für Weather Underground erforderlichen API-Key rückt der Server gegen eine gültige Email-Adresse heraus.

Abbildung 4: Der API-Key auf Weather Underground besteht aus einer 18-stelligen Hexzahl.

Schirm oder Risiko?

Um nun zu ermitteln, ob es an einem bestimmten Tag in den kritischen Zeitfenstern auf dem Weg zur und von der Arbeit regnet, und damit ein Schirm erforderlich ist, läuft das Skript in Listing 2 per Cronjob im Morgengrauen. Es definiert in Zeile 12 die kritischen Stunden, im Beispiel 8 und 9 Uhr morgens für den Weg zur Arbeit und 17, 18 und 19 Uhr abends für den Nachhauseweg nach Feierabend, falls mal wieder später wird. Im Parameter location in Zeile 15 ist der Ort des Wettergeschehens anzugeben, und unter api_key wieder der eingeholte Zugangsschlüssel. Die Option auto_api sorgt dafür, dass das CPAN-Modul mittels der in Zeile 20 aufgerufenen Methode hourly() gleich hinter den Kulissen die REST-Schnittstelle des Wunderground-Servers aufruft und den gewünschten JSON-Array zurückliefert.

Listing 2: umbrella

    01 #!/usr/local/bin/perl -w
    02 use strict;
    03 use WWW::Wunderground::API;
    04 use DateTime;
    05 use Log::Log4perl qw(:easy);
    06 use Getopt::Std;
    07 
    08 getopts "v", \my %opts;
    09 Log::Log4perl->easy_init(
    10   $opts{ v } ? $DEBUG : $ERROR);
    11 
    12 my @critical_hours = qw(8 9 17 18 19);
    13 
    14 my $w = WWW::Wunderground::API->new(
    15   location => "94114",
    16   api_key  => "0123456789abcdef",
    17   auto_api => 1,
    18 );
    19 
    20 for my $e (@{ $w->hourly }) {
    21   if( !scalar @critical_hours ) {
    22     last;
    23   }
    24 
    25   my $dt = DateTime->from_epoch(
    26     epoch => $e->{ FCTTIME }->{ epoch } );
    27 
    28   if( $dt->hour() == $critical_hours[0] ) {
    29     DEBUG "Checking hour ", $dt->hour(), 
    30       " ($e->{ pop }%)";
    31 
    32     if( $e->{ pop } > 50 ) {
    33       printf "Bring your Umbrella! " .
    34         "(%d%% rain at %s)\n",
    35           $e->{ pop }, $dt->hms();
    36       last;
    37     }
    38 
    39     shift @critical_hours;
    40   }
    41 }

Ohne Parameter aufgerufen gibt das Skript nur dann eine Meldung aus, falls es festgestellt hat, dass die Regenwahrscheinlichkeit in den kritischen Stunden einmal über 50% liegt. Dann schreibt die printf-Anweisung in Zeile 33 einen String wie

    Bring your Umbrella! (52% rain at 19:00:00)

auf die Standardausgabe und das nachfolgende Email-Skript in Listing 3 schickt die Meldung an den gespannt wartenden User. Besteht keine Gefahr, dass Niederschlag den Arbeitnehmer durchnässt, gibt das Skript auch nichts aus und der nachsetzende Emailer bricht ab, ohne eine Nachricht abzusenden.

Wer genau wissen möchte, was das Skript treibt, ruft es mit der Option -v auf, wie in Abbildung 5 gezeigt, und erhält die Regenwahrscheinlichkeiten in den kritischen Stunden mittels Log4perl-Meldungen angezeigt. Der Cronjob muss das Skript allerdings ohne -v, also zum Beispiel um 7 Uhr morgens mittels

    0 7 * * * umbrella | mandrill-email

aufrufen, damit der nachfolgende Emailer keine Nachricht schickt, falls es gar nicht regnet. Zu beachten ist weiterhin, dass die Skripts entweder in einem Verzeichnis stehen müssen, das der Cronjob findet, oder der Croneintrag muss den absolute Pfad zu den Skripts explizit festlegen.

Abbildung 5: Im Verbose-Modus gibt das Skript aus, welche Stundenmeldungen des Wetterberichts es prüft.

Email mit REST

Im Spam-Zeitalter ist es gar nicht mehr so einfach, automatisch Emails zu verschicken. In der Firma oder zuhause geht es noch, denn da kann der ISP verifizieren, wer da was treibt, und eventuell wildgewordenen Spammern schnell den Saft abdrehen. Um flexibel zu sein, wendet man sich am besten Drittanbieter. So erlaubt zum Beispiel die Firma Mandrill gegen einen Obulus die Nutzung ihrer Mailschleudern, aber Testaccounts dürfen auch ein paar Emails für lau schicken.

Auf http://mandrillapp.com kann sich der interessierte Nutzer, wie in Abbildung 6 gezeigt, mit einer gültigen Email registrieren und erhält dann einen 22-stelligen API-Key im Base64-Format, den Skripts später nutzen können, um mittels Mandrills REST-Server Emails zu versenden. Allerdings ist zu beachten, dass die From-Adresse immer die zur Registrierung angegebene gültige Email-Adresse sein muss, sonst verweigert Mandrill den Dienst, zumindest für die kostenlosen Test-Accounts.

Abbildung 6: Auf Mandrillapp.com gibt's einen Schlüssel für den Zugang zu deren Mailserver.

Listing 3: mandrill-email

    01 #!/usr/local/bin/perl -w
    02 use strict;
    03 use warnings;
    04 use LWP::UserAgent;
    05 use JSON qw(to_json);
    06 
    07 my $text = join "", <>;
    08 
    09 if( !length $text ) {
    10   exit 0;
    11 }
    12 
    13 my $api_key = "abcdefghi0123456789";
    14 my $from    = 'from@email-address.com';
    15 my $to      = 'to@email-address.com';
    16 
    17 my $ua = LWP::UserAgent->new();
    18 
    19 my $resp = $ua->post(
    20   "https://mandrillapp.com/api/1.0/" .
    21     "messages/send.json",
    22   Content => to_json( {
    23     key => $api_key,
    24     message => {
    25       text    => $text,
    26       subject => $text,
    27       from_email => $from,
    28       to => [ { email => $to } ],
    29     } } ),
    30 );
    31 
    32 if( $resp->is_error() ) {
    33   die "Sending mail failed: ", 
    34     $resp->message();
    35 }

Zeile 7 in Listing 3 schnappt sich die auf der Standardeingabe hereintrudelnden Textdaten und legt sie in der Variablen $text ab. Falls dieser String leer bleibt, weil das vorhergehende Skript in der Unix-Pipeline nichts ausgab, bricht Zeile 10 ohne Fehlermeldung ab, dann dann gibt es nichts zu senden.

In Zeile 13 ist der $api_key durch den auf der Mandrill-Seite eingeholten Testkey zu ersetzen und die Absenderadresse in $from in Zeile 14 durch die dort verwendete Registrations-Email. Die Empfängeradresse darf dann nach Belieben gesetzt werden. Das Skript kodiert dann die Email-Daten mittels der Funktion to_json des JSON-Moduls als JSON-Blob und schickt sie per POST-Request an den Mandrill-Server. Falls Zeile 32 keinen Fehler entdeckt, wurde die Email ordnungsgemäß versandt und der zur Arbeitstelle hetzende Arbeitnehmer kann auf der Notification-Screen seines Smartphones sehen, ob der Wetterdienst zur Mitführung eines Regenschirms rät oder nicht.

Infos

[1]

Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2015/02/Perl

[2]

"Probability of precipitation", http://en.wikipedia.org/wiki/Probability_of_precipitation

[3]

Weather Underground Developer API, http://www.wunderground.com/weather/api/

[4]

Mandrill Email Service, http://mandrillapp.com

Michael Schilli

arbeitet als Software-Engineer bei Yahoo in Sunnyvale, Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.