loadshedding scripts
This commit is contained in:
		
							
								
								
									
										149
									
								
								loadshedding-schedule.pl
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										149
									
								
								loadshedding-schedule.pl
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
#!/usr/bin/perl 
 | 
			
		||||
# Requires HTML::TableExtract and MIME::Lite
 | 
			
		||||
# On Ubuntu/Debian: sudo aptitude install libhtml-tableextract-perl libmime-lite-perl
 | 
			
		||||
#
 | 
			
		||||
# Downloads, caches, parses and prints the current load shedding schedule in Cape Town
 | 
			
		||||
# Written by Timothy Allen <tallen@allafrica.com>
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
use Getopt::Long;
 | 
			
		||||
use LWP::Simple;
 | 
			
		||||
use MIME::Lite;
 | 
			
		||||
use Time::Piece;
 | 
			
		||||
use Time::Seconds;
 | 
			
		||||
use List::MoreUtils;
 | 
			
		||||
use HTML::TableExtract;
 | 
			
		||||
 | 
			
		||||
my $url = "http://www.capetown.gov.za/en/electricity/Pages/LoadShedding.aspx";
 | 
			
		||||
my $schedule_file = "$ENV{HOME}/.loadshedding_schedule";
 | 
			
		||||
 | 
			
		||||
my @recipients = ();
 | 
			
		||||
my @zones;
 | 
			
		||||
my $verbose = 0;
 | 
			
		||||
GetOptions(
 | 
			
		||||
  "t|to=s{1,}" => \@recipients,
 | 
			
		||||
  "v|verbose" => \$verbose,
 | 
			
		||||
  "z|zones=i{1,}" => \@zones,
 | 
			
		||||
) or die "Usage: $0 [-t <email address> ... ] -z <zone number> ...\n";
 | 
			
		||||
 | 
			
		||||
if (scalar @zones < 1) {
 | 
			
		||||
  die "Usage: $0 -z <zone numbers>\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my ($last_schedule, $current_schedule, $last_str, $current_str, $last_time, $current_time, $diff);
 | 
			
		||||
$current_time = localtime;
 | 
			
		||||
$current_str  = $current_time->strftime('%Y-%m-%dT%H:%M:%S%z');
 | 
			
		||||
 | 
			
		||||
if (-f $schedule_file) {
 | 
			
		||||
  open STATUS, "<", $schedule_file or die $!;
 | 
			
		||||
  my @lines = <STATUS>;
 | 
			
		||||
  close STATUS or die $!;
 | 
			
		||||
  chomp($last_str      = $lines[0]) if ($lines[0] =~ s#^\s*Time:\s*##i);
 | 
			
		||||
  chomp($last_schedule = join "\n", @lines);
 | 
			
		||||
  if ($last_str && $last_str =~ /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+\d{4}/) {
 | 
			
		||||
    $last_time = Time::Piece->strptime($last_str, '%Y-%m-%dT%H:%M:%S%z') or warn "Time error";
 | 
			
		||||
    $diff = $current_time - $last_time;
 | 
			
		||||
  } else {
 | 
			
		||||
    undef $last_str;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Only refetch the page every hour
 | 
			
		||||
my $cached = 0;
 | 
			
		||||
if (!$last_str || int($diff->hours) >= 1) {
 | 
			
		||||
  my $content = LWP::Simple::get $url or die "Couldn't get $url";
 | 
			
		||||
  if ($content =~ m#<div[^>]*id="WebPartWPQ2"[^>]*>\s*(<table[^>]*>.*?</table>)#ims) {
 | 
			
		||||
    $current_schedule = $1;
 | 
			
		||||
  } else {
 | 
			
		||||
    die "Unable to parse schedule; load shedding page must have changed.\n";
 | 
			
		||||
  }
 | 
			
		||||
  open  STATUS, ">", $schedule_file or die $!;
 | 
			
		||||
  print STATUS "Time:   $current_str\n";
 | 
			
		||||
  print STATUS "$current_schedule\n";
 | 
			
		||||
  close STATUS or die $!;
 | 
			
		||||
} else {
 | 
			
		||||
  $current_schedule = $last_schedule;
 | 
			
		||||
  $cached = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my ($html, $table);
 | 
			
		||||
$current_schedule =~ s#<.?span[^>]*>##imsg;
 | 
			
		||||
$current_schedule =~ s#\s*(<[^>]*>)\s*#$1#imsg;
 | 
			
		||||
 | 
			
		||||
$html = HTML::TableExtract->new(
 | 
			
		||||
  br_translate => 0,
 | 
			
		||||
);
 | 
			
		||||
$html->parse($current_schedule);
 | 
			
		||||
if (scalar $html->tables() < 1) {
 | 
			
		||||
  unlink $schedule_file if (-f $schedule_file);
 | 
			
		||||
  die "No table found";
 | 
			
		||||
}
 | 
			
		||||
$table = $html->first_table_found();
 | 
			
		||||
 | 
			
		||||
# Create a hash (ref) of hash (ref)s with the load shedding info
 | 
			
		||||
my $shedding = {};
 | 
			
		||||
my ($title, $header_row, @rows) = $table->rows;
 | 
			
		||||
my ($empty, @headers) = @$header_row;
 | 
			
		||||
foreach my $row (@rows) {
 | 
			
		||||
  my $zone_times = {};
 | 
			
		||||
  my ($stage, @times) = @$row;
 | 
			
		||||
  foreach my $i (0 .. $#headers) {
 | 
			
		||||
    my $key = $headers[$i];
 | 
			
		||||
    $zone_times->{$key} = $times[$i];
 | 
			
		||||
  }
 | 
			
		||||
  $shedding->{$stage} = $zone_times;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my $day = shift @$title;
 | 
			
		||||
$day =~ s/.*?(\d+\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec).*?\s+\d{4}?).*?/$1/ or undef $day;
 | 
			
		||||
my $subject = "Load shedding times";
 | 
			
		||||
$subject .= " for $day" if defined $day;
 | 
			
		||||
 | 
			
		||||
my $output = "If there is load shedding today";
 | 
			
		||||
$output   .= " ($day)" if defined $day;
 | 
			
		||||
$output   .= ", it will be on the following schedule. We will alert you if and when the City of Cape Town website announces the actual commencement of load shedding.\n\n";
 | 
			
		||||
 | 
			
		||||
foreach my $zone (List::MoreUtils::uniq @zones) {
 | 
			
		||||
  $output .= "Load shedding hours for zone $zone";
 | 
			
		||||
  $output .= " on $day" if defined $day;
 | 
			
		||||
  $output .= "\n";
 | 
			
		||||
  foreach my $stage (sort keys %$shedding) {
 | 
			
		||||
    # Get applicable sets of hours for this zone in each stage
 | 
			
		||||
    my @hours;
 | 
			
		||||
    foreach my $key (sort keys %{$shedding->{$stage}}) {
 | 
			
		||||
      push @hours, $key if grep /\b$zone\b/, $shedding->{$stage}->{$key};
 | 
			
		||||
    }
 | 
			
		||||
    $output .= sprintf "%-10s ", "$stage:";
 | 
			
		||||
    if (scalar @hours > 0) {
 | 
			
		||||
      my $str = join ", ", @hours;
 | 
			
		||||
      $str =~ s/(.*),/$1 and/;
 | 
			
		||||
      $output .= "$str\n";
 | 
			
		||||
    } else {
 | 
			
		||||
      $output .= "No load shedding in zone $zone";
 | 
			
		||||
      $output .= " for $day" if defined $day;
 | 
			
		||||
      $output .= "\n";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  $output .= "\n";
 | 
			
		||||
}
 | 
			
		||||
if ($cached) {
 | 
			
		||||
  $output .= "Time since last schedule download: " . $diff->pretty . "\n" if $verbose;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (scalar @recipients < 1) {
 | 
			
		||||
  print $output;
 | 
			
		||||
} else {
 | 
			
		||||
  foreach my $to (@recipients) {
 | 
			
		||||
    my $message = MIME::Lite->new(
 | 
			
		||||
      From    => 'Loadshedding Alerts <load@node.org.za>',
 | 
			
		||||
      To      => $to,
 | 
			
		||||
      Subject => $subject,
 | 
			
		||||
      Data    => $output,
 | 
			
		||||
    );
 | 
			
		||||
    # send the message
 | 
			
		||||
    $message->send();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# vim: set indentexpr= expandtab sw=2 softtabstop=2 tw=10000 :
 | 
			
		||||
							
								
								
									
										94
									
								
								loadshedding-status.pl
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								loadshedding-status.pl
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#!/usr/bin/perl 
 | 
			
		||||
#
 | 
			
		||||
# Downloads, parses and prints the current load shedding status.
 | 
			
		||||
# It is suggested that you run this script every 5-20 minutes.
 | 
			
		||||
#
 | 
			
		||||
# Written by Timothy Allen <tallen@allafrica.com>
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
use Getopt::Long;
 | 
			
		||||
use LWP::Simple;
 | 
			
		||||
use MIME::Lite;
 | 
			
		||||
use Time::Piece;
 | 
			
		||||
use Time::Seconds;
 | 
			
		||||
 | 
			
		||||
my $url = "http://www.capetown.gov.za/en/electricity/Pages/LoadShedding.aspx";
 | 
			
		||||
my $status_file = "$ENV{HOME}/.loadshedding_status";
 | 
			
		||||
 | 
			
		||||
my @recipients = ();
 | 
			
		||||
my $verbose = 0;
 | 
			
		||||
GetOptions(
 | 
			
		||||
  "t|to=s{1,}" => \@recipients,
 | 
			
		||||
  "v|verbose" => \$verbose,
 | 
			
		||||
) or die "Usage: $0 [-t <email address> ... ] \n";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
my ($last_status, $current_status, $last_str, $current_str, $last_time, $current_time, $diff);
 | 
			
		||||
$current_time = localtime;
 | 
			
		||||
$current_str  = $current_time->strftime('%Y-%m-%dT%H:%M:%S%z');
 | 
			
		||||
 | 
			
		||||
if (-f $status_file) {
 | 
			
		||||
  open STATUS, "<", $status_file or die $!;
 | 
			
		||||
  my @lines = <STATUS>;
 | 
			
		||||
  close STATUS or die $!;
 | 
			
		||||
  chomp($last_status = $lines[0]) if ($lines[0] =~ s#^\s*Status:\s*##i);
 | 
			
		||||
  chomp($last_str    = $lines[1]) if ($lines[1] =~ s#^\s*Time:\s*##i);
 | 
			
		||||
  if ($last_str) {
 | 
			
		||||
    $last_time = Time::Piece->strptime($last_str, '%Y-%m-%dT%H:%M:%S%z');
 | 
			
		||||
    $diff = $current_time - $last_time;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my ($content, $output);
 | 
			
		||||
unless ($content = get $url) {
 | 
			
		||||
  warn "Couldn't get $url";
 | 
			
		||||
  $content = get "http://www.capetown.gov.za/loadshedding/Loadshedding.html" or die "Couldn't get alternate url";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if ($content =~ m#<div[^>]*id="WebPartWPQ3"[^>]*>.*?<table[^>]*>(.*?)</table>#ims) {
 | 
			
		||||
  $current_status = $1;
 | 
			
		||||
  $current_status =~ s#\s*(<br[^>]*>|\n)\s*# #imsg;
 | 
			
		||||
  $current_status =~ s#<.*?>##imsg;
 | 
			
		||||
} elsif ($content =~ m#<div[^>]*class="MainHeadText"[^>]*>.{0,1500}?<div style="display:block" class="alertbox">(.*?)</div>#ims) {
 | 
			
		||||
  $current_status = $1;
 | 
			
		||||
  $current_status =~ s#\s*(<br[^>]*>|\n)\s*# #imsg;
 | 
			
		||||
  $current_status =~ s#<.*?>##imsg;
 | 
			
		||||
} else {
 | 
			
		||||
  die "Unable to parse status; load shedding page must have changed.\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (($current_status !~ /no items/i) &&
 | 
			
		||||
    (!defined $last_status || $current_status ne $last_status)) {
 | 
			
		||||
  open  STATUS, ">", $status_file or die $!;
 | 
			
		||||
  print STATUS "Status: $current_status\n";
 | 
			
		||||
  print STATUS "Time:   $current_str\n";
 | 
			
		||||
  close STATUS or die $!;
 | 
			
		||||
  if (defined $last_status) {
 | 
			
		||||
    $output .= "The City of Cape Town's website indicates that the load shedding status has changed from: $last_status to: $current_status\n";
 | 
			
		||||
  } else {
 | 
			
		||||
    $output .= "Load shedding status for the City of Cape Town: $current_status\n";
 | 
			
		||||
  }
 | 
			
		||||
  $output .= "Time since last change: " . $diff->pretty . "\n" if defined $diff and $verbose;
 | 
			
		||||
} else {
 | 
			
		||||
  $output .= "Load shedding status: $current_status\n" if $verbose;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (defined $output) {
 | 
			
		||||
  if (scalar @recipients < 1) {
 | 
			
		||||
    print $output;
 | 
			
		||||
  } else {
 | 
			
		||||
    foreach my $to (@recipients) {
 | 
			
		||||
      my $message = MIME::Lite->new(
 | 
			
		||||
        From    => 'Loadshedding Alerts <load@node.org.za>',
 | 
			
		||||
        To      => $to,
 | 
			
		||||
        Subject => 'Load shedding status change',
 | 
			
		||||
        Data    => $output,
 | 
			
		||||
      );
 | 
			
		||||
      # send the message
 | 
			
		||||
      $message->send();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# vim: set indentexpr= expandtab sw=2 softtabstop=2 tw=10000 :
 | 
			
		||||
		Reference in New Issue
	
	Block a user