#!/usr/bin/perl -w ###################################### # $Id: classalarm 107 2004-03-09 06:23:12Z steve $ ###################################### # $Log$ # Revision 1.9 2004/03/09 06:23:12 steve # better error handeling # # Revision 1.8 2003/10/02 15:54:29 steve # * removed personal references to "steve" # * uses "wakeup" instead of "wakeup.steve" # * uses the old Config::Tiny file now that i know what was wrong with it # # Revision 1.7 2003/07/11 15:07:33 steve # now evals the config file instead of using the broken Config::Tiny # # Revision 1.6 2003/02/14 00:28:42 steve # re-did alarm system. it should now handle class skipping properly # # Revision 1.5 2003/01/29 15:25:26 steve # dies more gracefully # # Revision 1.4 2003/01/29 05:00:01 steve # added a default user # # Revision 1.3 2003/01/16 06:22:23 steve # now reads from a system config file # # Revision 1.2 2003/01/16 00:16:49 steve # merged changes from mu # # ###################################### use strict; use Schedule::Class; use Festival::Client; use Config::Tiny; use PhysStat; use Palm::PDB; use Palm::Datebook; use Palm::StdAppInfo; use Getopt::Std; use Time::Repeat; use Time::ParseDate; our( $opt_i, # interval $opt_s, # skip next class $opt_n, # fake the current time $opt_h, # help ); getopts( "n:si:h" ); my $Conf = Config::Tiny->new(); $Conf = Config::Tiny->read("/etc/schedules.conf"); my $basedir; my $defaultuser; ##### config vars ##### # minutes before class to start alerting my $mins_before = 90; # minutes # minutes before class to wake the user up my $wake_before = 120; # minutes # normally my $alert_every = 15; # minutes # and then, my $faster_mins_before = 15; # minutes # alert every my $faster_alert_every = 5; # minutes # stop bugging the user when they're this late my $give_up_mins = 50; # minutes # delay between alerts, in minutes my $delay = 1; $basedir = $Conf->{_}->{'basedir'}; $defaultuser = $Conf->{_}->{'defaultuser'}; $mins_before = $Conf->{_}->{'mins_before'}; $wake_before = $Conf->{_}->{'wake_before'}; $alert_every = $Conf->{_}->{'alert_every'}; $faster_mins_before= $Conf->{_}->{'faster_mins_before'}; $give_up_mins = $Conf->{_}->{'give_up_mins'}; $faster_alert_every = $Conf->{_}->{'faster_alert_every'}; $delay = $Conf->{_}->{'delay'}; $defaultuser = $ENV{'USER'} unless $defaultuser; my $schedule = "$basedir/$defaultuser.xml"; my $skipflag = "/var/tmp/skippingclass"; my $skipnext = "/var/tmp/skipnextclass"; my $physstat_user = 'steve'; my $pidfile = "/var/run/classalarm/classalarm.pid"; #### end config ##### # handy "constant" my $MIN_PER_HOUR = 60; usage() if $opt_h; if( $opt_s ){ die "skip-next-class flag, $skipflag, already set.\n" if -e $skipflag; open SKIP, ">$skipnext" or die "cannot create skipflag: $skipnext: $!"; print SKIP ""; close SKIP; print "set the skip-next-class flag: $skipnext\n"; exit; } die "$0 daemon already running. If it has died, try removing $pidfile\n" if -e $pidfile; open PID, ">$pidfile" or die( "cannot write to $pidfile: $!\n" ); print PID "$$\n"; close PID; # allow for the config file to be reloaded $SIG{HUP} = \&rehash; $SIG{TERM} = \&clear_pid; $SIG{__DIE__} = \&clear_pid; #$SIG{KILL} = \&clear_pid; $SIG{QUIT} = \&clear_pid; $SIG{INT} = \&clear_pid; # allow the time to be overridden for testing purposes my $now = sub {time}; $now = sub {$opt_n} if $opt_n; # load the schedule rehash(); set_user( $physstat_user ); # change the interval $delay = $opt_i if $opt_i; do{ # 0 1 2 3 4 5 6 7 8 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(&$now); # current number of minutes in the day my $cmins = $hour * $MIN_PER_HOUR + $min; # get the physical status mode my $physstat = get_mode(); # get the next class information my @next = get_next(&$now); my $skipping = -e $skipflag; my $til_end = 0; if( @next ){ my $class = $next[1]; $next[0] =~ /(\d+):(\d+)\-(\d+):(\d+)/; my $shour = $1; my $smin = $2; my $smins = $shour * $MIN_PER_HOUR + $smin; my $ehour = $3; my $emin = $4; my $room = $next[2]; my $emins = $ehour * $MIN_PER_HOUR + $emin; my $tilmins = $smins - $cmins; $til_end = $emins - $cmins; if( $tilmins <= $mins_before && $tilmins > 1 ){ # if class is in the time period specified or less if( -e $skipnext ){ system("rm", "-f", $skipnext); system("touch", $skipflag); $skipping = 1; } }elsif( $til_end == 1 ){ `rm -f $skipflag` if $skipping; } } # look at the next class if the current one's being skipped @next = get_next(&$now + $til_end * 60) if $skipping; if( @next ){ my $class = $next[1]; $next[0] =~ /(\d+):(\d+)\-(\d+):(\d+)/; my $shour = $1; my $smin = $2; my $smins = $shour * $MIN_PER_HOUR + $smin; my $ehour = $3; my $emin = $4; my $room = $next[2]; my $emins = $ehour * $MIN_PER_HOUR + $emin; my $tilmins = $smins - $cmins; if( $tilmins <= $wake_before && $tilmins > 0 && $physstat =~/sleep/i ){ unless( 0 && grep /[^(grep)].+wakeup/, `ps wx` ){ my $result = system "wakeup", "-c", "/home/$physstat_user/.wakeuprc.xml", "-r", "Your next event, $class, starts soon."; warn "error running wakeup script: $!\n" if $result; } }elsif( $tilmins <= $mins_before && $tilmins > 0 ){ # if class is in the time period specified or less if( $physstat=~/here/i && $tilmins <= $mins_before && ( ( $tilmins > $faster_mins_before ) ? $tilmins % $alert_every == 0 : $tilmins % $faster_alert_every == 0 ) ){ my $timetill = $tilmins ? "in ".speakable_time($tilmins): "now"; system "alertsteve", "Your next event, $class, starts $timetill."; } if ($tilmins <= $faster_mins_before && $physstat =~ /sleep/i){ # drag in the heavy artillery # system "heyu turn speakers on;"; my $festival = Festival::Client->new(); $festival->say("Get up. It is time to go to $class."); } }elsif( $tilmins <= 0 ){ # if class is currently in session # "class is in session.\n"; # the most it tries to complain is within 50 minutes of the class # starting # it is assumed that after that, the user is simply screwed unless( $physstat =~ /away/i || $tilmins < -$give_up_mins || ($tilmins < -10 && $tilmins % 5 != 0) ){ my $timelate = $tilmins ? speakable_time(-$tilmins): ""; system "alertsteve", "You are $timelate late to $class. Get going\!"; } } } sleep $delay * 60; }while( 1 ); sub speakable_time{ my ($mins) = @_; my $time = ""; my $value = $mins; if( $value >= 60 ){ $value = int($mins / 60); $time = $value.' hour'.($value == 1 ? "" : "s"); } $value = $mins % 60; if( $value < 60 ){ my $min_txt = $value.' minute'.($value == 1 ? "" : "s"); if( $value != 0 ){ if( $time eq "" ){ $time = $min_txt; }else{ $time .= " and $min_txt"; } } } return $time; } sub rehash{ load_schedule( $schedule, date=> &$now ); } sub clear_pid{ unlink $pidfile; die( "Terminating $0 daemon: @_\n"); } sub usage{ die< USAGE }