html/weather.pl


   1 # Irssi weather as statusbar item and forecast command
   2 
   3 # Fixes in this version
   4 #
   5 # 1. Fixed issue with N/A°F in the windchill/feelslike variable.
   6 # 2. Added more information in the /forecast command.
   7 # 3. Added an extended function for three day forecasts.
   8 # 4. Moved the extended function info /forecast -e (yippie! FLAGS!)
   9 # 5. Added a /forecast -o option to print the info to the active window
  10 # 6. 2.0 has a TOTAL rewrite on how to get the data. We no longer use weather.com its now Geo::Weather data
  11 # 7. Removed the three day extended forecast for the 2.0 version, will rewrite it later.
  12 # 8. 2.1 now has 10 day extended forecast, takes slightly longer to load
  13 # 9. 2.2 has returned to writing statusbar info to a file to save time in window switching
  14 # 10. 2.3 allows theme customization of weather in statusbar (written for ardya)
  15 
  16 
  17 # Use the following format in your theme file 
  18 # weather_display = "$0%R>%n%_$2%_%G>%n$4";
  19 #
  20 # $0 is temperature
  21 # $2 is feelslike (windchill/headindex)
  22 # $4 is current conditions (windy or partly cloudy)
  23 
  24 # - You have to modify this line to the path of your LWP-dir
  25 use lib '/usr/lib/perl5/vendor_perl/5.6.1';
  26 use Irssi;
  27 use Irssi::TextUI;
  28 use Geo::Weather;
  29 use 5.6.0;
  30 
  31 use vars qw($VERSION %IRSSI $zipcode $refresh $show_type $last_refresh $refresh_tag);
  32 
  33 $VERSION = '2.3';
  34 %IRSSI = (
  35     authors     => 'GrayWolf',
  36     contact     => 'graywolf@i-differ.net',
  37     name        => 'Weather.pl',
  38     description => 'Put local weather information in your statusbar as well as add a forecast command',
  39     license     => 'Public Domain'
  40 );
  41 
  42 #
  43 ## Variable defaults, can be changed with /set in Irssi, to save trouble I recommend setting your zipcode here
  44 #
  45 if (!$zipcode) {
  46   $zipcode = "46260";
  47 }
  48 $zip = $zipcode;
  49 $country = "us";
  50 $refresh = "900";
  51 $show_type = "yes";
  52 $in_celsius = "no";
  53 
  54 #
  55 ## Bind our commands
  56 #
  57 Irssi::command_bind("weather", "show_usage");
  58 Irssi::command_bind("forecast", "show_weather");
  59 Irssi::command_bind("weathsecret", "get_weather_for_status");
  60 
  61 #
  62 ## Usage information
  63 #
  64 sub show_usage {
  65     Irssi::print("[Usage] Forecast Statusbar script v$VERSION");
  66     Irssi::print("/weather : shows usage info");
  67     Irssi::print("/forecast <zipcode> : Show current forecast for <zipcode>");
  68     Irssi::print("/forecast -o <zipcode> : Show current forecast for <zipcode> in the active window \(print to channel\)");
  69     Irssi::print("/forecast -e <zipcode> : Show extended ten day forecast for <zipcode>");
  70     Irssi::print("/set weather_refresh : Sets how often the statusbar is updated");
  71     Irssi::print("/set weather_zip : The zipcode of the area you want displayed in the statusbar");
  72     Irssi::print("/set weather_show_type (yes/no) : Choose if you want to see F/C in the statusbar");
  73     Irssi::print("/set weather_in_celsius (yes/no) : Choose if you want to see the temperature in celsius");
  74     Irssi::print("/statusbar <bar> add weather : see /help statusbar for more information.");
  75 }
  76 
  77 #
  78 ## First thing run the status grab
  79 #
  80 get_weather_for_status();
  81 
  82 #
  83 ## Get down to work
  84 #
  85 sub weather_us {
  86 my $winfo = new Geo::Weather;
  87 $winfo->{timeout} = 5; # set timeout to 5 seconds instead of the default of 10
  88 
  89 # Get the $zip from a /forecast command
  90 $zip = $_[0];
  91 
  92 # Currently only the US is supported, hope to add more countries later
  93   if ( $country == "us" ) {
  94     # Lets go get the information
  95     my $current = $winfo->get_weather($zip);
  96     my $forecast = $winfo->report_forecast();
  97 
  98     $temperature = $current->{temp};
  99     $feelslike = $current->{heat};
 100     $description = $current->{cond};
 101     $wind = $current->{wind};
 102     $wind =~ s/\<br\>/\n/g;
 103     $wind =~ s/\<.+?\>//sg;
 104     $wind =~ s/&nbsp;//g;
 105     $dewp = $current->{dewp};
 106     $humi = $current->{humi};
 107     $visb = $current->{visb};
 108     $visb =~ s/\<br\>/\n/g;
 109     $visb =~ s/\<.+?\>//sg;
 110     $baro = $current->{baro};
 111     $baro =~ s/\<br\>/\n/g;
 112     $baro =~ s/\<.+?\>//sg;
 113     return($temperature,$feelslike,$description,$wind,$dewp,$humi,$visb,$baro,$forecast);
 114   } else {
 115     Irssi::print("Unable to get weather for $country");
 116     return(0,0,0);
 117   }
 118 }
 119 
 120 # Statusbar formatting
 121 sub get_weather_for_status {
 122 
 123   ($temperature,$feelslike,$description,$therest) = weather_us($zipcode);
 124 
 125   # If the user doesn't want to see F in the statusbar lets strip that out
 126   if ( $show_type eq 'yes' ) {
 127     $temperature .= 'F';
 128     $feelslike .=  'F';
 129   }
 130   # If the user wants his temp in celsius lets convert it here
 131   if ($in_celsius eq 'yes') {
 132      $convert = $temperature;
 133      $convert =~ s/F//g;
 134      $temperature = ((5 / 9) * ($convert - 32));
 135      $temperature = sprintf('%d', $temperature);
 136      $feelcon = $feelslike;
 137      $feelcon =~ s/F//g;
 138      $feelslike = ((5 / 9) * ($feelcon - 32));
 139      $feelslike = sprintf('%d', $feelslike);
 140      # Now we have the temp in celsius, do they want to see the C?
 141      if ($show_type eq 'yes') {
 142        $temperature .= 'C';
 143        $feelslike .= 'C';
 144      }
 145   }
 146   chomp($temperature);
 147   chomp($feelslike);
 148   chomp($description);
 149   open STATUS_FILE, ">.irssi/weather.status" or die "Can't write to status file: $!";
 150   print STATUS_FILE "$temperature,$feelslike,$description";
 151   close STATUS_FILE;
 152 }
 153 
 154 # Status bar information
 155 
 156 sub theme_format {
 157   open GET_STATUS, "<.irssi/weather.status" or die "Can't open status file: $!";
 158 
 159   my $themed = "";
 160   my $themecmd = "";
 161 
 162   while ($line = <GET_STATUS>) {
 163     ($temperature,$feelslike,$description) = split(/,/, $line);
 164   }
 165   chomp($description);
 166   $description =~ s/ /\_\_/g;
 167   $themed = Irssi::current_theme->format_expand("{weather_display $temperature  $feelslike  $description}",Irssi::EXPAND_FLAG_IGNORE_REPLACES);
 168 
 169   $statusbar_output = $themed;
 170   return $statusbar_output;
 171   Irssi::statusbar_items_redraw('weather');
 172   close GET_STATUS;
 173 }
 174 
 175 sub weather {
 176   my ($item, $get_size_only) = @_;
 177 
 178   $sbar_out = theme_format();
 179   $sbar_out =~ s/  /\//g;
 180   $sbar_out =~ s/\_\_/ /g;
 181   chomp($sbar_out);
 182   $item->default_handler($get_size_only, "{sb $sbar_out}", undef, 1 );
 183 }
 184 
 185 sub show_weather {
 186   my ($commands, $item, $witem) = @_;
 187   ($flag, $thezip) = split(/ /, $commands);
 188   if ($flag =~ /^\s*$/) {
 189     $thezip = $zipcode if $thezip < 1;
 190     ($temperature,$feelslike,$description,$wind,$dewp,$humi,$visb,$baro) = weather_us($thezip);
 191     Irssi::active_win()->print("Information for $thezip - Temp: $temperature, Feels Like: $feelslike, Currently: $description, Dew Point: $dewp, Humidity: $humi, Visibility: $visb, Pressure: $baro, Wind: $wind");
 192   } elsif ($flag =~ /^\d*$/) {
 193     $flag = $zipcode if $flag < 1;
 194     ($temperature,$feelslike,$description,$wind,$dewp,$humi,$visb,$baro) = weather_us($flag);
 195     Irssi::active_win()->print("Information for $flag - Temp: $temperature, Feels Like: $feelslike, Currently: $description, Dew Point: $dewp, Humidity: $humi, Visibility: $visb, Pressure: $baro, Wind: $wind");
 196     $flag = $zipcode;
 197     weather_us($flag);
 198   } elsif ($flag =~ /\-o/) {
 199     if ($witem) {
 200       $thezip = $zipcode if $thezip < 1;
 201       ($temperature,$feelslike,$description,$wind,$dewp,$humi,$visb,$baro) = weather_us($thezip);
 202       $witem->command("MSG ".$witem->{name}." Information for $thezip - Temp: $temperature, Feels Like: $feelslike, Currently: $description, Dew Point: $dewp, Humidity: $humi, Visibility: $visb, Pressure: $baro, Wind: $wind");
 203       $thezip = $zipcode;
 204       weather_us($thezip);
 205     }
 206   } elsif ($flag =~ /\-e/) {
 207     if ($witem) {
 208       $thezip = $zipcode if $thezip < 1;
 209       ($temperature,$feelslike,$description,$wind,$dewp,$humi,$visb,$baro,$forecast) = weather_us($thezip);
 210       my $count = 0;
 211       my $active = 0;
 212       my $day = 0;
 213       my @days = ();
 214 
 215       $forecast =~ s/\<.+?\>//sg;
 216       $forecast =~ s/&nbsp;//g;
 217       $forecast =~ s/&deg;/F/g;
 218       $forecast =~ s/FF/F/g;
 219       $forecast =~ s/^\s+//gm;
 220 
 221       @fore = split(/\n/, $forecast);
 222       $endres = undef;
 223 
 224       foreach $line (@fore) {
 225         if ($count > 3) {
 226           if ($active <= 4) {
 227             if ($active eq 0) {
 228               # This is necessary to prevent wrapping into the next call of /forecast -e
 229               $endres .= "\n";
 230             }
 231             chomp($line);
 232             $line =~ s/ \/ /\//g;
 233             $line =~ s/ \%/\% chance of precipitation/g;
 234             $endres .= "$line ";
 235             $active ++;
 236           } else {
 237             $active = 1;
 238             $day ++;
 239             if ($day < 11) {
 240               chomp($line);
 241               $line =~ s/ \/ /\//g;
 242               $line =~ s/ \%/\% chance of precipitation/g;
 243               $endres .= "\n$line";
 244             }
 245           }
 246         }
 247         $count ++;
 248       }
 249       
 250       @days = split(/\n/, $endres);
 251       Irssi::active_win()->print("Extended Ten Day Forecast for $thezip");
 252       Irssi::active_win()->print("$days[1]");
 253       Irssi::active_win()->print("$days[2]");
 254       Irssi::active_win()->print("$days[3]");
 255       Irssi::active_win()->print("$days[4]");
 256       Irssi::active_win()->print("$days[5]");
 257       Irssi::active_win()->print("$days[6]");
 258       Irssi::active_win()->print("$days[7]");
 259       Irssi::active_win()->print("$days[8]");
 260       Irssi::active_win()->print("$days[9]");
 261       Irssi::active_win()->print("$days[10]");
 262     }
 263   } else {
 264      Irssi::active_win()->print("Cannot print to the active window");
 265   }
 266 }
 267 
 268 
 269 sub refresh_weather {
 270     Irssi::statusbar_items_redraw('weather');
 271 }
 272 
 273 sub read_settings {
 274   $zipcode = Irssi::settings_get_str('weather_zip');
 275   $show_type = Irssi::settings_get_str('weather_show_type');
 276   $in_celsius = Irssi::settings_get_str('weather_in_celsius');
 277   $refresh = Irssi::settings_get_int('weather_refresh');
 278   $refresh = 1 if $refresh < 1;
 279   return if $refresh == $last_refresh;
 280   $last_refresh = $refresh;
 281   Irssi::timeout_remove($refresh_tag) if $refresh_tag;
 282   $refresh_tag = Irssi::timeout_add($refresh * 1000, 'get_weather_for_status', undef);
 283   $refresh_tag = Irssi::timeout_add($refresh * 1000, 'refresh_weather', undef);
 284 }
 285 
 286 Irssi::settings_add_int('misc', 'weather_refresh', $refresh);
 287 Irssi::settings_add_str('misc', 'weather_zip', $zipcode);
 288 Irssi::settings_add_str('misc', 'weather_show_type', $show_type);
 289 Irssi::settings_add_str('misc', 'weather_in_celsius', $in_celsius);
 290 
 291 Irssi::statusbar_item_register('weather', undef, 'weather');
 292 Irssi::statusbars_recreate_items();
 293 
 294 read_settings();
 295 Irssi::signal_add('setup changed', 'read_settings');
 296 
 297 #
 298 ## TODO
 299 #
 300 # 1. Add other countries -- (not sure if this will ever happen, feel free....)
 301 # 2. Fix the read_settings so that changes are immediate -- (just update your zipcode at the top of the script and don't bug me)
 302 # 3. Allow the extended function to show temps in C (currently only F) -- (I don't want to)
 303 # 4. I want to add some type of error check for the zipcodes, so that you can't enter an invalid one
 304 # 5. Make the lookup background or async so it doesn't tie up the irssi session -- (all I can find is fork() which requires a major rewrite.
 305 #    Wait the 10 damn seconds you lazy gits.)