This post shows you how to control the Hue light bulbs using a Raspberry Pi. In particular, it shows you how to fade up your lights half an hour before to sunset.
In true Heath Robinson style the solution is rather convoluted. But hey, that’s where the fun resides.
Email me, Sun
First, we set up IFTTT to email us sunrise and sunset times.
This involves setting up two recipes triggered based on the weather channel.
Read my emails, Pi
Now we need to set up a Python script to read our Gmail. To set this up I first, via the Gmail web interface, set up the account to auto-label emails from IFTTT containing the word “Sun” as “Sun”. This makes the emails easier to read as we then only need to read the email ‘folder’ “Sun”.
The email reading script is similar to that used in the previous post.
#!/usr/bin/env python import imaplib from email.parser import HeaderParser import sqlite3 as lite import datetime def extract_date(word): date_index_start = word.find('for ')+4 date_index_end = word.find(' at')+11 date_out = word[date_index_start:date_index_end] return date_out def extract_sunevent(word): word_out = word[0:7] return word_out.strip() def read_subjects(label): obj = imaplib.IMAP4_SSL('imap.gmail.com', '993') obj.login('username@gmail.com', 'password') obj.select(label) typ ,data = obj.search(None,'UnSeen') subjects =[] for num in data[0].split(): data = obj.fetch(num, '(BODY[HEADER])') header_data = data[1][0][1] parser = HeaderParser() msg = parser.parsestr(header_data) subjects.append(msg['Subject']) return subjects def storesuninsql(rows): #Save in database con = lite.connect('/home/[pi username]/sun.db') with con: cur = con.cursor() #Create a sun event table if it doesn't already exist cur.execute('CREATE TABLE IF NOT EXISTS sun (r_datetime TIMESTAMP, sunevent TEXT)') for row_values in rows: #print row_values #row_values = rows[i] cur.execute('INSERT INTO sun VALUES(?,?)', (row_values[0], row_values[1])) def store_sun(subjects): #Initialise temporary array for data rows = [] unread_count = len(subjects) #Process and store unread mail items for j in range(0,unread_count): #print subjects[j] #Extract date/time of sun event extracted_date = extract_date(subjects[j]) #Extract time of sunevent event_time = datetime.datetime.strptime(extracted_date, '%B %d, %Y at %I:%M%p') #Extract event name event_name = extract_sunevent(subjects[j]) #Add (event time, event name) tuple to rows rows.append((event_time, event_name)) storesuninsql(rows) if __name__ == '__main__': subjects = read_subjects('Sun') store_sun(subjects)
The code ‘reads’ the emails using the imaplib Python library. It extracts the subject lines, which indicate the sun event (“Sunset” or “Sunrise”), the date and the time. It then stores a datetime stamp in an SQLite database with a text field indicating the event type.
This Python script is then scheduled to run (via cron) overnight.
Fade Up
Now we write a little Python script to fade up all our Hue light bulbs. This has become a lot easier since Philips launched the official Hue API and documentation in March 2013 (fair play to Philips – I think this is done really well).
First go to the Getting Started page of the Hue Developers site. Follow the simple steps there to add and authenticate a new user.
Next we write the code. In short summary the Hue API works using an HTTP PUT request. This passes a JSON object (basically a string) containing ‘variablename:value’ pairs that sets the state of a bulb. More detail is found on the Hue Developers site. To fade up I use the ‘transitiontime’ variable, which I set to 6000 (6000*100ms=10minutes). To be snazzy I use xy values for the D65 standard illuminant – [0.32,0.33]. Another nice colour, with an orangey sunset feel has xy values of around [0.43,0.53].
The code is:
import requests import time ip = "[Your Bridge IP Address]" sunrise='{"on":true,"bri":0,"xy":[0.32,0.33]}' def set_light(light, data): global ip, key requests.put("http://%s/api/[username]/lights/%s/state" % (ip, light), data=data) def fade_up_all(): for i in range(1,4): set_light(i, sunrise) time.sleep(1) data = '{"on":true, "bri":255, "transitiontime":6000}' set_light(i, data) fade_up_all()
PS: I did try to use a for-loop to iterate through brightness values from 1 to 255 but this caused some buggy behaviour – the lights would flash and jump in brightness. The ‘transitiontime’ variable is a great improvement.
Cron Legacy
Finally we have my favourite bit – editing a cron tab via Python to schedule our fade up script half an hour before sunset.
Luckily some lovely person has provided a module called, unsurprisingly, python-crontab. To install it I first had to install a utility called dateutil:
sudo pip install python-dateutil sudo pip install python-crontab
The documentation can be found at the above link. The comments in the code below will hopefully explain how to use it – it’s pretty simple. The only trick is to work out the syntax: [crontab object].[crontab field].on([value]) sets that field value, for example: cron.minute.on(5) sets the minute field to 5. Also you need to clear each field (e.g. cron.minute.clear()) before setting using .on() otherwise it add the value to the existing value (e.g. an existing value of ‘0’ and the previous would give ‘0,5’). In the code below the first function extracts the time of the last sunset from the database and returns hour and minute values for a time half an hour before yesterday’s sunset (the trick to that is to use timedelta). Also remember with cron to use absolute paths in your code – cron jobs are typically run as if you were in your home directory.
from crontab import CronTab import datetime import sqlite3 as lite def gettime(): con = lite.connect('/home/[pi username]/sun.db') #Get last sunset time cur = con.execute("SELECT r_datetime FROM sun WHERE sunevent='Sunset' ORDER BY r_datetime DESC LIMIT 1") #We can use fetchone() as only one record will be returned record = cur.fetchone() sunsetdt = datetime.datetime.strptime(record[0], "%Y-%m-%d %H:%M:%S") timearray = [sunsetdt.hour, sunsetdt.minute] return timearray def setcron(timearray): #timearray input is an array in the form [hour, minute] hour = timearray[0] minute = timearray[1] user_cron = CronTab('[Your raspberry pi username]') #Open crontab for user list = user_cron.find_comment('sunon') #look for existing cron job for fade up lights using comment if not list: #if no job added job = user_cron.new(command='python /home/[pi username]/MyCode/Hue/fadeupall.py',comment='sunon') #Add new job else: #if existing jon job = list[0] #select first job returned from search job.minute.clear() #Clear previous minute job.hour.clear() #Clear previous hour job.minute.on(minute) #set minute value job.hour.on(hour) #set hour value job.enable() #enable job user_cron.write() #write crontab timearray = gettime() setcron(timearray)
The above code is scheduled to run once a day sometime after midnight, after the script to read my emails above. At some point I’ll get all this on to github. Update: I have finally added the code (updated in places) to github: https://github.com/benhoyle/Hue.
Hey presto. My lights turn on half an hour before sunset everyday!
Would it be useful if the ‘on’ method cleared the previous selection in that slice? I’m open to changing that part of the API since I’ve never specified what the API would do if one called ‘on’ more than once. (should test for that)
Nice article.
I’m comparing Philips with LIFX as I want to be able to program the Pi to scan for mac addresses, and when I come home and my iPhone auto connects to the wifi, to recognise the iPhone is now on the wifi and turn on the lights (if > sunset and < bedtime)
This is an AWESOME idea, I want to add this functionality to the great tutorial above, any help?
I’m looking to do a similar project. Did you ever get this working?
AWESOME idea, this and the EXCELLENT tutorial above is the best ingredients together. I have Philips Hue, anybody can help me with that?
Thanks.