light nerdery

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.

Leave a Reply to STrRedWolf (@strredwolf)Cancel reply