#!/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 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 ... ] -z ...\n"; if (scalar @zones < 1) { die "Usage: $0 -z \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 = ; 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#]*id="WebPartWPQ2"[^>]*>\s*(]*>.*?)#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 ', To => $to, Subject => $subject, Data => $output, ); # send the message $message->send(); } } # vim: set indentexpr= expandtab sw=2 softtabstop=2 tw=10000 :