150 lines
4.5 KiB
Perl
Executable File
150 lines
4.5 KiB
Perl
Executable File
#!/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 :
|