After I rediscovered my PiFace Digital, I wanted to play with it a bit. I thought that I could switch a night light with one of the relays. But I didn’t want to estimate roughly when it is dark, I wanted to orientate by the current sunrise and sunset.

Of course, it doesn’t matter what you switch with it. If the GPIO interface of the Raspberry Pi is sufficient, you can also switch an LED when it gets dark. This might even be enough for a night light, I have to test it. Since I have a PiFace Digital that gives me switchable 5V on the Raspberry Pi, I took this. With the GPIO interface you can also switch another relay. Other options would be you use IR or even switch the USB ports of the Raspberry Pi on and off.

Note: I switched to Astral doing this. Here is an in depth explanation how to do this and what Astral can do for you.

Sunrise off / Sunset on

Three things were important to me for this project:

  1. Getting the time of sunrise and sunset of the given day.
  2. Check if I am between sunrise and sunset because the relay should be off then.
  3. Get the data again at the change of day. It is not necessary to retrieve the data continuously.
Switch the night light on at sunset and off at sunrise
Switch the night light on at sunset and off at sunrise

Where do I get the data for sunrise / sunset?

Of course, we need the data for sunrise and sunset — otherwise the project is nonsense. I get the data via sunrise-sunset.org. For the city of London, for example, the relevant URL request looks like this:

https://api.sunrise-sunset.org/json?lat=51.517576&lng=-0.07978&formatted=0

You must specify latitude (lat) and longitude (lng). The request above will give you these data:

Sunrise, dawn and sunset for London
Sunrise, dawn and sunset for London

You get the following information:

  • Sunrise
  • Sunset
  • Solar Noon
  • Day length (in seconds)
  • Civil twilight — reading outside is still possible without problems
  • Nautical twilight
  • Astronomical twilight

It is important that all times are given in UTC. The time zone is not considered, but I don’t care because I simply calculate everything in UTC. I just don’t change the time zone of the Pi and it will work. But you could also add or subtract with x hours (maybe with timedelta(hours=x) ) or define the time zone.

My script works with sunrise and sunset. In many cases, the civil twilight would probably be more suitable. But here you can experiment yourself.

My script (Python 3) looks (currently) like this

I let the while loop run every minute, hence the sleep(60) at the end. Since I don’t query the REST API from sunrise-sunset.org on every pass, I could run it more often. But the night light does not need to switch spot on the second.

The last two lines simply check if the string from the current date is different from the one from the URL query. If so, the program simply fetches new data. So, ideally the REST API of the website is queried only once a day.

Maybe checking the status would still be useful. If I get something other than OK, then jump into a loop, wait x seconds and query again.

For the relays this is similar. If they already have the desired status, the script simply jumps one step further.

import requests
import json
from datetime import datetime, time, timedelta
from time import sleep
import pifacedigitalio as p # you only need it if you use PiFace Digital
pifacedigital = p.PiFaceDigital()
url = 'https://api.sunrise-sunset.org/json?lat=51.517576&lng=-0.07978&formatted=0' # URL, change lat and lng
r = requests.get(url) # query data

while True:
    d = datetime.now()
    today_date = d.date() # date today
    time_now = d.time() # time now
    data = json.loads(r.content)
    sunrise = data['results']['sunrise']
    sunset = data['results']['sunset']
    sunrise_time = time(int(sunrise[11:13]), int(sunrise[14:16])) # Change sunrise in time format
    sunset_time = time(int(sunset[11:13]), int(sunset[14:16])) # Change sunset into time format

    if time_now > sunrise_time and time_now < sunset_time: # In between sunrise and sunset

        if pifacedigital.output_pins[1].value == 1: # If relay on, turn it off
            pifacedigital.output_pins[1].turn_off() # turn it off
    else:
        if pifacedigital.output_pins[1].value != 1: # If relay off, turn it on
            pifacedigital.output_pins[1].turn_on() # turn on

    sleep(60)

    if str(sunrise[0:10]) != str(today_date): # different date? Get new data
        r = requests.get(url)

I saved the script as sunrise-sunset.py, made it executable

chmod +x sunrise-sunset.py

and started it on the Raspberry Pi (it runs in the background)

nohup python3 ./sunrise-sunset.py &

You can end it by first finding out the PID (this gives you a number):

pgrep -f sunrise

or you do that

ps -ef | grep "sunrise" | awk '{print $2}'

If you have the correct number you kill the program:

kill <given PID / number>

That works as well

pkill -f sunrise

In my case, this should not cause any problems because there is only one program with the keyword sunrise running.

Alternatively, you could call the program in a screen session and abort it with Ctrl + C if necessary.

Improving the script

The program is not perfect, I know that. For example, it aborts if the URL cannot be reached due to network errors, maintenance work and so on. I can catch the error with except requests.exceptions.ConnectionError. I will add a check if the received data is ok. If there is a network error, just wait x seconds and try again. The URL query would be better in a function I can use.

Furthermore, I would not have to set sunrise and sunset every time the while loop is run. Also here once a day is enough. If you create a function to query the URL, then setting the times would make sense here. I will refine my script while it is running.

Change it as you like

Of course, you can also use sunrise_time and sunset_time to define a time span yourself in which something should happen. Maybe you listen to the radio between 1 and 2 pm. Or take a buzzer and let yourself be woken up for 5 minutes 30 minutes after sunrise.

I’ve been running this for a few days now and it works. It does what it is supposed to do. I am open for suggestions for improvement, because I don’t program that much.

It would also have been possible, I just query the sunrise, switch off the relay, let the program sleep for the length of the daylight and then switch it on again. But I prefer my version, because I can vary more easily with the twilight.

Do you have a Sense HAT? Make a Bitcoin ticker – it’s easy!

Script with gpiozero

For completeness, here is the script of how it might work with gpiozero — GPIO(17) is switched.

import requests
import json
from datetime import datetime, time, timedelta
from time import sleep
from gpiozero import LED

url = 'https://api.sunrise-sunset.org/json?lat=51.517576&lng=-0.07978&formatted=0'
r = requests.get(url)

led = LED(17)

while True:

    d = datetime.now()
    today_date = d.date()
    time_now = d.time()

    data = json.loads(r.content)
    sunrise = data['results']['sunrise']
    sunset = data['results']['sunset']
    sunrise_time = time(int(sunrise[11:13]), int(sunrise[14:16]))
    sunset_time = time(int(sunset[11:13]), int(sunset[14:16]))

    if time_now > sunrise_time and time_now < sunset_time:
        if led.is_active:
            led.off()
    else:
        if not led.is_active:
            led.on()

    sleep(60)

    if str(sunrise[0:10]) != str(today_date):
        r = requests.get(url)

You only have to change the script in a few places and can use it to switch the pins on your Raspberry Pi at sunrise and sunset.

Gpiozero is preinstalled on Raspbian, but not on Raspbian Lite. If you run your Raspberry Pi headless with the Lite version, you may need to install gpiozero first:

sudo apt install python3-gpiozero

Now it works. It’s amazing what you can do with a Raspberry Pi. Do you need more ideas? Why not build a VPN router.