Weather Underground ( wunderground ) XML API with Perl

Weather data is pretty cool to play with. Especially when playing with home automation or just turning the current weather conditions into a meme.

For example:
Weather for Omaha, Nebraska on 4/13/2014 at 16:00

[text]
raging@virtualbox3:~/weather$ perl grabweather.pl
Forecast – Today:
weekday: Sunday
high: 61
low: 27
precip: 100
cond: Thunderstorm
month: 4
day: 13
hour: 22

Forecast – Tomorrow:
weekday: Monday
high: 43
low: 25
precip: 10
cond: Partly Cloudy
month: 4
day: 14
hour: 22

Forecast – Day After Tomorrow:
weekday: Tuesday
high: 66
low: 37
precip: 0
cond: Partly Cloudy
month: 4
day: 15
hour: 22

Current Conditions:
conditions: Overcast
temp: 34
relative_humidity: 86
wind: 10.9
wind_gust: 0
pressure_trend: –
feelslike: 26
precip_1hr: 0

Radar Static Image: /tmp/68127-radar.png
Animated Radar Image: /tmp/68127-amimatedradar.gif
[/text]

More info about Weather Underground’s API: www.wunderground.com/weather/api

Keep reading for my bit of Perl to grab it and make it usable.


[perl]
use LWP::Simple;
use XML::Simple;
use XML::Validate::LibXML;
use warnings;
use strict;

###### Notes ######
## Since I’m in the US and using Fahrenheit and Miles per hour and Inches, these are the units I’m grabbing.
## Please see the API reference and change the values if you’re using a better measurement system

###### Configuration ######

## See API Reference at http://www.wunderground.com/weather/api/d/docs?d=index
## You will have to supply your own API key
my $APIKEY = ”;

## location follows the examples listed under query
## see API reference page http://www.wunderground.com/weather/api/d/docs?d=data/index
my $location = "";

## where radar images are stored
my $cachedir = ‘/tmp’; ## no trailing slash

###### Programs Go Here ######

## Using Forecast data http://http://www.wunderground.com/weather/api/d/docs?d=data/forecast
## get_forecast takes 2 options. Location and day. 0 is today, 1 is tomorrow, 2 is day after tomorrow
print "Forecast – Today:n";
my ($high, $low, $precip, $cond, $month, $day, $hour, $weekday) = get_forecast($location, ‘0’);
print "weekday:t" . $weekday . "n" .
"high:t" . $high . "n" .
"low:t" . $low . "n" .
"precip:t" . $precip . "n" .
"cond:t" . $cond . "n" .
"month:t" . $month . "n" .
"day:t" . $day . "n" .
"hour:t" . $hour . "nn";

print "Forecast – Tomorrow:n";
($high, $low, $precip, $cond, $month, $day, $hour, $weekday) = get_forecast($location, ‘1’);
print "weekday:t" . $weekday . "n" .
"high:t" . $high . "n" .
"low:t" . $low . "n" .
"precip:t" . $precip . "n" .
"cond:t" . $cond . "n" .
"month:t" . $month . "n" .
"day:t" . $day . "n" .
"hour:t" . $hour . "nn";

print "Forecast – Day After Tomorrow:n";
($high, $low, $precip, $cond, $month, $day, $hour, $weekday) = get_forecast($location, ‘2’);
print "weekday:t" . $weekday . "n" .
"high:t" . $high . "n" .
"low:t" . $low . "n" .
"precip:t" . $precip . "n" .
"cond:t" . $cond . "n" .
"month:t" . $month . "n" .
"day:t" . $day . "n" .
"hour:t" . $hour . "nn";

## Using Conditions data http://www.wunderground.com/weather/api/d/docs?d=data/conditions
## get_conditions only takes 1 option, Location
print "Current Conditions:n";
my ($conditions, $temp, $relative_humidity, $wind, $wind_gust, $pressure_trend, $feelslike, $precip_1hr) = get_conditions($location);
print "conditions:t" . $conditions . "n" .
"temp:t" . $temp . "n" .
"relative_humidity:t" . $relative_humidity . "n" .
"wind:t" . $wind . "n" .
"wind_gust:t" . $wind_gust . "n" .
"pressure_trend:t" . $pressure_trend . "n" .
"feelslike:t" . $feelslike . "n" .
"precip_1hr:t" . $precip_1hr . "nn";

## Using Radar data http://www.wunderground.com/weather/api/d/docs?d=layers/radar
## get_radar only takes 1 option, Location
## gets a png image of current radar conditions
my $radarimage = get_radar($location);
print "Radar Static Image:t" . $radarimage . "n";

## Using Radar data http://www.wunderground.com/weather/api/d/docs?d=layers/radar
## get_animated_radar only takes 1 option, Location
## almost identical to get_radar, except grabs animated gif
my $animatedradarimage = get_animated_radar($location);
print "Animated Radar Image:t" . $animatedradarimage . "n";

###### Danger, Thar Be Dragons ######

sub get_forecast {
my $forecast_location = shift;
my $counter = shift;

my $query_URL = ‘http://api.wunderground.com/api/’ . $APIKEY . ‘/forecast/q/’ . $forecast_location . ‘.xml’;

my $raw_data = get( $query_URL );
die "Couldn’t get URL: ", $query_URL unless defined $raw_data;

my $xml_validator = new XML::Validate::LibXML;
if ( !$xml_validator->validate($raw_data) ) {
my $intro = "Document is invalidn";
my $message = $xml_validator->last_error()->{message};
my $line = $xml_validator->last_error()->{line};
my $column = $xml_validator->last_error()->{column};
die "Error: $intro $message at line $line, column $column";
}

my $xml = XML::Simple->new;
my $forecast_data_ref = $xml->XMLin( $raw_data );
my $forecast_data = $forecast_data_ref->{forecast}->{simpleforecast}->{forecastdays}->{forecastday};

## much more data returned than what I’m returning. See the API docs for more info. http://www.wunderground.com/weather/api/d/docs?d=data/forecast

my $forecast_high = $forecast_data->[$counter]->{high}->{fahrenheit};
my $forecast_low = $forecast_data->[$counter]->{low}->{fahrenheit};
my $forecast_precip = $forecast_data->[$counter]->{pop};
my $forecast_cond = $forecast_data->[$counter]->{conditions};
my $forecast_month = $forecast_data->[$counter]->{date}->{month};
my $forecast_day = $forecast_data->[$counter]->{date}->{day};
my $forecast_hour = $forecast_data->[$counter]->{date}->{hour};
my $forecast_weekday = $forecast_data->[$counter]->{date}->{weekday};

return($forecast_high, $forecast_low, $forecast_precip, $forecast_cond, $forecast_month, $forecast_day, $forecast_hour, $forecast_weekday);
}

sub get_conditions {
my $conditions_location = shift;

my $query_URL = ‘http://api.wunderground.com/api/’ . $APIKEY . ‘/conditions/q/’ . $conditions_location . ‘.xml’;

my $raw_data = get( $query_URL );
die "Couldn’t get URL: ", $query_URL unless defined $raw_data;

my $xml_validator = new XML::Validate::LibXML;
if ( !$xml_validator->validate($raw_data) ) {
my $intro = "Document is invalidn";
my $message = $xml_validator->last_error()->{message};
my $line = $xml_validator->last_error()->{line};
my $column = $xml_validator->last_error()->{column};
die "Error: $intro $message at line $line, column $column";
}

my $xml = XML::Simple->new;
my $conditions_data_ref = $xml->XMLin( $raw_data );
my $conditions_data = $conditions_data_ref->{current_observation};

## much more data returned than what I’m returning. See the API docs for more info. http://www.wunderground.com/weather/api/d/docs?d=data/conditions

my $conditions_weather = $conditions_data->{weather};
my $conditions_temp_f = int($conditions_data->{temp_f});
my $conditions_relative_humidity = $conditions_data->{relative_humidity};
my $conditions_wind_mph = $conditions_data->{wind_mph};
my $conditions_wind_gust_mph = $conditions_data->{wind_gust_mph};
my $conditions_pressure_trend = $conditions_data->{pressure_trend};
my $conditions_feelslike_f = int($conditions_data->{feelslike_f});
my $conditions_precip_1hr_in = $conditions_data->{precip_1hr_in};

$conditions_relative_humidity =~ s/s?%//;

if ( $conditions_precip_1hr_in <= -999) {
$conditions_precip_1hr_in = 0;
}
if ( $conditions_feelslike_f <= -999) {
$conditions_feelslike_f = 0;
}
if ( $conditions_wind_gust_mph <= -999) {
$conditions_wind_gust_mph = 0;
}
if ( $conditions_wind_mph <= -999) {
$conditions_wind_mph = 0;
}
if ( $conditions_relative_humidity <= -999) {
$conditions_relative_humidity = 0;
}
if ( $conditions_temp_f <= -999) {
$conditions_temp_f = 0;
}

return($conditions_weather, $conditions_temp_f, $conditions_relative_humidity, $conditions_wind_mph, $conditions_wind_gust_mph, $conditions_pressure_trend, $conditions_feelslike_f, $conditions_precip_1hr_in);
}

sub get_animated_radar{
my $radar_location = shift;

my $cachefile = $cachedir . "/" . $radar_location . "-amimatedradar.gif";

## Many other options available when building request string. See the API docs for more info. http://www.wunderground.com/weather/api/d/docs?d=layers/radar

my $status = getstore("http://api.wunderground.com/api/&quot; . $APIKEY . "/animatedradar/q/" . $radar_location . ".gif?width=600&height=600&newmaps=1&noclutter=1&num=5", $cachefile);

if ( is_success($status) ) {
return($cachefile);
} else {
die "Download Failed: " . $status;
}
}

sub get_radar{
my $radar_location = shift;

my $cachefile = $cachedir . "/" . $radar_location . "-radar.png";

## Many other options available when building request string. See the API docs for more info. http://www.wunderground.com/weather/api/d/docs?d=layers/radar

my $status = getstore("http://api.wunderground.com/api/&quot; . $APIKEY . "/radar/q/" . $radar_location . ".png?width=600&height=600&newmaps=1&noclutter=1", $cachefile);

if ( is_success($status) ) {
return($cachefile);
} else {
die "Download Failed: " . $status;
}
}
[/perl]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: