#!/usr/bin/perl -w # # Alarm clock # Eventually to be merged with MisterHouse? # # Last updated by gossamer on Tue Oct 14 16:56:50 EST 2003 # use strict; use AI::DateInWords; use AI::TimeInWords; use Astro::MoonPhase; use Data::Dumper; use Date::Manip; use Festival::Client; use PDA::Pilot; use Weather::Underground; use vars qw( $VERSION $DEBUG $NO_SOUND $NO_SPEECH $SKIP_SECRET $Festival $text $username $mp3_bin $alarm_clock_mp3_1 $alarm_clock_mp3_2 $todo_db @months @moonphases ); ################################################################## # BEGIN USER SETUP # ################################################################## $DEBUG = 0; $NO_SOUND = 1; $NO_SPEECH = 1; $username = "Ricky"; $sound_player = "/usr/bin/music123"; $sound_1 = "/share/Music/Shriekback/Cradle_Song.mp3"; $sound_2 = "/share/sound/Music/Lou_Reed/CFS-20s_Disk/15.Perfect_Day.ogg"; $SKIP_SECRET = 0; $todo_db = $ENV{PILOT_TODODB} || $ENV{HOME} . "/pilot/backup/ToDoDB.pdb"; # # END SETUP # # # Bits of data we need later # $VERSION = 0.2; @months = ("January", "February","March","April","May","June","July","August","September","October","November","December"); @moonphases = ("New Moon", "First quarter", "Full moon", "Last quarter", "New Moon"); ################################################################## # BEGIN MAIN # ################################################################## # # Initial song # # Play first song, unless sound turned off or debug turned on `$sound_player $sound_1` unless ($DEBUG || $NO_SOUND); # # Initial greeting # # Greeting to $username $text = "G'day, $username.\nWakey, wakey, rise and shine ...\n" . "The sea is rough but the weather is fine " . "and its time to get up anyway. You really will live.\n" . "I promise. Or perhaps its a threat. Now listen up!\n"; # Say it!!! if ($NO_SPEECH) { print $text; } else { $text =~ s/\n/ /g; $Festival = new Festival::Client "localhost"; $Festival->say($text); } # # Second song # # Play second song, unless sound turned off or debug is on `$sound_player $sound_2` unless ($DEBUG || $NO_SOUND); # # Second greeting # # Greeting to $username $text = "Good morning, $username. This is your alarm clock.\n"; # # Current time # # What time is it? my $converter = new AI::TimeInWords; $text .= "$username, the time is now " . $converter->LongTime() . ".\n"; # # Current date # $converter = new AI::DateInWords; $text .= "The date is " . $converter->LongDate() . ".\n"; # # Week or weekend? # my $date = ParseDate("today"); if (Date_IsWorkDay($date)) { $text .= "Its one of them boring work days.\n"; } else { $text .= "Yay! Its the weekend!\n"; } # # Current weather in Melbourne # my $weather_ok = 1; if (my $weather = Weather::Underground->new(place => "Melbourne, Australia")) { if (my $w = $weather->getweather()) { $text .= "Current temperature is " . $w->[0]->{"celsius"} . " degrees, " . "with " . $w->[0]->{"humidity"} . "\% humidity.\n" . "Conditions include " . $w->[0]->{"conditions"} . ".\n"; } else { $weather_ok = 0; } } else { $weather_ok = 0; } $text .= "I can't seem to get the weather for you, though, sorry.\n" unless $weather_ok; # # Current/Next phases of the moon # my @phase_parts = phase(); $text .= sprintf "The moon is %0.0f days old.\n", $phase_parts[2]; my ($before, $after, $after_days); my @phases = phasehunt(); for (0..4) { if ($phases[$_] < time) { $before = $moonphases[$_]; } else { $after = $moonphases[$_]; # TODO figure out how many days it'll happen in #my $next_phase_date = ParseDate(localtime($phases[$_])); last; } } $text .= "The previous moonphase was " . lc($before) . ", and the next one is " . lc($after) . ".\n"; #"It'll be " . lc($after) . " on " . $after_days . ".\n"; # # Today's Appointments # # What appointments do we have today? # eg "From 10am to 11am there is an appointment named 'PCA'" # $text .= "$username, today's appointments include: "; # XXX TODO XXX TODO XXX TODO XXX TODO # # Housekeping for ToDoDB Stuff # my($file) = PDA::Pilot::File::open($todo_db) || die "Unable to PDA::Pilot::File::open('$todo_db'): $!"; # Watch it: The above error check doesn't work - open() does not # return a meaningful error indicator. $DEBUG && warn "Opened DB file $todo_db"; # Unpack the application block information to get the category names my($appblock) = PDA::Pilot::ToDo::UnpackAppBlock($file->getAppBlock); my(@categorynames) = @{$appblock->{categoryName}}; $DEBUG && warn "Got category names: " . join(", ", @categorynames); my($numrecords) = $file->getRecords(); $DEBUG && warn "Found $numrecords total todo items."; # Collect up list of todo items my (@overdue, @due, @undated); # ToDo lists foreach (0 .. $numrecords) { my($record) = $file->getRecord($_) || next; my($todo) = PDA::Pilot::ToDo::Unpack($record); next if $todo->{"deleted"}; next if $todo->{"complete"}; next if ($todo->{"secret"} && $SKIP_SECRET); $DEBUG && print Dumper $todo->{"due"}; #if ((!$todo->{"due"}) && # ($todo->{"priority"} >= 3)) { if (!$todo->{"due"}) { push(@undated, $todo); } else { # ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) my ($now_mday,$now_mon,$now_year) = (localtime())[3,4,5]; my ($mday, $mon, $year) = @{$todo->{"due"}}[3,4,5]; if (($now_mday == $mday) && ($now_mon == $mon) && ($now_year == $year)) { # due today push(@due, $todo); } elsif (($now_mday >= $mday) && ($now_mon >= $mon) && ($now_year >= $year)) { # overdue (or due today, by the logic, but we just eliminated them) push(@overdue, $todo); } } } # # Overdue ToDo Items # # NB: We rely on these arriving sorted earliest->latest, which they are $text .= "$username, these are the overdue to do items.\n" . "Please pay attention.\n"; my ($old_mday) = 0; my ($old_mon) = 0; my ($old_year) = 0; my ($counter); foreach my $todo (@overdue) { # ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) #print "*******\n"; $DEBUG && print Dumper $todo->{"due"}; #print "*******\n"; my ($mday, $mon, $year) = @{$todo->{"due"}}[3,4,5]; if (($mday > $old_mday) || ($mon > $old_mon) || ($year > $old_year)) { $counter = 0; } if ($counter) { $text .= "Item " . ++$counter . " for " . $months[$mon] . " " . $mday . "is: "; } else { $text .= "Due on " . $months[$mon] . " " . $mday . ", we have:\n"; $counter++; } $text .= $todo->{"description"} . ".\n"; if ($todo->{"note"}) { $text .= "The attached note reads:\n" . $todo->{"note"} . ".\n Note Ends.\n"; } $old_mday = $mday; $old_mon = $mon; $old_year = $year; } # # Todo items due today # $text .= "$username, these are the to do items which are due today.\n"; # TODO read today list, ordered by priority, reading priority # once for each priority that has items in it, $text .= "In decending order of priority, we have:\n"; foreach my $priority (1 .. 5) { my $counter = 0; foreach my $todo (@due) { next unless $todo->{"priority"} == $priority; if ($counter) { $text .= "Item " . ++$counter . " for " . "priority $priority is: "; } else { $counter++; $text .= "Items of priority $priority include: "; } $text .= $todo->{"description"} . ".\n"; if ($todo->{"note"}) { $text .= "The attached note reads:\n" . $todo->{"note"} . ".\n Note Ends.\n"; } } } # # Random selection of undated Todo items # $text .= "Oh, and here's a couple of the undated to do items: "; # picks 5 items at random from list, or all if list < 5. # then order by priority, and do as for today but skipping date. my $chance = 1 / @undated * 5; foreach my $todo (@undated) { if (rand() <= $chance) { # The ToDo hash contains the following fields: # category # priority # complete # deleted # secret # due @($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst); # description # note $text .= "This undated to do item in category " . $categorynames[$todo->{"category"}] . " is of priority " . $todo->{"priority"} . ".\nIts description is " . $todo->{"description"} . ".\n"; if ($todo->{"note"}) { $text .= "The attached note reads:\n" . $todo->{"note"} . ".\n Note Ends.\n"; } } } # The docs say to do this but when you run it it complains it # doesn't exist, and since we don't modify it the end of the # proggy closes it well enough for the purpose. #PDA::Pilot::File::close($file); # # Goodbye message # $text .= "That's all, $username, so I hope you've got it all.\n"; $text .= "Now, up and at 'em, woman."; # # Output the second section of speech # # Say it!!! if ($NO_SPEECH) { print $text; } else { $text =~ s/\n/ /g; $Festival->say($text); } # # Ende. #