Tag Archives: Pretty

Creating a custom HTML front end for Domotiz Part 3 – YouTube companion article

We’re going to get a bit more clever now. When we have taught Domoticz a list of devices (be they switches or lights or door contacts) we may want to list them all in one go without manually entering a long list of html for each device. Also, if we can get the HTML front end to gather all the information about each switch, it means that we can update the contents of the buttons automatically, for example if we change the name of a device, it will automatically update in the HTML front end.

I’ve created some code to do this and the commentary on this is over on my YouTube video.

Here’s the code and two css files that you’ll need. Please note that the css files contain more than is needed at the moment: however the css will be needed in future articles.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<head>
<title>Home Control</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />

<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<link rel="shortcut icon" href="favicon.ico" />
	<link rel="apple-touch-icon" href="iphone-icon.png"/>
	<link rel="icon" sizes="196x196" href="logo.png">
	<link rel="icon" sizes="192x192" href="logo192.png">
	<link rel="stylesheet" media="(orientation: portrait)" href="portrait.css">
	<link rel="stylesheet" media="(orientation: landscape)" href="landscape.css">
	<link href="https://fonts.googleapis.com/css?family=Baloo|Comfortaa" rel="stylesheet">

<script language="JavaScript" type="text/JavaScript">
<!--
<!--

var devicestodisplay =[36,35,31,141,32,134,132,29,136,140,33,1617];
var nod=0;
var domoticzurl="192.168.1.163";
var domoticzport="8080";

function speak(textToSpeak) {
 // Create a new instance of SpeechSynthesisUtterance
 var newUtterance = new SpeechSynthesisUtterance();

 // Set the text
 newUtterance.text = textToSpeak;

 // Add this text to the utterance queue
 window.speechSynthesis.speak(newUtterance);
}

function switchon(devicecode){
 execute('PUT', 'http://'+domoticzurl+':'+domoticzport+'/json.htm?type=command&param=switchlight&idx='+devicecode+'&switchcmd=On', '');
}

function switchoff(devicecode){
 execute('PUT', 'http://'+domoticzurl+':'+domoticzport+'/json.htm?type=command&param=switchlight&idx='+devicecode+'&switchcmd=Off', '');
}

function toggle(devicecode){
 execute('PUT', 'http://'+domoticzurl+':'+domoticzport+'/json.htm?type=command&param=switchlight&idx='+devicecode+'&switchcmd=Toggle', '');
}

function dim(devicecode,dimlevel){
 execute('PUT', 'http://'+domoticzurl+':'+domoticzport+'/json.htm?type=command&param=switchlight&idx='+devicecode+'&switchcmd=Set%20Level&level='+dimlevel, '');
}


function execute($method,$url,$message){
xmlhttp=new XMLHttpRequest();
xmlhttp.open($method,$url,true)
xmlhttp.send($message);
}
window.onload = function() {
 // all of your code goes in here
 // it runs after the DOM is built

 //updateweather();
 getalldevices();
 updatealldevices();
 }
 
window.setInterval(function(){
 updatealldevices();
 }, 1000);



function updatedevice(idx){
 var location="DEVICE"+idx;
 console.log("checking status of idx "+idx);
 var xmlhttp = new XMLHttpRequest();
 var url = 'http://'+domoticzurl+':'+domoticzport+'/json.htm?type=devices&rid='+idx;
 var onoff;
 xmlhttp.onreadystatechange = function() {
 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
 var myArr = JSON.parse(xmlhttp.responseText);
 onoff = myArr.result[0].Status;
 devicename = myArr.result[0].Name;
 // myFunction(myArr);
 document.getElementById(location).innerHTML='<span class="device">'+devicename+'</span>';
 }
 if (onoff == "On") {
 document.getElementById(location).style.background = "red";
 } 
 if (onoff == "Off") {
 document.getElementById(location).style.background = "grey";
 }
 if (onoff == "Open") {
 document.getElementById(location).style.background = "red";
 } 
 if (onoff == "Closed") {
 document.getElementById(location).style.background = "grey";
 }
 }
xmlhttp.open("GET", url, true);
xmlhttp.send();
}

function getalldevices(){
fLen = devicestodisplay.length;
for (i = 0; i < fLen; i++) {
 preparediv(devicestodisplay[i]);
 getdevice(devicestodisplay[i]);
 }
}

function preparediv(deviceidx){
nod=nod+1;
var div = document.createElement("div");
div.className = 'devicecontainer';
div.id = "DEVICE"+deviceidx;
document.getElementById("devicesdiv").appendChild(div);
}

function updatealldevices(){
fLen = devicestodisplay.length;
for (i = 0; i < fLen; i++) {
 updatedevice(devicestodisplay[i]);
 }
}

function getdevice(idx){
 console.log("getting device idx" + idx);
 var xmlhttp = new XMLHttpRequest();
 var url = "http://"+domoticzurl+":"+domoticzport+"/json.htm?type=devices&rid="+idx;
 xmlhttp.onreadystatechange = function() {
 console.log(devicestodisplay[i] + " " +xmlhttp.readyState)
 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
 var myArr = JSON.parse(xmlhttp.responseText);
 devicename = myArr.result[0].Name;
 devicestatus = myArr.result[0].Status;
 console.log(devicename + "(" + devicestatus+")");
 adddevice(idx,devicename,devicestatus);
 }
 }
xmlhttp.open("GET", url, true);
xmlhttp.send();
}

function adddevice(deviceidx,devicenametext,devicestatus){
nod=nod+1;
var div = document.createElement("div");
//div.style.width = "100px";
//div.style.height = "100px";
//div.style.background = "red";
//div.style.color = "white";
div.className = 'devicecontainer';
div.innerHTML = devicenametext;
div.id = "DEVICE"+deviceidx;

if (devicestatus=="On"){
 div.style.background = "red";
}

if (devicestatus=="Open"){
 div.style.background = "red";
}

if (devicestatus=="Off"){
 div.style.background = "grey";
}

if (devicestatus=="Closed"){
 div.style.background = "grey";
}

console.log("-------------"+div.id);
//document.getElementById("devicesdiv").appendChild(div);
document.getElementById(div.id).addEventListener('click', function() {
 { toggle(deviceidx); };
}, false);
}

function myFunction(arr) {
 var out = "";
 out += arr[0].result.Status;
 document.getElementById("id01").innerHTML = out;
}

//-->
</script>


<body>

<audio id="scene" src="assets/sounds/beep2.mp3" preload="auto"></audio>
<div align="left" class="toplinks">
<a href="index.htm"><span class="menutext">Home</span></a>
<a href="lights.htm"><span class="menutext">Lights</span></a>
<a href="devices.htm"><span class="menutext">Devices</span></a>
<a href="audiotrial.htm"><span class="menutext">Audio</span></a>
<a href="climate.htm"><span class="menutext">Environment</span></a>
<a href="security.htm"><span class="menutext">Security</span></a>
<a href="activities.htm"><span class="menutext">Other screens</span></a></div>
<div id="notification" align="center" class="notificationpane" onClick="clearnotification(11);"></div>
<div id="devicesdiv"></div>
</body>
</html>

Here’s the landscape.css file


body {
background-color:#000000;
font-family: 'Comfortaa', sans-serif;
margin: 0 0 0 0;
padding: 0 0 0 0 ;
}

.back-to-top {
background: none;
margin: 0;
position: fixed;
bottom: 50px;
right: 20px;
width: 150px;
height: 50px;
z-index: 100;
display: none;
text-decoration: none;
}

.back-to-top i {
font-size: 60px;
}

.timerselecttext {
font-size: 60px;
color:#FFFFFF;
}

.clockdisplay {
font-size: 250px;
color:grey;
}

div.clocktext {
font-size: 100px;
color:#000000;
}

div.weathertext {
font-size: 70px;
color:#000000;
}


div.standardcontainer {
background-color:grey;
position:relative;
width:25%;
height:25vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 4vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.doublecontainer {
background-color:grey;
position:relative;
width:50%;
height:25vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 4vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.devicecontainer {
background-color:grey;
width:25%;
height:12vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 2vw;
box-shadow:inset 0px 0px 0px 1px black;
}


div.albumcontainer {
width:28vw;
height:28vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.trackcontainer {
width:70vw;
height:10vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.audiocontrols {
background-color:grey;
width:100vw;
height:10vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.audioactivitycontainer {

width:70vw;
height:10vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.camera {
background-color:grey;
width:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.camcontrols {
background-color:green;
width:300px;
float:right;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

span {
background-color: #ffffff;
color: #75099b;
font-size:20px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

div.nightclock {
background-color:#333333;
position:relative;
width:100%;
float:left;
padding-top:10px;
overflow:auto;
color:#666666;
font-size: 5vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

span.title {
background-color:#666666;
color:#FFFFFF;
font-size:35px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.timertext {
background-color:green;
color:#FFFFFF;
font-size:70px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.playstate {
background-color:green;
color:#FFFFFF;
font-size:12px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.temptext {
background-color:green;
color:#FFFFFF;
font-size:40px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.roomname {
background-color:green;
color:#FFFFFF;
font-size:40px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.lightroom {
background-color:green;
color:#FFFFFF;
font-size:30px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}


span.device {
background-color:#666666;
color:#FFFFFF;
font-size:20px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.sensor {
background-color:#666666;
color:#FFFFFF;
font-size:18px;
display: inline-block;
padding: 3px 10px;
margin: 0px 5px;
font-weight: bold;
border-radius: 5px;
}


span.screentitle {
background-color:#666666;
font-family: 'Baloo', sans-serif;
color:#FFFFFF;
font-size:30px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
border-radius: 5px;
}

span.menutext {
background-color:#0066FF;
color:#FFFFFF;
font-size:24px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.datetime {
background-color:#009900;
color:#FFFFFF;
font-size:16px;
display: inline-block;
padding: 5px 5px;
margin: 0px 5px;
border-radius: 5px;
}


div.audioroomcontainer {
background-color:#66FF99;
float:left;
height:200px;
padding-top:10px;
overflow:auto;
font-size: 25px;
margin: 0 0 10px 10px;
}

div.nightmodecontainer {
background-color:#666666;
width:150px;
float:left;
height:200px;
padding-top:10px;
overflow:auto;
font-size: 25px;
margin: 0 0 10px 10px;
}


div.notificationpane {
background-color:#FFFFCC;
width:100%;
font-size: 2.5vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.nightnotificationpane {
background-color:grey;
width:100%;
font-size: 2.5vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.toplinks {
background-color:#66CCFF;
top:0px;
left:0px;
width:100%;
font-size: 30px;
box-shadow:inset 0px 0px 0px 1px black;
}

div.popuppanel {
background-color:grey;
width:100vw;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

and here’s the portrait.css file


body {
background-color:#000000;
font-family: 'Comfortaa', sans-serif;
margin: 0 0 0 0;
padding: 0 0 0 0 ;
}

.back-to-top {
background: none;
margin: 0;
position: fixed;
bottom: 50px;
right: 20px;
width: 150px;
height: 50px;
z-index: 100;
display: none;
text-decoration: none;
}

.back-to-top i {
font-size: 60px;
}

.timerselecttext {
font-size: 60px;
color:#FFFFFF;
}

.clockdisplay {
font-size: 200px;
color:#000000;
}

div.clocktext {
font-size: 100px;
color:#000000;
}

div.weathertext {
font-size: 70px;
color:#000000;
}


div.nightclock {
background-color:#333333;
position:relative;
width:100%;
float:left;
padding-top:10px;
overflow:auto;
color:#666666;
font-size: 5vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.doublecontainer {
background-color:grey;
position:relative;
width:100%;
height:25vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 4vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.standardcontainer {
background-color:grey;
position:relative;
width:50%;
height:15vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 2vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.nightclock {
background-color:#333333;
position:relative;
width:100%;
float:left;
padding-top:10px;
overflow:auto;
color:#666666;
font-size: 5vw;
text-align:center;
box-shadow:inset 0px 0px 0px 1px black;
}

div.devicecontainer {
background-color:grey;
width:50%;
height:10vh;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 2vw;
box-shadow:inset 0px 0px 0px 1px black;
}


div.albumcontainer {
background-color:grey;
width:100vw;
height:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.trackcontainer {
background-color:grey;
width:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.audiocontrols {
background-color:grey;
width:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.audioactivitycontainer {
background-color:grey;
width:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.camera {
background-color:grey;
width:100vw;
float:left;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

div.camcontrols {
background-color:green;
width:100vw;
float:right;
padding-top:0px;
color:white;
font-size: 3vw;
box-shadow:inset 0px 0px 0px 1px black;
}

span.menutext {
background-color: #ffffff;
color: #75099b;
font-size:3vw;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span {
background-color: #ffffff;
color: #75099b;
font-size:18px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.title {
background-color:#666666;
color:#FFFFFF;
font-size:18px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.timertext {
background-color:green;
color:#FFFFFF;
font-size:20px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.lightroom {
background-color:green;
color:#FFFFFF;
font-size:16px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}
span.temptext {
background-color:green;
color:#FFFFFF;
font-size:20px;
display: inline-block;
padding: 10px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}
span.device {
background-color:#666666;
color:#FFFFFF;
font-size:18px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.screentitle {
background-color:#666666;
font-family: 'Baloo', sans-serif;
color:#FFFFFF;
font-size:18px;
display: inline-block;
padding: 5px 5px;
margin: 5px;
border-radius: 5px;
}


span.menutext {
background-color:#0066FF;
color:#FFFFFF;
font-size:24px;
display: inline-block;
padding: 3px 10px;
margin: 5px;
font-weight: bold;
border-radius: 5px;
}

span.datetime {
background-color:#009900;
color:#FFFFFF;
font-size:12px;
display: inline-block;
padding: 5px 5px;
margin: 0px 5px;
border-radius: 5px;
}

span.sensor {
background-color:#666666;
color:#FFFFFF;
font-size:14px;
display: inline-block;
padding: 3px 10px;
margin: 0px 5px;
font-weight: bold;
border-radius: 5px;
}

div.audioroomcontainer {
background-color:#66FF99;
float:left;
height:200px;
padding-top:10px;
overflow:auto;
font-size: 25px;
margin: 0 0 10px 10px;
}

div.nightmodecontainer {
background-color:#666666;
width:150px;
float:left;
height:200px;
padding-top:10px;
overflow:auto;
font-size: 25px;
margin: 0 0 10px 10px;
}


div.notificationpane {
background-color:#FFFFCC;
width:100%;
font-size: 16px;
box-shadow:inset 0px 0px 0px 1px black;
}


div.toplinks {
background-color:#66CCFF;
top:0px;
left:0px;
width:100%;
font-size: 30px;
overflow:scroll;
box-shadow:inset 0px 0px 0px 1px black;
}

div.popuppanel {
background-color:grey;
width:100vw;
float:left;
padding-top:10px;
overflow:auto;
color:white;
font-size: 2vw;
box-shadow:inset 0px 0px 0px 1px black;
}

Syncing your Hue lights to Sonos – part 2

I successfully built a program to sync up Phillips Hue lights with Sonos in this post.  Unfortunately the Echonest API is no longer in use and I have run up to difficulties with authorising the replacement Spotify API (any help with that is gratefully received!).

I’ve also always fancied myself as a lighting technician and the responsiveness of the Hue lights got me thinking that there might be a way to sync my lights to a specific track or a bunch of tracks.  Imagine a party where the lights “do their own thing” until a track comes on that is recognised, and then the lights sync up in exactly the way you have programmed them.

I wanted to make the program do as a minimum the following:

  • Have a “default” setting where the lights change in a generic but random way
  • When a track starts, to detect that the track has “light cues” associated with it and to start playing the light cues alongside the track
  • To be synced to the second, so that the lights always “keep up” with the track
  • To be able to “join in” and catch up with the track, even if it is paused or rewound (completely or partly)

The attached files are small, and are by no means polished, but they do the job and the program can run all night successfully changing between “I don’t know this song so I’ll just do a random light show” to “Oooh! I know this song, let me load up the light cues and join in!”.

There are two parts: and Excel spreadsheet where the lights are encoded, and a small python program that is run to manage the current playing track and loading of the associated light cues if they exist.

This program also relies on the node-sonos-http-api code by Jishi available here (https://github.com/jishi/node-sonos-http-api)  with installation instructions mentioned in my previous Hue/Sonos post.  This node program should be running (it always is on my Raspberry Pi as I use it to control all Sonos throughout the flat using HTTP commands).

The Excel spreadsheet:

The spreadsheet is a macro-enabled spreadsheet that allows you to create, load, edit and save light cue files (saved as *.lig files).  Simply open the spreadsheet and listen to your music.  When you come to a part of the music that deserves a light change, you go to that line and then choose a function and type in some numbers.

At the top of the spreadsheet, type in the name of the track you want to set up a light cue datafile for.  Enter the whole location of the file but without any extension.  For example, if the track is called Like a Cat, and you want it to be saved in your C:\Users\Sample\Documents folder, in cell C5 type in C:\Users\Sample\Documents\Like a Cat

A word of caution here: run the program and change the Sonos to the track you want to encode first.  The program will show the exact name of the file it is looking for.  In order to simplify the program, it strips out brackets and hyphens, as well as commas and full stops (periods).  You may therefore end up with a track name that has a few extra spaces in it, so look out for that.

In cells C9 to L9 enter the reference numbers of each light you want to use in this song.  You can use all available lights or just some.  You don’t have to select the same lights for each song, but remember if one known song comes on after the other, some lights may remain as you left them in the first song if you don’t include them in the second song’s list.

In cell C11 enter the group number of the Hue lights you want to control.  This is useful as some of the light cues you can use address the whole group rather than individual bulbs in sequence.  You can set groups using the Hue API.

Listen to a song and then work out in your head what would be a good lighting effect.  Maybe you’d like each bulb to flick between strong random colours every 2 seconds, or maybe you want a low orange glow on all lights until the chorus, and then raise up the lights to a vivid red, maybe making them flash at just the right moment.

For each song you can enter as many or as few lines of light cues as you like, even just one at the beginning of the track, to change the lights to match the mood of the music for the whole song, or maybe an epic display matched perfectly to each bar.

Functions available are:

ALLSET which changes in sequence each light you have specified to use

CHGALL which changes the whole group of lights instead of each individual light

FLASH which pulses the lights either once or twice (depending on how fast your host machine is)

INDIV which controls one individual bulb (enter this in the Lightref/delay column). If you want to change 2 bulbs together just put two INDIV lines together, one after the other, a second apart.

ALLBRI ignores a lot of the variables supplied and just changes the whole group of lights to a specific brightness

ALLOFF and ALLON switch the whole group of lights off or on, at a transition time you specify

CHGSEQ changes each light sequentially, just like ALLSET, but waits a certain number of seconds you specify (enter this in the Lightref/delay column)

How to write a light cue line:

First choose what you want to do.  Let’s say in the very first second of the track we want to bring up all the lights to a dark, moody red colour.  Enter the following line.

hms1

I want to force the lights to one specific colour, so I have entered the same number into MinHue and MaxHue so that I am sure that colour 0 (red) will be chosen.

Then we want to spring into vibrant colours which change every 10 seconds, from the 15th second of the track.

hms2

Notice this time I have set MinHue to 0 and MaxHue to 65000 – this gives a good range of colours, in fact the whole spectrum.  I set MaxSat and MinSat to 255 as I don’t want to mix the colours with any white.  The closer you get to 0 for Sat, the more white is mixed in with the colour.  I have set TransTime to 10 because I want them to change over the period of 1 second.  I could have put 5 or 1 in there if I wanted a faster or instant response.  There is some randomness between the brightness too, from 100 (mid-dim) to 255 (bright).

I then copy this line from column D to column L and then paste into 25 seconds, 30 seconds, 35 seconds and so on.

When I get to the end of the song I just finish.  Nothing else to do unless you want to add some other flourishes.  Let’s go back to 2 minutes and 24 seconds, as there is one enormous drum solo that starts there and I would like to put pulsing white lights for 10 seconds.

hms3

Now just click the Export button at the top.  Excel won’t reportanything (I told you it wasn’t finished) but the light cue file will be saved in the location and with the name you specified in cell C5.  Make sure it has shown up.

You can quickly build up quite a catalogue of “*.lig” light cue files.

When done, copy these files to the location where you’ll be running the python program (oh, you’ll need to create or download one called “default.lig” which is the file that the program will open if the track playing does not have its own file).

Inside the python program change 192.168.1.94:5005 to the address of the server that is running for node-sonos-http-api (the address where server.js is running) and then change the Sonos zone name from “Living%20Room” to the one that will be playing the music.  If you want more than one player to play music (at a party for example) make sure the player you specify here is the “main” player and additional speakers are grouped to it.

Run the python program (in python 3) and you’ll see it start to wait for the Sonos player to start playing something.  Play one of the songs you have created a light cue file for and make sure it opens.  If it does, you’ll enjoy the fruits of your labour! If not, first check what file the program was actually looking for (each title is printed out by the program when the Sonos player changes track).

I hope you enjoy this small program and light cue creation spreadsheet.  Please let me know how you’d improve it or if you use it!  Thanks as always for your comments, they keep me writing these posts.

Downloads

Take off the “.doc” extension before using – WordPress does not allow me to upload *.py, *.xlsx or my own *.lig files.

hms.py

Default.lig

Hue Music Encoder 2.xlsm

Hue Go

My latest purchase is a Hue Go.  It’s a rechargeable (and therefore totally mobile) version of a Philips Hue bulb, encased in a bowl shaped frosted enclosure.

After a 1 and a half hour charge the light is good to go for 3 hours.  There’s a build in ‘bulge’ stand to allow for it to be pointed towards a surface, like the Iris or the Bloom.

What makes the light more usable for the whole family is that there’s a button on it to cycle through moods and colours, and to switch the light on or off.  The moods include such campness as Warm white, Cozy candle and Night adventure (which has a pink hue and a heart icon, I wonder what that mood is for…)

I like it, and think it’s going to make a great addition to the 20 or so Hue lights we already have.  For one thing, it can be moved in the dry to a balcony table – perfect for the approaching summer nights.

bowl2From a home automation point of view, there may be issues with having the light in ‘mobile’ mode, i.e. unplugged.  According to the literature, the light switches from ‘standby’ to ‘off’ after 2 hours of inactivity when in battery mode, so I’m guessing that means even if an ‘on’ command is sent, it won’t respond until either plugged in again or the button is pressed.  Obviously this is a fail-safe to ensure the battery does not drain completely.

£80 puts the Hue Go at the same price point as the Lightstrips, and I can see why.  It’s well made and even looks pretty when in standby mode.  I like the modern, ‘glossy glass’ look of the front panel and this continues even to the back where the understated function button resides.

The pros far outweigh the cons though, especially as after testing the Hue Go with the homemade disco controller I made, it is very responsive to commands even when in battery mode.

The brightness and saturation (although not seen well in these pictures) is as expected for a Hue product – read: amazing,

The literature seems to suggest that on battery mode, the brightness is reduced to extend battery life.  I can’t say I’ve noticed that, unless they mean that when displaying the pre-set moods that feature is enabled.

The charging cable (reminiscent of a cable for the Living Colours lamps) looks strong enough to be plugged and unplugged regularly.  The charging port on the unit itself is also recessed quite a distance which is good for protection purposes.

bowl3

Overall, a great product and another reason to get excited about Hue!

DIY: Make a better handset controller

Although I like the look of the standard LightwaveRF hanset controllers, the user of said controller has to remember what device is switched on and off when a numbered button is pressed.  This requires Mastermind-level memory.

Besides, I don’t use the controllers for ‘On’ and ‘Off’ per se, rather just to send a signal to Domoticz, so that the computer can decide what to do (or to reject the command altogether).  This means that the same button can be used for ‘On’ and ‘Off’, and therefore the same handset can be used for multiple functions (lighting moods and audio for example).

So a way to make these controllers more intuative is to add personally created templates to them so that the controller becomes part of your home setup.  You can choose a theme and run with it (as shown).

Firstly, this procedure is reversible, so if you don’t like what you’ve done, you can undo it all and revert back to your original handset.  Just remember to keep all the bits that come off the controller safe.

Secondly, you will lose some functionality.  Because the switch is removed, you don’t have the ability to select button set A, B, C or D.  I personally don’t care about this because the whole reason I wanted to make a custom cover for my remotes was to make them as user friendly as possible.

And if your family can remember that C4 controls the TV power, and D2 controls the garage door, and B1 to B4 control the kitchen lights, then you shouldn’t be reading this blog: you should all be at Cape Canaveral getting ready to take off a-la-Lost in Space.  Danger, Will Robinson!

Basically, you’ll end up with 10 buttons per customised handset.

 

1. Peel off the backing sticker from the controller to expose a screw and unscrew it.

1

2. Prise open the controller.  The things that should come off (quite easily are: The front cover, the rubber keys, the switch (which may come off in one part or may split into the plastic part of the switch and the metal part).  And the screw of course.  If the circuit board has come off from the base of the remote then I think you used a little too much force!  Get you, butchy.

2

You’ll notice that the rubber buttons are not needed – the ‘pads’ you can see in the image are self-contained switches.  Like the ones you get on blister-remotes.

3. Design your overlay.  I will post a template that you can use (search for the ‘Templates’ tag).  Don’t forget to leave a space where the led will shine when a button is pressed.

I used icons from Flat Icon.

4. Print and cut out the overlay.  I found that good quality bright white matt card worked best.

You may need to cutoff tiny strips from any side to make the overlay fit correctly.

3

5. Put one or two layers of the same card underneath the overlay (between the keys and the overlay).  This will make the control pad seem more springy.

6. Tape on the overlay onto the front of the controller.  I used normal tape here but I should have used a thick tape so that I only needed to use 1 pass, rather than 3 passes as I have done in the above picture.

Make sure that you don’t tape up the drawer on the back, otherwise you’ll have probelms when it comes to changing the battery.

7. Voila!  The below example is for the kitchen.  That’s why there is a cute chef as one of the buttons: when you press the chef button, all the devices in the kitchen switch on.

4

8. Now it’s time to program what happens when you press the keys.  More on that elsewhere in this blog.

5

Here’s an example handset (this one is for my bedroom).  It’s been created in the same style as all the ‘hardware controllers’ in the flat, so that picking it up and using it should be second nature.

You can for once use your creative side and your geek side together for this project.  Why not speak to members of your family to get an idea of what they’d like to see on the controllers (favourite colours, family member/pet’s faces etc).  Maybe they could even design the templates for you!