I just spent about five hours kludging together a little Python script to change the color of my foyer’s light based on the temperature. I already had that happening via If This Then That, but there were some limitations – it didn’t work well with me also wanting to change the light to a dim red in the evening.
So I dove into the mysteries of the LIFX web API, and the forecast.io web API, and wrote something. Now, my foyer will change between four different colors based on the temperature (blue/purple/yellow/nearly white) between the hours of 8am and 8:30pm, and switch to a dim red outside of those hours. There’s also a temperature offset based on the next hour’s conditions; precipitation or high winds lower the effective a bit so I’m not out there being miserable when it’s near the bottom of a temperature range and wind or rain is making it feel chillier.
# peggy's little foyer-light controller
#
# selects from a set of lifx scenes based on the temperature from forecast.io
#
import requests
import sys
import math
import time
lifxToken = 'your token goes here'
# generate your token at https://cloud.lifx.com/settings
darkSkiesToken = 'your token goes here'
# generate your token at https://developer.forecast.io
#
# desired temperature ranges for scenes you've set up via the lifx app
#
choices = [
{'scene':'Foyer Cold', 'lower':0-float("inf"), 'upper':54, 'note':'Wear a heavy coat, miss dragon.'},
{'scene':'Foyer Chilly', 'lower':55, 'upper':66, 'note':'Light coat. Or sweater. Or something.'},
{'scene':'Foyer Hot', 'lower':67, 'upper':74, 'note':'As long as your sin globes are covered, anything goes.'},
{'scene':'Foyer Really Hot', 'lower':75, 'upper':float("inf"), 'note':'It is HOT. Take a parasol or something.'},
]
nighttime = {
'scene':'Foyer Evening',
'nightBegins': 20.5, # 24-hour decimal time. 8:30PM = 20.5.
'nightEnds': 8.0, # 24-hour decimal time. 8 AM = 8.0.
}
# temperature offsets for various conditions
# as defined by forecast.io's 'icon' property of the forecast
offsets = {
'clear-day':0,
'clear-night':0,
'rain':-10,
'snow':-10,
'sleet':-10,
'wind':-5,
'fog':0,
'cloudy':-5,
'partly-cloudy-day':-5,
'partly-cloudy-night':-5,
}
latitude = '47.6659248'
longitude = '-122.3181908'
# where are you?
# really this should talk to OSX's location manager
# but that starts to look like work
repeatDelay = 5*60 # delay between repetitions, in seconds
# lifx' api throttles you to about once a minute
# forecast.io throttles to 1000 requests/day (about one every 1.4 min)
#
# picks a scene based on the forecast
#
def ChooseScene (choices, offsets, nighttime, forecast):
# there should be some logic here to check the time
# and if it's earlier or later than certain times
# we cancel out and just display the nocturnal light
now = time.localtime()
now = now.tm_hour+((1.0/60)*now.tm_min)
if (now < nighttime['nightEnds']) or (now > nighttime['nightBegins']):
print "it's nighttime! the time is now ",now
return nighttime['scene']
# figures out temperature offset based on the next hour's condition
condition = forecast['hourly']['data'][0]['icon']
offset = offsets[condition]
temperature = round(forecast['hourly']['data'][0]['temperature']+offset)
print 'current temperature:',temperature
for choice in choices:
if temperature > choice['lower'] and temperature < choice['upper']:
print choice['note']
return choice['scene']
#
# contacts LIFX and acquires a list of scenes
#
def GetScenes(token):
headers = {
"Authorization": "Bearer %s" % token,
}
authorization = requests.get('https://api.lifx.com/v1/lights/all', headers=headers)
if authorization.status_code != 200:
print "\ninvalid authorization, maybe check your token?\n"
sys.exit()
scenerequest = requests.get('https://api.lifx.com/v1/scenes', headers=headers).json()
# I am sure there is a much more pythonic way to do this. Works though.
scenes = {}
for scene in scenerequest:
scenes[scene['name']] = scene['uuid'].encode('ascii')
return scenes
#
# sets a lifx scene
#
def SetScene(uuid,token):
headers = {
"Authorization": "Bearer %s" % token,
"duration": 60*5,
}
result = requests.put('https://api.lifx.com/v1/scenes/scene_id:'+uuid+'/activate', headers=headers)
return result
#
# acquires a forecast from forecast.io
#
def GetForecast(latitude, longitude, token):
forecast = requests.get('https://api.forecast.io/forecast/'+token+'/'+latitude+','+longitude).json()
return forecast
#
# all functions are defined, time to do it to it!
#
lastScene = ''
scenes = GetScenes(lifxToken)
while True:
forecast = GetForecast(latitude, longitude, darkSkiesToken)
whichScene = ChooseScene(choices, offsets, nighttime, forecast)
if lastScene == whichScene:
print "no need to change the scene right now."
else:
print 'setting scene:'+whichScene
SetScene(scenes[whichScene],lifxToken)
lastScene = whichScene
print "waiting a bit..."
time.sleep(repeatDelay)
# fail silently because i'm a bad girl
…I really should get some kind of code formatting plugin for this blog someday.
You should remove your API keys while you’re at it.
Oh, hmm, yeah, good point. I should also make it gracefully handle failing to make a connection; right now it just quits.