Current Weather

If you'd like to be able to give your Asterisk users the current weather and optionally the forecast, you can install the Weather::Underground module from CPAN and then use a Perl script to pull the weather conditions from Wunderground on a periodic basis. The weather conditions can be converted, using Festival, to a wave file which can be played by Asterisk, to a user.

Begin by installing the Weather::Underground Perl module:

     su
     perl -MCPAN -e shell
     install Weather::Underground

Then, put this perl script in a convenient location.

/etc/asterisk/local/FetchWeather.pl:

     #!/usr/bin/perl
     #
     # Get the local weather from Weather Underground.
     #
     #
     # Usage
     # -----
     #
     # FetchWeather.pl [--Forecast] [--Place=place] [output_file]
     #
     # Forecast          Switch indicating that the forecast is to be retrieved
     #                   as well as the weather data.  If the forecast can be
     #                   fetched, it will be appended to the current conditions.
     #
     # Place             The name of place whose weather data is to be retrieved.
     #                   This should be a string like "Norwood, Massachusetts".
     #                   The default place name is defined by the $DEFAULTPLACE
     #                   variable herein ("Norwood, Massachusetts").
     #
     # output_file       Optional output file name prefix.  Two files will be
     #                   generated, one with a ".txt" extension and one with a
     #                   ".wav" extension.  The prefix given here will be used to
     #                   name those files.  The default file name prefix is defined
     #                   by the $DEFAULTFILE variable herein
     #                   ("/home/monitor/wunderground/Weather-Norwood").
     #
     #
     # Description
     # -----------
     #
     # This program gets the current weather information (and optionally the
     # forecast) from Wunderground and produces a wave file which can be played by
     # Asterisk, when the user asks for the weather.
     #
     ##############################################################################
     #
     # Include all of the modules that we use.
     #
     use Getopt::Long;
     use LWP::UserAgent;                     # One include covers all of them!
     #db use LWP::Debug qw(+);               # LWP debugging, if needed
     use URI::URL;                           # Or, perhaps not
     use Weather::Underground;
     #
     # Local information, if not specified.
     #
     my $DEFAULTPLACE = "Norwood, Massachusetts";
     my $DEFAULTFILE = "/home/monitor/wunderground/Weather-Norwood";
     #
     # Wunderground XML API URL, used to pull forecasts.  Don't include the
     # "http://" on the front.  We add it automatically.
     #
     my $FORECASTURL = "api.wunderground.com/auto/wui/geo/ForecastXML/" .
                           "index.xml?query=";
     #
     # Table to convert Wunderground wind directions into something Festival
     # can say.
     #
     my %WindDir =
         (
         "N" => "North",
         "NNE" => "North North East",
         "NE" => "North East",
         "ENE" => "East North East",
         "E" => "East",
         "ESE" => "East South East",
         "SE" => "South East",
         "SSE" => "South South East",
         "S" => "South",
         "SSW" => "South South West",
         "SW" => "South West",
         "WSW" => "West South West",
         "W" => "West",
         "WNW" => "West North West",
         "NW" => "North West",
         "NNW" => "North North West"
         );
     #
     # Options.
     #
     my $Place = "";                         # Place to retrieve weather for
     my $OutputFile = "";                    # Prefix to use for generated files
     #
     # Option switches.
     #
     $WantForecast = 0;                      # Set true if we want the forecast
     #
     # The forecast, if there is one.
     #
     $Forecast = "";
     #
     # Process the command line options.  If this fails, give a summary of the
     # program's usage.
     #
     ProcessOptions() || UsageInfo();
     #
     # Use the default place, if we weren't given one.
     #
     $Place = $DEFAULTPLACE if ($Place eq "");
     #
     # Get the output file prefix, if any.
     #
     $OutputFile = shift;
     $OutputFile = $DEFAULTFILE if (length($OutputFile) <= 0);
     #
     # Open the output file.
     #
     open(TXTFILE, ">".$OutputFile.".txt")
         or die("Error, could not open ".$OutputFile.".txt: ".$@."\n");
     #
     # Get the weather from Wunderground.
     #
     my $WeatherHand = Weather::Underground->new(
         place => $Place,
         debug => 0)
         || die("Error, could not create new weather object: ".$@."\n");
     $Weather = $WeatherHand->get_weather()
         || die("Error, calling get_weather() failed: $@\n");
     #
     # Dump the weather data to a text file.
     #
     my ($Key, $Value);
     print(TXTFILE "Weather search for ".$Place.":\n\n");
     foreach (@$Weather)
         {
         print(TXTFILE "Matched weather for ".${%{$_}}{place}.":\n");
      while (($Key, $Value) = each %{$})
          {
          print(TXTFILE "\t$Key = $Value\n");
          }
      }

#
# If the user wants the forecast, see if we can get it. #
if ($WantForecast)

      {
      my ($URLFCast, $Req, $Resp, $FCast);
      #
      # Set up the HTTP headers used to fetch the forecast.  Just in case
      # they're watching, we pretend to be Mozilla.
      #
      my $HTTPHdrs = HTTP::Headers->new(
          'Accept' => 'text/xml,application/xml,application/xhtml+xml,' .
                      'text/html;q=0.9,text/plain;q=0.8,*/;q=0.5',
          'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,;q=0.7',
          'Accept-Language' => 'en-us,en;q=0.5',
          'User-Agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.0; ' .
                          'en-US; rv:1.7.13) Gecko/20060414');
      #
      # Set up a user agent to do the fetching.  The agent looks like a
      # full-fledged browser.  And, it handles just about everything the remote
      # site can throw at it.
      #
      my $UserAgent = LWP::UserAgent->new(
          requestsredirectable => ['GET', 'HEAD', 'POST']);
      #
      # Build a URL that we can use to look up the forecast.
      #
      $URLFCast = $Place;
      $URLFCast =~ s/^\s+//; $URLFCast =~ s/\s+$//;
      $URLFCast =~ s/\s,\s+/,/g; $URLFCast =~ s/\s+/+/g;
      $URLFCast = URI::URL->new("http://".$FORECASTURL.$URLFCast);
      #
      # See if we can get the forecast.
      #
      $Req = HTTP::Request->new("GET", $URLFCast, $HTTPHdrs);
      $Resp = $UserAgent->request($Req);
      #
      # If we got a forecast, process all of the day forecasts within.
      #
      if ($Resp->is_success)
          {
          $Resp->content =~ /\<txt_forecast.?\>(.+?)\<\/txt_forecast/gis;
          $_ = $1;
          #
          # Loop through all of the day forecasts.
          #
          while (m/\<forecastday.?\>(.+?)\<\/forecastday/gis)
              {
              $FCast = $1;
              #
              # If we have a title, compose the forecast for that period.
              #
              if ($FCast =~ /\<title.?\>(.+?)\<\/title/)
                  {
                  $Forecast .= ("Forecast for ".lc($1));
                  if ($FCast =~ /\<fcttext.?\>(.+?)\<\/fcttext/)
                      {
                      #
                      # The droid has trouble with a few expressions and some
                      # of the punctuation that Wunderground likes to use.
                      #
                      $FCast = $1;
                      $FCast =~ s/\s\r?\n\s/ /gs;
                      $FCast =~ s/\s\.\.\.\s*/, /gs;
                      $FCast =~ s/\'/\'\'/gs;
                      $FCast =~ s/\bmph\b/miles per hour/gis;
                      $FCast =~ s/\bmph\.]/miles per hour./gis;
                      $Forecast .= (". ".$FCast." ");
                      }
                  else { $Forecast .= " is not available. "; }
                  }
              }
          }
      }

#
# Turn the wind speed and direction into English. #
my ($Wind);

     if ($Weather->[0]->{wind_milesperhour} eq "0.0")
         { $Wind = "the winds are calm"; }
     else
         {
         $Wind = "wind direction is ".
             ((exists($WindDir{$Weather->[0]->{wind_direction}}))
                 ? $WindDir{$Weather->[0]->{wind_direction}}
                     : $Weather->[0]->{wind_direction})."  " .
             "at ".$Weather->[0]->{wind_milesperhour}." miles per hour";
         }
     #
     # Figure out how to end the weather information.
     #
     my ($Sec, $Min, $Hour, $Finish);
     ($Sec, $Min, $Hour) = localtime(time());
     if (($Hour >= 23) || (($Hour >= 0) && ($Hour < 4)))
       { $Finish = "good night"; }
     elsif ($Hour >= 18) { $Finish = "pleasant evening"; }
     else { $Finish = "pleasant day"; }
     #
     # Let's get Festival to turn this into a wave file that Asterisk can use.
     #
     system("echo 'Weather for ".$Place.". " .
         "Current temperature is ".$Weather->[0]->{temperature_fahrenheit}." " .
             "degrees, " .
         "conditions are ".$Weather->[0]->{conditions}.", " .
         "visibility is ".$Weather->[0]->{visibility_miles}." miles, and " .
         $Wind.". " .
         "Current humidity is ".$Weather->[0]->{humidity}." percent, " .
         "and the dew point is ".$Weather->[0]->{dewpoint_fahrenheit}.". " .
         $Forecast .
         "Have a ".$Finish.".' " .
         "| /usr/bin/text2wave -F 8000 -o ".$OutputFile.".wav");
     #
     # We're getting outta here.
     #
     close(TXTFILE); exit 0;
     ##############################################################################
     sub ProcessOptions
     #
     # Process command line options and set the appropriate variables.
     #
     {
     my ($Result);
     #
     # Get options using standard option processor.
     #
     $Result = &GetOptions(
         "Forecast", \$WantForecast,         # Switch for forecast as well
         "Place=s", \$Place);                # The place to get weather for
     #
     # Return success if options gotten OK.
     #
     return ($Result);
     }
     ##############################################################################
     sub UsageInfo
     #
     # Routine to display program usage information.  Called whenever the command
     # line parameters don't make sense.
     #
     {
     #
     # Exit after giving help.
     #
     print <<USAGE ;