Skip to content

Instantly share code, notes, and snippets.

@jacoby
Last active February 11, 2017 09:37
Show Gist options
  • Save jacoby/9dc5b7baf2f068a7aa2d to your computer and use it in GitHub Desktop.
Save jacoby/9dc5b7baf2f068a7aa2d to your computer and use it in GitHub Desktop.
Perl program to search Forecast.IO API for current weather conditions, based upon latitude and longitude, and display them in Linux using notify-send
#!/usr/bin/env perl
# forecast.pl is Copyright (C) 2014, by Dave Jacoby.
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation; either external linkversion 1, or (at your option) any later versionexternal link, or
#
# b) the "Artistic License".
# interface to forecast.io which uses Linux notify-send to
# send a variety of weather information to the user's awareness
# Usage:
# --current , -c cureent conditions
# --alerts, -a alerts; current weather alerts if any
# --hourly, -h hourly forecasts for the next four hours
# --daily, -d forecast for the next two days
# You can use all the flags at once. No flags defaults to current conditions
use feature qw{ say state } ;
use strict ;
use warnings ;
use utf8 ;
use Carp ;
use Data::Dumper ;
use DateTime ;
use Getopt::Long ;
use JSON ;
use LWP::UserAgent ;
use YAML qw{ LoadFile } ;
my $config = config() ;
my $url =
'https://api.forecast.io/forecast/'
. $config->{ apikey } . '/'
. ( join ',', map { $config->{ $_ } } qw{ latitude longitude } ) ;
my $agent = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } ) ;
my $response = $agent->get( $url ) ;
if ( $response->is_success ) {
my $content = $response->content ;
my $forecast = decode_json $content ;
my $current = $forecast->{ currently } ;
my $alerts = $forecast->{ alerts } ;
my $hourly = $forecast->{ hourly } ;
my $daily = $forecast->{ daily } ;
say join "\n",
( map { join "\t", '+', $_, ref $forecast->{ $_ } } sort keys %$forecast ), ''
if $config->{ verbose } ;
current_conditions( $current ) if $config->{ current } ;
daily_forecasts( $daily ) if $config->{ daily } ;
hourly_forecasts( $hourly ) if $config->{ hourly } ;
handle_alerts( $alerts ) if $config->{ alerts } ;
}
exit ;
sub current_conditions {
my $current = shift ;
my $temp = int $current->{ temperature } ;
my $summary = $current->{ summary } ;
my $liketemp = int $current->{ apparentTemperature } ;
my $icon = $current->{ icon } ;
my $title = qq{Currently ${temp}°F} ;
my $body = qq{$summary. Feels like ${liketemp}°F.\nPowered by Forecast.} ;
say $icon if $config->{ verbose } ;
my $png = get_icons( $icon ) ;
notify( $title, $body , $png ) ;
}
sub handle_alerts {
my $alerts = shift ;
my $body = qq{Powered by Forecast.} ;
for my $alert ( @$alerts ) {
my $title = $alert->{ title } ;
my $warning = 0 ;
$warning = 1 if $title =~ /warning/i ;
notify( $title, $body ) ;
}
}
sub hourly_forecasts {
my $hourly = shift ;
my $icon = $hourly->{ icon } ;
my $summary = $hourly->{ summary } ;
my $data = $hourly->{ data } ;
my $body = $summary . "\n\n" ;
for my $hour ( @$data[ 0 .. 3 ] ) {
my $summary = $hour->{ summary } ;
my $dt = DateTime->from_epoch( epoch => $hour->{ time } ) ;
$body .= join " ", $dt->hour_12(), $dt->am_or_pm(), '-', $summary, "\n" ;
}
$body .= qq{Powered by Forecast.} ;
my $title = 'Four-Hour Forecast' ;
my $png = get_icons( $icon ) ;
notify( $title, $body , $png ) ;
}
sub daily_forecasts {
my ( $daily, $f ) = @_ ;
my $icon = $daily->{ icon } ;
my $summary = $daily->{ summary } ;
my $data = $daily->{ data } ;
my $body ;
for my $day ( @$data[ 1 .. 2 ] ) {
my $summary = $day->{ summary } ;
my $dt = DateTime->from_epoch( epoch => $day->{ time } ) ;
my $max = int $day->{ temperatureMax } ;
my $min = int $day->{ temperatureMin } ;
my $icon = $day->{ icon } ;
$body .= join ' ',
$dt->day_name() . ':',
$summary, "\n\t" ,
'H:',
$max . '°F',
'- L:',
$min . '°F',
"\n" ;
}
$body .= qq{Powered by Forecast.} ;
my $title = 'Two-Day Forecast' ;
my $png = get_icons( $icon ) ;
notify( $title, $body , $png ) ;
}
# Handles the actual notification, using Linux's notify-send
sub notify {
my $title = shift ;
my $body = shift ;
my $icon = shift ;
$body = $body || '' ;
$icon = $icon || $ENV{HOME} . '/Pictures/Icons/icon_black_muffin.jpg' ;
`notify-send "$title" "$body" -i $icon ` ;
}
# show the icons
sub get_icons {
my $icon = shift ;
my $path = $ENV{ HOME } . '/Pictures/forecast.io/' ;
# these are the icon names that forecast.io will use.
my @icons = qw{
clear-day
clear-night
cloudy
fog
partly-cloudy-day
partly-cloudy-night
rain
sleet
snow
wind
} ;
# I found my weather icon set online, which had vastly
# different icons. I cannot distribute them, as they aren't mine
# and I don't hold the copyright, but you can find/make your own.
# Coming soon?
# hail
# thunderstorm
# tornado
if ( grep m{$icon} , @icons ) {
return $path . $icon . '.png' ;
}
return $path . 'default' . '.png' ;
}
# Reads configuration data from YAML file. Dies if no valid config file
# if no other value is given, it will choose current
sub config {
my $config_file = $ENV{ HOME } . '/.forecast.yaml' ;
my $output = {} ;
if ( defined $config_file && -f $config_file ) {
my $output = LoadFile( $config_file ) ;
GetOptions(
'verbose' => \$output->{ verbose },
'alerts' => \$output->{ alerts },
'current' => \$output->{ current },
'daily' => \$output->{ daily },
'hourly' => \$output->{ hourly },
) ;
$output->{ current } = 1
if !$output->{ alerts }
and !$output->{ hourly }
and !$output->{ daily } ;
say Dumper $output if $output->{ verbose } ;
return $output ;
}
croak( 'No Config File' ) ;
}
__DATA__
What ~/.forecast.yaml should look like, set for Lafayette
---
apikey: You can't have my API key
latitude: 40.422778
longitude: -86.915278
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment