I’ve used Hue Disco and other apps to sync up my Philips Hue lights with my music, but what I really wanted was something that I could start during a party, and just leave to get on with it. I wanted the lights to change reasonably often, but I wanted them to respond to the type of music being played.
This program is written in Python 3 and uses the Echonest database to get the energy and danceability, as well as the tempo of the currently plying track from the Sonos player (obtained by this great program: node-sonos-http-api. The program then selects a brightness and saturation range and randomly changes all the hue lights you specify. The lights change in a frequency relating to the tempo of the track playing.
The program must be started AFTER you’ve started playing a track on the Sonos, and then continuously runs until you pause the Sonos player, and then raises the lights to a medium white light, and then the program quits.
I’ve got this program running on my Raspberry Pi, and am really pleased with it, obviously more tinkering can be done, but I’ve not seen any similar apps that do this. I have a Domoticz ‘switch’ that can start running this program, and then to end the program, you just pause playback. But you could just run the program from the command line each time you want to have a disco!
Best used when you have a night of partying, with a large random playlist going. Don’t forget that as long as the player you specify in the variables is part of the playing ‘group’, you can play music to different rooms and include those Hue bulbs into the list too!
You can download the code here: Hue-BR-Analyse
Here’s the code:
import urllib from urllib import request from random import randint import base64,requests,json,time,datetime """ Hue Sonos Analyser by Harry Plant 2015 This program changes the hue, brightness and tempo of Philips Hue lights depending on the energy and danceability of the track, as reported by Echonest. Requirements: *Philips Hue bridge and at least one Hue Light (http://www2.meethue.com/en-gb/) *At least one Sonos player (http://www.sonos.com/) *Python 3 (I have it installed on my Raspberry Pi) *Echonest api key (obtainable at https://developer.echonest.com/ - click 'Get an API Key') *node-sonos-http-api (https://github.com/jishi/node-sonos-http-api) change the following variables to match your setup: zone is the name of the sonos player you will be listening to my_list is an array of the hue lights you want to control sonosinfo is the address of the node-sonos-http-api server hue_bridge is the address and key that you use to control the hue echonest_apikey is the key you obtained from echonest I have used this program with success (great parties) on multiple occasions but do not accept responsibility for any loss or damage caused by using this code. """ zone = 'Kitchen' my_list = ['3', '4', '5', '7', '15', '17', '19', '20'] sonosinfo = 'http://192.168.1.100:5005/' hue_bridge = 'http://192.168.1.101/api/newdeveloper/' echonest_apikey = '02MKNPMXXXXXXXXXX' """ MAIN PROGRAM BELOW, YOU DON'T NEED TO CHANGE ANYTHING FROM HERE """ currenttrack = 'No track' min_bri = 100 max_bri = 100 secs_waited = 0 secs_to_wait = 10 sat = 100 tt = 10 trackfound = 1 std_assumptions = 0 for r in range(1,999999): secs_waited = secs_waited + 1 req = request.urlopen(sonosinfo + zone + '/state') encoding = req.headers.get_content_charset() obj = json.loads(req.read().decode(encoding)) """ print(json.dumps(obj,indent=4)) """ if obj['zoneState'] == 'PAUSED_PLAYBACK' or obj['zoneState'] == 'STOPPED': print('Player has stopped playing. Returning lights to white and exiting program.') my_list_len = len(my_list) for i in range(0,my_list_len): hue = 10000 bri = 200 sat = 50 tt = 50 payload = '{"on":true,"sat":' + str(sat) + ',"bri":' + str(bri) + ',"hue":' + str(hue) + ',"transitiontime":' +str(tt) + '}' """ print(payload) """ r = requests.put(hue_bridge + "lights/" + my_list[i] + "/state",data=payload) req = request.urlopen('http://192.168.1.94:8080/json.htm?type=command¶m=switchlight&idx=252&switchcmd=Off') exit() track = obj['currentTrack']['title'] artist = obj['currentTrack']['artist'] elapsedtime = obj['elapsedTime'] playing = obj['playerState'] if track == currenttrack: """ print('Waited ' + str(secs_waited) + ' out of ' + str(secs_to_wait)) """ if track != currenttrack: std_assumptions = 0 print('This is a new track!') print('Now playing : ' + track + ' by '+ artist + ".") requeststring = 'http://developer.echonest.com/api/v4/song/search?api_key=' + echonest_apikey + '&format=json&artist=' + urllib.parse.quote(artist) + '&title=' + urllib.parse.quote(track) """ print(requeststring) """ req = request.urlopen(requeststring) encoding = req.headers.get_content_charset() obj = json.loads(req.read().decode(encoding)) """ print(obj) """ if not obj['response']['songs']: std_assumptions = 1 print('Song not in database - using standard assumptions') if std_assumptions == 0: songid = obj['response']['songs'][0]['id'] """ print(songid) """ requeststring = 'http://developer.echonest.com/api/v4/song/profile?api_key=' + echonest_apikey + '&id=' + songid + '&bucket=audio_summary' """ print(requeststring) """ req = request.urlopen(requeststring) encoding = req.headers.get_content_charset() obj = json.loads(req.read().decode(encoding)) if not obj['response']['songs'][0]['audio_summary']: print('Although song was in database, there is no energy and danceability data. Using standard assumptions for this track.') std_assumptions = 1 if std_assumptions == 0: print(obj) dancepercent = int((obj['response']['songs'][0]['audio_summary']['danceability'])*100) energypercent = int((obj['response']['songs'][0]['audio_summary']['energy'])*100) pulserate = obj['response']['songs'][0]['audio_summary']['tempo'] print('Danceability is '+str(dancepercent) + '% and energy is ' + str(energypercent) + '% with a tempo of '+str(pulserate) +'bpm.') currenttrack = track if std_assumptions == 1: dancepercent = 50 energypercent = 50 pulserate = 100 print('Danceability is '+str(dancepercent) + '% and energy is ' + str(energypercent) + '% with a tempo of '+str(pulserate) +'bpm.') currenttrack = track if pulserate < 100: secs_to_wait = 20 tt = 20 if pulserate > 99 and pulserate < 120: secs_to_wait = 6 tt = 10 if pulserate > 119 and pulserate < 160: secs_to_wait = 4 tt = 7 if pulserate > 159: secs_to_wait = 2 tt = 5 """ secs_to_wait = int((pulserate/60)*2) """ if energypercent < 20: sat = 50 if energypercent > 19 and energypercent < 40: sat = 100 if energypercent > 39 and energypercent < 60: sat = 120 if energypercent > 59 and energypercent < 80: sat = 170 if energypercent > 59 and energypercent < 80: sat = 200 if energypercent > 79: sat = 255 if dancepercent < 20: max_bri = 60 min_bri = 40 if dancepercent > 19 and dancepercent < 40: max_bri = 100 min_bri = 80 if dancepercent > 39 and dancepercent < 60: max_bri = 150 min_bri = 80 if dancepercent > 59 and dancepercent < 80: max_bri = 200 min_bri = 50 if dancepercent > 59 and dancepercent < 80: max_bri = 255 min_bri = 50 if dancepercent > 79: max_bri = 255 min_bri = 10 print('Changing hue lights now.') my_list_len = len(my_list) for i in range(0,my_list_len): hue = randint(0,65000) bri = randint(min_bri,max_bri) payload = '{"on":true,"sat":' + str(sat) + ',"bri":' + str(bri) + ',"hue":' + str(hue) + ',"transitiontime":' +str(tt) + '}' """ print(payload) """ r = requests.put(hue_bridge + "lights/" + my_list[i] + "/state",data=payload) secs_waited = 0 if secs_waited >= secs_to_wait: print('Changing hue lights now.') my_list_len = len(my_list) for i in range(0,my_list_len): hue = randint(0,65000) bri = randint(min_bri,max_bri) payload = '{"on":true,"sat":' + str(sat) + ',"bri":' + str(bri) + ',"hue":' + str(hue) + ',"transitiontime":' +str(tt) + '}' """ print(payload) """ r = requests.put(hue_bridge + "lights/" + my_list[i] + "/state",data=payload) secs_waited = 0 time.sleep(1)