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:
- Getting the time of sunrise and sunset of the given day.
- Check if I am between sunrise and sunset because the relay should be off then.
- Get the data again at the change of day. It is not necessary to retrieve the data continuously.
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:
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.
Steve Higham
says:Hi,
Thanks for this article, it is what I have been looking for, though I could do with advice on how to make a couple of modifications.
I have a Raspberry Pi (3) controlling my lounge & hall lights while I am away from home.
Your program would allow the switching ON of the lights at sunset time, but I do not want the lights to be on all night.
Can you advise on how to modify the program to allow ‘hard coding’ of an OFF time for the lights eg. 23:00hrs, rather than at sunrise.
If possible I would also like to switch the lights ON & OFF randomly during the ON period – to simulate people being in the house.
I am not skilled at writing Python code, so would appreciate any help you can offer. Many thanks
guyfawkes
says:Hi,
in your use case wouldn’t it be easier just to use an off the shelf time switch? Normally they have a random on/off integrated.
But to answer your question: in this use case I would switch the if-else-statement and check for sunset_time and a new parameter called e.g. elevenoff that is set to 23:00hrs. If the time is in between — switch on.
To add the random part you first need to import random:
import random
In the ON part you generate for example a random number in between 1 and 10
random_number = random.randint(1, 10)
Now you switch as you like — for example if the number is smaller than 4 switch on — else switch off.
Steve Higham
says:Hi,
Thanks for your reply, I’ll give your suggestions a try & let you know how it goes.
One thing I am not clear on is how to get the time ( ie 23:00) into the correct format to compare with sunset_time and time_now
Thanks
guyfawkes
says:Cool …
You can define it like sunset_time but with set numbers. In my example with elevenoff it could be:
elevenoff = time(int(23), int(00))
Paresh Chauhan
says:Hello,
Nic information about the above function you gave. i have a task and need some suggestion from from your side related to the task.
For my task in need that my circuit should be ‘ON’ after certain time from the sunrise (lets say at 12:00 pm in the afternoon and should turn ‘OFF’ at the sunset depending upon the latitude and longitude. How should i proceed with coding? Any suggestion would be helful. since i am new to programming and i need some guidance.
guyfawkes
says:You can add hours to the times given – for example:
d.time() + timedelta(hours=9)
would add additional 9 hours