I was asked how best to control home lighting for examples using the remote 433mHz sockets that sold in multipacks. I've explained how these can be controlled from an £1.50 8266 ESP (under $3 US) unit and a 433mHz transmitter board and a receiver board (combined both board cost less than one dollar US)
The problem was determining sun set and sunrise accurately.
I noticed that the light on say for hall lights would depend on the house hold routine and DoW so this might be 6am weekdays and later at weekend. That's simple enough using cron, but what about when to turn off the light AFTER the sun is up?
The same is true in the evening a cron job will most likely turn off the lights at say 11:30pm but when should the lights come on as sunset time vary so much?
There are astronomical utilities that can compute Sunset and Sunrise given the date and the location. But that's only part of the puzzle as we need to start a program that grabs the current date calculates the time to turn off the lights, turns them off, calculates when to turn them on in the evening, and fiinally turns them on in the evening.
I looked around and there were no ready to go programs specifically to do this. I wanted something that could also be used from Kodi so later I could use the same code to control presets for AV set ups.
First up I tested that my lighting could be operated without issue using cron to trun them on and off. That was fine so I commented out the lines for turning off the lights in the moring and turning them on after dark:
0 6 * * 0-6 /root/hallon.sh >/dev/null 2>&1 # Hall lights on 6am
#45 6 * 3-9 0-6 /root/halloff.sh >/dev/null 2>&1 Hall lights off 6:45 am summer
#0 8 * 1,2,10-12 0-6 /root/halloff.sh >/dev/null 2>&1 #Hall lights off at 8am Winter
0 23 * * 0-6 /root/halloff.sh >/dev/null 2>&1 # Hall lights off 11pm daily
#15 19 * 3-9 0-6 /root/hallon.sh >/dev/null 2>&1 Hall lights on 17:15 summer
#0 16 * 1,2,10-12 0-6 /root/hallon.sh >/dev/null 2>&1 #Hall lights on at 4pm Winter
In the example above you can see I've comment out all the events where the new program will operate the lights. I then added one line to start the program each morning while its still dark
0 5 * * * /usr/bin/python /root/sun.py >> /root/lightingcontrol.log
import urllib2
import datetime
from astral import Astral
import time
import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal
def wait_until(execute_it_now):
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print('Target: %s' % str(execute_it_now))
print('Current: %s' % str(local_now))
while True:
diff = (execute_it_now - local_now).total_seconds()
print('Difference:%s' % diff)
if diff <= 0: #Time to run ?
return
else: #Wait 60s update local_now with updated time
time.sleep(60)
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
#Main program
#Ok lets work out the times for sunrise and sunset today. but first convert UTC to local time
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('Local: %s' % local_now)
print ('UTC: %s' % utc_now)
run_start_time=local_now
city_name = 'London'
a = Astral()
a.solar_depression = 'civil'
city = a[city_name]
print('City: %s/%s\n' % (city_name, city.region)) #Information for London/England
timezone = city.timezone
print('Timezone:%s' % timezone) #Timezone: Europe/London
print('Timezone:%s' % timezone) #Timezone: Europe/London
print('Latitude:%.02f; Longitude: %.02f\n' % (city.latitude, city.longitude)) #Latitude: 51.60; Longitude: 0.08
sun = city.sun(date=local_now, local=True)
print('Dawn: %s' % str(sun['dawn']))
print('Sunrise: %s' % str(sun['sunrise']))
print('Noon: %s' % str(sun['noon']))
print('Sunset: %s' % str(sun['sunset']))
print('Dusk: %s' % str(sun['dusk']))
light_off_time=sun['sunrise'] +datetime.timedelta(minutes=5)
light_on_time=sun['sunset'] -datetime.timedelta(minutes=10)
print('Off Tine:%s' % str(light_off_time))
print('On Time: %s' % str(light_on_time))
print('Sleeping:%s' % str(light_off_time))
wait_until(light_off_time)
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('Awoke: %s' % str(local_now))
print ('Target: %s' % str(light_off_time))
print ('Lights: OFF')
urllib2.urlopen("http://192.168.0.202/HALLOFF").read()
print('Sleeping:%s' % str(light_on_time))
wait_until(light_on_time)
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
The problem was determining sun set and sunrise accurately.
I noticed that the light on say for hall lights would depend on the house hold routine and DoW so this might be 6am weekdays and later at weekend. That's simple enough using cron, but what about when to turn off the light AFTER the sun is up?
The same is true in the evening a cron job will most likely turn off the lights at say 11:30pm but when should the lights come on as sunset time vary so much?
There are astronomical utilities that can compute Sunset and Sunrise given the date and the location. But that's only part of the puzzle as we need to start a program that grabs the current date calculates the time to turn off the lights, turns them off, calculates when to turn them on in the evening, and fiinally turns them on in the evening.
I looked around and there were no ready to go programs specifically to do this. I wanted something that could also be used from Kodi so later I could use the same code to control presets for AV set ups.
First up I tested that my lighting could be operated without issue using cron to trun them on and off. That was fine so I commented out the lines for turning off the lights in the moring and turning them on after dark:
0 6 * * 0-6 /root/hallon.sh >/dev/null 2>&1 # Hall lights on 6am
#45 6 * 3-9 0-6 /root/halloff.sh >/dev/null 2>&1 Hall lights off 6:45 am summer
#0 8 * 1,2,10-12 0-6 /root/halloff.sh >/dev/null 2>&1 #Hall lights off at 8am Winter
0 23 * * 0-6 /root/halloff.sh >/dev/null 2>&1 # Hall lights off 11pm daily
#15 19 * 3-9 0-6 /root/hallon.sh >/dev/null 2>&1 Hall lights on 17:15 summer
#0 16 * 1,2,10-12 0-6 /root/hallon.sh >/dev/null 2>&1 #Hall lights on at 4pm Winter
In the example above you can see I've comment out all the events where the new program will operate the lights. I then added one line to start the program each morning while its still dark
0 5 * * * /usr/bin/python /root/sun.py >> /root/lightingcontrol.log
As I said I wanted something that I coudd late use in Kodi so a short Python script made sense:
This is the script called sun.py I've got mine in the /root/ directory.
The program has 3 sections.
The first part imports our libraries and defines a single function to wait until the time it is give has passed. This is an actual time of day and date, not just "wait until 7am" but "wait until 19:04 June 20 2017"
Ok so we have done the setup but not done anything yet. The second part of the code is the main program it grabs the time (UTC) and converts it to local time. You need to change the location and timezone to match yours. Google maps is good for this data. I'm in London and the data for big cities is build into the libraries. So we now have the local time and sunrise, sunset!
The last and final part of the program uses the function we setup initially and the data computed in the second part to turn off the lights after sunrise, and turn them on again when its dark.I've included offset so you can tweak the on / off time by a fixed amount each day in my case the lights go off five minutes after sunrise and turn on ten minutes before sunset.
As you can see most of the code is to print information to the screen its useful for debugging.
import urllib2
import datetime
from astral import Astral
import time
import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal
def wait_until(execute_it_now):
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print('Target: %s' % str(execute_it_now))
print('Current: %s' % str(local_now))
while True:
diff = (execute_it_now - local_now).total_seconds()
print('Difference:%s' % diff)
if diff <= 0: #Time to run ?
return
else: #Wait 60s update local_now with updated time
time.sleep(60)
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
#Main program
#Ok lets work out the times for sunrise and sunset today. but first convert UTC to local time
local_tz = get_localzone()
ts = time.time()
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('Local: %s' % local_now)
print ('UTC: %s' % utc_now)
run_start_time=local_now
city_name = 'London'
a = Astral()
a.solar_depression = 'civil'
city = a[city_name]
print('City: %s/%s\n' % (city_name, city.region)) #Information for London/England
timezone = city.timezone
print('Timezone:%s' % timezone) #Timezone: Europe/London
print('Timezone:%s' % timezone) #Timezone: Europe/London
print('Latitude:%.02f; Longitude: %.02f\n' % (city.latitude, city.longitude)) #Latitude: 51.60; Longitude: 0.08
sun = city.sun(date=local_now, local=True)
print('Dawn: %s' % str(sun['dawn']))
print('Sunrise: %s' % str(sun['sunrise']))
print('Noon: %s' % str(sun['noon']))
print('Sunset: %s' % str(sun['sunset']))
print('Dusk: %s' % str(sun['dusk']))
light_off_time=sun['sunrise'] +datetime.timedelta(minutes=5)
light_on_time=sun['sunset'] -datetime.timedelta(minutes=10)
print('Off Tine:%s' % str(light_off_time))
print('On Time: %s' % str(light_on_time))
print('Sleeping:%s' % str(light_off_time))
wait_until(light_off_time)
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('Awoke: %s' % str(local_now))
print ('Target: %s' % str(light_off_time))
print ('Lights: OFF')
urllib2.urlopen("http://192.168.0.202/HALLOFF").read()
print('Sleeping:%s' % str(light_on_time))
wait_until(light_on_time)
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('Awoke: %s' % str(local_now))
print ('Target: %s' % str(light_off_time))
print ('Lights: ON')
urllib2.urlopen("http://192.168.0.202/HALLON").read()
print ('Started: %s' % str(run_start_time))
utc_now, now = datetime.datetime.utcfromtimestamp(ts), datetime.datetime.fromtimestamp(ts)
local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now
print ('End: %s' % str(local_now))