Apr 132014
 

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

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

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.

Advertisement:
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 . "\n\n";


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 . "\n\n";


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 . "\n\n";

## 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 . "\n\n";


## 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 invalid\n";
		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 invalid\n";
		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/" . $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/" . $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;
	}
}

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)