The lighting in my bedroom uses Philips Hue bulbs — specifically, the coloured ones. Last night, I decided it would be nice to set the three lights in my bedroom to cycle slowly through a set of warm colours using a script.
I didn't want harsh transitions from one colour to the next, but for the lighting to fade from one colour to the next in a smooth gradient. Also, I didn't want the three bulbs to all be the exact same colour, but wanted each bulb to be at different stage in the cycle, like they're "chasing" each other through the colours.
So I whipped up a quick script. It requires the command-line tool hueadm to be installed and set up before we start. You can run hueadm lights to get a list of available lights, and in particular, their ID numbers.
First step, we need to be able blend two colours together. This is necessary to be able to create smooth transitions between colours. I stole a little code from Color::Fade on CPAN to do this.
Pretty simple averaging of two RGB values. You could arguably get a better effect by converting to HSL, averaging those values and then converting back to RGB, but in practice, using RGB seems fine.
Now we can build the gradients. We want to take an array of colours and make it into a cycle by copying the first colour to the end, then create a new array, copying the colours into the new array, but inserting a blend of each pair between them, so our initial array of N colours grows to an array of 2N colours. Then repeat that process a few times.
Nice. Now we know what colours our lights should be.
Here's a quick Moo class to control a lightbulb, given its ID. Nothing interesting to see here.
Finally we can work on the logic. We have an array of colours; we need to pick an index in that array for each bulb. My array of colours (after being expanded into a gradient), ended up being 160 colours, so it seemed that a good starting point was to set my three bulbs initially to colours 0, 16, and 32 in the array. Something along these lines:
Then pause for a second or two, then add one to each index (looping any that go over 160 back to 0) and repeat. Here's the full code for that:
Now let's test it out...