Quick And Dirty

Dave with light switch As Dave is an on-going project I wanted to start switching my outdoor lights as soon as possible. Because the light sensor on one of my porch lights had died a couple of months ago, I needed an automated light controller quick. This obviously became Dave's first Domotics task. At first I had started controlling the porch light at fixed times. In the heart of winter daylight increases with just 2 minutes per day, so a fixed time will do quite nice for now.
However I couldn't resist adding an automatic adaption to sunrise and sunset times. And less than a week later Dave was able to turn the porch light on at sunset and turn it off again at sunrise the next morning. He still does that in a quick and dirty way, but it works all the same. I will refine everything as soon as I can find the time.

Download scripts from this page. Before you can use the quick and dirty methods described on this page you'll have to make all the preparations mentioned on this page first.

The involved script is rather long and may be a bit awkward to copy and paste. Therefore I have collected everything in a zip file, which you can download here.

Turning One Light On And Off

Right, the last paragraph of the preparations page explains how to turn a relay on or off. Let's assume that my porch light is connected to the relay 3 output (which in fact it is).
I have written two small scripts which turn the porch light on and off. These scripts are placed in my ~/bin directory.

I have named my porch light on script lon and it contains:

#! /bin/bash

echo 1 > /sys/class/gpio/gpio22/value

And the script loff turns the light off again and it contains:

#! /bin/bash

echo 1 > /sys/class/gpio/gpio22/value

Really, that's all there's to it. Easy enough, isn't it. Oh, BTW, don't forget to make both scripts executable with the command chmod u+x ~/bin/lon ~/bin/loff.

Automating The Porch Light

Automating this is very simple. Enter the command crontab -e and add the next lines at the end of the file:

# Turn the porch light off around about sunrise
0   8   *   *   *   /home/pi/bin/loff 2>&1

# Turn the porch light on around about sunset
0   17  *   *   *   /home/pi/bin/lon 2>&1

This will do the trick. The porch light is switched off at 8:00 in the morning (0 minutes, 8 hours), and it is switched on again at 5:00 in the evening (0 minutes 17 hours).
It works, and it is quick and rather dirty. You'll have to change the sunrise and sunset times manually regularly (depending on the time of year and your geographical location). This means that you'll have to change the times in your crontab just about every week in most parts of the world. Not very convenient.
Let's automate that too.

If you do want to implement the automatic adaption to the sunrise and sunset times you'll have to remove the lines above from the crontab again because they will be replaced by the script in the next paragraph.

Automatic Light Control At Sunrise And Sunset

Before we begin we'll need to install a library which we are going to use. This library is a Perl file, which will calculate the sunrise and sunset times for us. To install it execute the command sudo apt-get install libdatetime-astro-sunrise-perl . Although I have written a bash shell script here, the actual calculation of the sunrise and sunset times are done by a Perl script. I have embedded this Perl script inside the bash script, to reduce the number of script files required for the job.
I can't write Perl scripts myself. I have found this one here and made some minor adaptions.

Copy/paste the next script in your text editor on the Raspberry Pi and name the file cronsunriset.sh and place it in your ~/bin directory. In this case the name of the script is important and has to be cronsunriset.sh, because it is referred to literally inside the script itself. Don't forget to make it executable again with the command chmod u+x ~/bin/cronsunriset.sh .

#! /bin/bash

# Author : San Bergmans
# Date   : 11-1-2015
# Origin : www.sbprojects.net

# This script will add two lines to the crontab, or alter them if
# they already exist. One line is set at sunrise ( ± offset), the
# other is set at sunset ( ± offset).

# Installation

# Copy this script in your ~/bin directory
# Change, if necessary, these variables:
# BINPATH       -->    Path to the binary path which contains this script
#                       and the sunrise and sunset scripts.
# OFFSET_RISE   -->    Offset from sunrise (max ± 60 minutes)
# OFFSET_SET    -->    Offset from sunset (max ± 60 minutes)
# CRON_CMD_RISE -->    Command to be executed at sunrise
# CRON_CMD_SET  -->    Command to be executed at sunset
# latitude      -->    In Perl script. Your location. Positive is East
# longitude     -->    In Perl script. Your longitude. Positive is North

# Then simply run this script manually once. From then on it will run
# daily just after midnight to set today's sunrise and sunset times.

# Some path information
BINPATH="$HOME/bin"
TEMP_FILE="/tmp/cronsunriset"

# Configuration
OFFSET_RISE=0           # Offset in minutes to sunrise (max ± 60 min)
OFFSET_SET=0            # Offset in minutes to sunset (max ± 60 min)

# Scripts to run at sunrise and sunset
CRON_CMD_RISE="$BINPATH/loff 2>&1"
CRON_CMD_SET="$BINPATH/lon 2>&1"

# Crontab extras
CRON_HEADER="# Sunrise and sunset scripts"
CRON_FLG_RISE="# cron at sunrise"
CRON_FLG_SET="# cron at sunset"

# Main cron task which sets the switching times for the next day
CRON_TSK_FLG="# Set new sunrise and sunset times"
CRON_TSK_CMD="01\t0\t*\t*\t*\t$BINPATH/cronsunriset.sh 2>&1"

# Embedded Perl script to calculate sunrise and sunset times
# Script requires: libdatetime-astro-sunrise-perl to be installed
# Result is in this format: 2015-01-08T08:41:00 to 2015-01-08T16:51:00
BOTH=$(/usr/bin/perl -x $0)
echo <<'__END__' > /dev/null
#! /usr/bin/perl -wl
# Change latitude and longitude to your location.

use DateTime;
use DateTime::Astro::Sunrise;
$latitude = "+51.592996"; $longitude = "+5.171443";
$sr = DateTime::Astro::Sunrise->new($longitude, $latitude, 0, 3);
$date = DateTime->now; $date->set_time_zone("local");
($rise, $set) = $sr->sunrise($date);
$rise->set_time_zone("local"); $set->set_time_zone("local");
print $rise, " to ", $set, "\n";
__END__
# End of embedded Perl script

# Isolate sunrise and sunset times
SUNRISE=$(echo "$BOTH" | awk '{ print $1 }' | awk -FT '{ print $2 }')
SUNSET=$(echo "$BOTH" | awk '{ print $3 }' | awk -FT '{ print $2 }')

# Split into hours and minutes
SUNRISE_H=$(echo "$SUNRISE" | awk -F: '{print $1}')
SUNRISE_H=$((10#$SUNRISE_H))    # Strip leading 0 (otherwise number is mistaken for octal base)
SUNRISE_M=$(echo "$SUNRISE" | awk -F: '{print $2}')
SUNRISE_M=$((10#$SUNRISE_M))    # Strip leading 0 (otherwise number is mistaken for octal base)
SUNSET_H=$(echo "$SUNSET" | awk -F: '{print $1}')
# No need to strip leading 0, there never is one in the sunset hour
SUNSET_M=$(echo "$SUNSET" | awk -F: '{print $2}')
SUNSET_M=$((10#$SUNSET_M))      # Strip leading 0 (otherwise number is mistaken for octal base)

# Add offset
SUNRISE_M=$((SUNRISE_M+OFFSET_RISE))
if [ $SUNRISE_M -gt 59 ]
then
        SUNRISE_M=$((SUNRISE_M-60))
        SUNRISE_H=$((SUNRISE_H+1))
fi
if [ $SUNRISE_M -lt 0 ]
then
        SUNRISE_M=$((SUNRISE_M+60))
        SUNRISE_H=$((SUNRISE_H-1))
fi

SUNSET_M=$((SUNSET_M+OFFSET_SET))
if [ $SUNSET_M -gt 59 ]
then
        SUNSET_M=$((SUNSET_M-60))
        SUNSET_H=$((SUNSET_H+1))
fi
if [ $SUNSET_M -lt 0 ]
then
        SUNSET_M=$((SUNSET_M+60))
        SUNSET_H=$((SUNSET_H-1))
fi

# Read current crontab
crontab -l > "$TEMP_FILE"

# See if the main cron task has already been installed
grep -q "$CRON_TSK_FLG" "$TEMP_FILE"
if [ $? -ne 0 ]
then
        # Not installed yet. Install it now
        echo -e "$CRON_TSK_FLG" >> "$TEMP_FILE"
        echo -e "$CRON_TSK_CMD\n" >> "$TEMP_FILE"
fi

# See if the sunrise and sunset scripts have already been installed
grep -q "$CRON_HEADER" "$TEMP_FILE"
if [ $? -eq 1 ]
then
        # Entries don't exist yet, add them
        echo -e "$CRON_HEADER" >> "$TEMP_FILE"
        echo -e "$SUNRISE_M\t$SUNRISE_H\t*\t*\t*\t$CRON_CMD_RISE\t\t$CRON_FLG_RISE" >> "$TEMP_FILE"
        echo -e "$SUNSET_M\t$SUNSET_H\t*\t*\t*\t$CRON_CMD_SET\t\t$CRON_FLG_SET" >> "$TEMP_FILE"
else
        # Entries do exist, change them
        sed -i "/$CRON_FLG_RISE/c$SUNRISE_M\t$SUNRISE_H\t*\t*\t*\t$CRON_CMD_RISE\t\t$CRON_FLG_RISE" "$TEMP_FILE"
        sed -i "/$CRON_FLG_SET/c$SUNSET_M\t$SUNSET_H\t*\t*\t*\t$CRON_CMD_SET\t\t$CRON_FLG_SET" "$TEMP_FILE"
fi

# Setup new crontab
crontab "$TEMP_FILE"

# Remove temp file
rm "$TEMP_FILE"

I admit, it's a long script. But that is mainly because of the comments I have added to make it readable and hopefully understandable.

You may have to make some changes to adapt the script to your situation and location. First of all you'll have to change your location, unless you live near me. Go to Google Maps and find your location. Right click on the map at your location and select "What is here?" from the drop down menu. Write down the latitude and longitude data which are now shown in a pop-up box. Find the words latitude and longitude in the script and replace my location by yours. The easiest way to do that is looking for the coordinates +51.592996 and +5.171443 in the script.

You may want to add or subtract an offset to the sunrise and the sunset times. You do that by changing the values behind the OFFSET_RISE and OFFSET_SET variables. For instance if you want to light to stay on 15 minutes after sunrise you change the OFFSET_RISE value to 15. If you want to change the offset for the sunset to 20 minutes before sunset, change the OFFSET_SET value to -20. The maximum offset the program will handle correctly is ±60 minutes. Any other values will confuse the script.

Finally you may want to replace the scripts that have to be called at sunrise and at sunset to your particular payload scripts. In this example I use the simple lights off and lights on scripts from earlier on this page.

The rest of the script should explain itself. I have included ample comments to make it easier to understand.

Getting It Started

Getting this script started is quite easy. Simply run it, and your done. It will automatically add a rule to your crontab which will start this script every day at 1 minute past midnight.
It will also add 2 more rules to your crontab, one for turning the light on, and one for turning it off again.

After that the script will run once a day, at one minute past midnight. It will then change the times of today's sunrise and sunset events. At sunrise the lights off event is triggered, while at sunset the lights on event is triggered. Easy does it.

Pitfalls

First of all the lights are only switched at sunrise and sunset. This means that the light will stay off if you first start the script after sunset, until the next day. You'll have to turn the light on manually if you want the light to be on instantly.

The second pitfall is related to the first. If you reboot your Dave, it will leave the lights off, even if it is past sunset, or before sunrise.. The light will be switched on for the first time at the next sunset. Therefore avoid unnecessary reboots, otherwise you might have to switch the lights manually today.

The third pitfall depends on your geographical location. This script doesn't take locations within the polar circle regions into account, where it may happen that the sun doesn't rise or set at all. Please forgive me for this if you do live in the arctics.

And finally don't manually change the crontab rules which are controlled by this script. You may create some conflicts when the script tries and fails to find its flags.

Stop The Script

You may stop this script at any time. Simply remove the next lines from your crontab and it won't bother you again.

# Set new sunrise and sunset times
01  0   *   *   *   /home/pi/bin/cronsunriset.sh 2>&1

# Sunrise and sunset scripts
39  8   *   *   *   /home/pi/bin/loff 2>&1     # cron at sunrise
55  16  *   *   *   /home/pi/bin/lon 2>&1      # cron at sunset