00001 #!/usr/bin/perl -w
00002 package NWSAlert;
00003 use strict;
00004 use XML::Parser;
00005 use base qw(XML::SAX::Base);
00006 use Date::Manip;
00007 use Data::Dumper;
00008 use Getopt::Std;
00009 use LWP::Simple;
00010
00011 my $alerts;
00012 my $currAlert;
00013 my $currInfo;
00014
00015 sub StartDocument {
00016 $alerts = [];
00017 }
00018
00019 sub StartTag {
00020 my ($expat, $name, %atts) = @_;
00021 if ($name eq "cap:alert"){
00022 $currAlert = {};
00023 }
00024
00025 if ($name eq "cap:info") {
00026 $currInfo = {};
00027 }
00028
00029 }
00030
00031
00032 sub EndTag {
00033 my ($expat, $name, %atts) = @_;
00034
00035 if ($name eq "cap:alert") {
00036 push @$alerts, $currAlert;
00037 }
00038 if ($name eq "cap:info") {
00039 push (@{$currAlert->{'cap:info'}}, $currInfo);
00040 }
00041 }
00042
00043 sub Text {
00044 my ($expat, $text) = @_;
00045
00046 if ($expat->within_element('cap:info')) {
00047 $currInfo->{$expat->current_element} = $expat->{Text} if ($expat->{Text}
00048 =~ /\w+/);
00049
00050 } elsif ($expat->within_element('cap:alert')) {
00051 $currAlert->{$expat->current_element} = $expat->{Text} if ($expat->{Text} =~
00052 /\w+/);
00053 }
00054
00055 }
00056
00057 ############################################
00058 sub getWarnings {
00059
00060 my $state = shift;
00061 $state =~ tr/[A-Z]/[a-z]/;
00062 my $parser = new XML::Parser(Style => 'Stream');
00063 my $capfile = get "http://www.weather.gov/alerts/$state.cap" or
00064 die "cannot retrieve alert data";
00065 $parser->parse($capfile);
00066 return $alerts;
00067 }
00068
00069 sub getEffectiveWarnings {
00070 my $date = shift;
00071 my $state = shift;
00072 my $geo = shift;
00073 my @results;
00074 if (!$alerts) {
00075 getWarnings($state);
00076 }
00077 my $alert;
00078 my $info;
00079
00080 $date = ParseDate($date);
00081 $date = Date_ConvTZ($date, "", "UTC");
00082 $date = UnixDate($date, "%O");
00083 my @dates;
00084 while ($alert = shift @$alerts) {
00085 push @dates, $alert->{'cap:sent'};
00086 while ($info = shift @{$alert->{'cap:info'}}) {
00087 if ($info->{'cap:effective'} && Date_Cmp($date, "$info->{'cap:effective'}") >= 0 &&
00088 Date_Cmp($date, "$info->{'cap:expires'}") < 0 &&
00089 (!$geo || $info->{'cap:geocode'} == $geo)) {
00090 push @results, $info;
00091 }
00092 }
00093 }
00094 if (scalar(@dates) > 1) {
00095 return @dates, @results;
00096 } else {
00097 return $dates[0], @results;
00098 }
00099 return @results;
00100 }
00101
00102 our ($opt_v, $opt_t, $opt_T, $opt_l, $opt_u, $opt_d);
00103
00104 my $name = 'NWS-Alerts';
00105 my $version = 0.1;
00106 my $author = 'Lucien Dunning';
00107 my $email = 'ldunning@gmail.com';
00108 my $updateTimeout = 10*60;
00109 my $retrieveTimeout = 30;
00110 my @types = ('swlocation', 'updatetime', 'alerts');
00111 my $dir = "./";
00112
00113 getopts('Tvtlu:d:');
00114
00115 if (defined $opt_v) {
00116 print "$name,$version,$author,$email\n";
00117 exit 0;
00118 }
00119
00120 if (defined $opt_T) {
00121 print "$updateTimeout,$retrieveTimeout\n";
00122 exit 0;
00123 }
00124 if (defined $opt_l) {
00125 open(LOCS, "bp16mr06.dbx") or die "couldn't open bp16mr06.dbx";
00126 my $search = shift;
00127 while(<LOCS>) {
00128 if (m/$search/i) {
00129 my @entry = split /[|]/;
00130 print "$entry[6]::$entry[3], $entry[0]\n";
00131 }
00132 }
00133 exit 0;
00134 }
00135
00136 if (defined $opt_t) {
00137 foreach (@types) {print; print "\n";}
00138 exit 0;
00139 }
00140
00141 if (defined $opt_d) {
00142 $dir = $opt_d;
00143 }
00144
00145 my $loc = shift;
00146
00147 if (!(defined $loc && !$loc eq "")) {
00148 die "Invalid usage";
00149 }
00150
00151 my $state;
00152 my $locstr;
00153 # its a big file that we have to search linearly, so we keep a simple cache
00154 if (open(CACHE, "$dir/NWSAlert_$loc")) {
00155 ($state, $locstr) = split /::/, <CACHE>;
00156 chomp $locstr;
00157 close(CACHE);
00158 }
00159
00160 if (!$state || !$locstr) {
00161 ($state, $locstr) = doLocation($loc);
00162 if ($state && $locstr) {
00163 my $file = "$dir/NWSAlert_$loc";
00164 open(CACHE, ">$file") and
00165 print CACHE "${state}::${locstr}\n";
00166 } else { die "cannot find location"; }
00167 }
00168
00169 my ($updatetime, @warnings) = getEffectiveWarnings("now", $state, $loc);
00170
00171 foreach my $warning (@warnings) {
00172 my $txt = $warning->{'cap:description'};
00173 for my $line (split /\n/, $txt) {
00174 print "alerts::$line\n" if ($line =~ m/\w+/);
00175 }
00176 }
00177 if (!@warnings) {
00178 print "alerts::No Warnings\n";
00179 }
00180
00181 print "swlocation::$locstr,$state\n";
00182
00183 print "updatetime::Last Updated on " .
00184 UnixDate(Date_ConvTZ(ParseDate($updatetime)), "%b %d, %I:%M %p %Z") . "\n";
00185
00186 sub doLocation {
00187 my $code = shift;
00188 open(LOCS, "bp16mr06.dbx") or die "couldn't open bp16mr06.dbx";
00189 while(<LOCS>) {
00190 if (m/$code/) {
00191 my @entry = split /[|]/;
00192 return ($entry[0], $entry[3]);
00193 }
00194 }
00195 }