Sometime Friday evening, I found myself with a malfunctioning alarm
clock cellphone that kept turning off at the wrong times. I had to get up
early the next morning to drive a friend to the airport, so I decided my laptop
would just have to shoulder the burden.
I’ve tried a couple different OS X alarm clock apps in the past, but even the simplest of them seemed like overkill for the task at hand:
At a predetermined time, do something obnoxious enough to wake me up.
A simple Bash script seemed like the obvious answer, but how to play a sound
from bash? Opening a file in iTunes is easy, but the whole idea was to cut down
on bloat. A little Googling pointed me towards a utility called afplay, and I
was on my way.
With a total investment of about 10 minutes, I had this uncommented, somewhat unreadable, but working script:
#!/bin/bash
t="04:30"
t_h=`echo $t | awk -F: '{print $1}'`
t_m=`echo $t | awk -F: '{print $2}'`
t_s=`dc -e "$t_h 60 60 ** $t_m 60 *+p"`
c=`date | awk '{print $4}'`
c_h=`echo $c | awk -F: '{print $1}'`
c_m=`echo $c | awk -F: '{print $2}'`
c_s=`echo $c | awk -F: '{print $3}'`
c_t=`dc -e "$c_h 60 60 ** $c_m 60 * $c_s ++p"`
til=`dc -e "$t_s $c_t -p"`
sleep $til
while :
do
afplay /System/Library/Sounds/Hero.aiff
done
It woke me up, all right, but the rapid playing of that system sound and having
to fumble for Ctrl-c to stop the thing were pretty annoying. Later, having
gone to the airport and then back to sleep for a few hours, I polished it up a
bit:
#!/bin/bash
# A simple alarm clock script
echo "What time should the alarm go off? (HH:MM)"
read target
# sleep interval is 15 minutes
snooze=`dc -e "15 p"`
# convert wakeup time to seconds
target_h=`echo $target | awk -F: '{print $1}'`
target_m=`echo $target | awk -F: '{print $2}'`
target_s_t=`dc -e "$target_h 60 60 ** $target_m 60 *+p"`
# get current time and convert to seconds
clock=`date | awk '{print $4}'`
clock_h=`echo $clock | awk -F: '{print $1}'`
clock_m=`echo $clock | awk -F: '{print $2}'`
clock_s=`echo $clock | awk -F: '{print $3}'`
clock_s_t=`dc -e "$clock_h 60 60 ** $clock_m 60 * $clock_s ++p"`
# calculate difference in times, add number of sec. in day and mod by same
sec_until=`dc -e "24 60 60 **d $target_s_t $clock_s_t -+r%p"`
echo "The alarm will go off at $target."
sleep $sec_until
# snooze loop
while :
do
echo -e "\nWake up!"
./wakeup_playlist.sh &
bpid=$!
disown $bpid # eliminates termination message
read -n1 input
for bsub in $(ps -o pid,ppid -ax | \
awk "{ if (\$2 == $bpid) { print \$1 }}")
do
kill $bsub # kill children
done
kill $bpid
if [ "$input" == "Q" ]
then
echo -e "\nGood morning!"
exit
else
echo -e "\nSnoozing for $snooze seconds..."
sleep $snooze
fi
done
You’ll notice I used dc wherever basic arithmetic was required. It’s an
RPN or postfix
calculator, so operands are pushed onto the stack, and operators pop them off
and then push the result. This was an arbitrary choice; using dc over bc or
even awk for basic math has everything to do with my preference for postfix
math. (Lots of nostalgia for using my dad’s
HP-15C calculator to do math
homework as a kid.)
The “buzzer” code has been exported to another script, for the sake of the
snooze loop and because it’s use of afplay is specific to Mac OS X 10.5 and
later. By running it as a background process, the main script can wait for user
input. Any input character other than Q kills the buzzer process and sleeps
for the set interval, while on Q the script exits cleanly. The kill command
causes a termination message on stderr from the killed process, so the buzzer
process gets disowned to suppress that message.
You can also find the above code on GitHub, including a couple buzzer scripts that will only run on Mac OS X.