Controlling your lighting with Hue and Perl

It seemed like a ridiculous extravagance to be able to control your home lights with some very expensive Philips Hue lightbulbs but having lived with them for a bit I’m actually quite impressed at how well they work and the range of lighting they can produce.

My next thought obviously then turns to ‘how can I make my lighting do something more useful?’. The Hue iphone app is quite clever (geofences allow you to to turn the lights on as you arrive home for example) and IFTTT integration is also fun but rather limited (only one light at a time, no conditional triggers). Philips have kindly documented the Hue API and it’s pretty straightforward to use – time to break out the Perl.

There is a Perl module on CPAN (Device::Hue) but it’s not all that great so I decided just to poke the Hue bridge directly, and as it’s just JSON, this is pretty easy.

So here are a couple of examples of things I’ve done. I wouldn’t consider them re-usable as they are but are probably a useful starting point if you’re thinking of doing something similar.

  • Before using you’ll need to set up an new user’s API key on the bridge, just follow the Philips instructions to do this.
  • You’ll also need to know the IP/URL to your bridge on your network.
  • As the colour space (‘hue’) is a bit complicated to determine, I’ve tended to configure the lights to how I want them then do a GET of the light state and then reused those values in the script. Probably a nicer way to work this out but for these quick and dirty scripts it’ll be fine.

Lights on at sunset

I trigger this via cron at (say) 3pm daily, it then just checks every five minutes if the sun has set for that particular day yet before exiting. The lights get turned off by Hue app scene timer or by hand.

#!/usr/bin/perl

use Astro::Sunrise;
use common::sense;
use LWP::Simple;
use LWP::UserAgent;

open my $LOG, '>>', '/scripts/lights.log' or die "Unable to open log for writing $!";
$| = 1;

my $sunset = sun_set(0.0,50.0); #long, lat
$sunset =~ s/://g;

my $uplighturl = "http://bridgeurl/api/apikey/lights/1/state";
my $downlighturl = "http://bridgeurl/api/apikey/lights/2/state";

my $uplight = '{"on":true, "sat":220, "bri":26, "hue":34440}';
my $downlight = '{"on":true, "sat":220, "bri":190, "hue":34440}';

while (1) {

my @curtime = localtime();
my $curtime = join '', @curtime[2,1];

if ( $curtime > $sunset ) {

print $LOG "$curtime is after $sunset, turning lights on\n";
setlight($uplight,$uplighturl);
setlight($downlight,$downlighturl);
exit(0);
}

else {

print $LOG "$curtime before $sunset, sleeping...\n";
sleep 300;

}

}

sub setlight {

my ( $body,$bridgeurl ) = @_;
my $req = HTTP::Request->new( 'PUT', $bridgeurl );
$req->header( 'Content-Type' => 'application/json' );
$req->content( $body );
my $lwp = LWP::UserAgent->new;
my $response = $lwp->request( $req );
print $response->decoded_content;

}

Tell me what the weather & tube is like before I leave the house

In the mornings I’d quite like to know if I need to take an umbrella, the tube is broken, or both. I have a lamp on the exit route that shows blue (rain), red (tube) or purple (apocalypse). I’ve used a couple of API services for tube & weather that return things in a nice JSON format that plays well with Perl, but you can adapt for your own uses.

#!/usr/bin/perl

use common::sense;
use LWP::Simple;
use LWP::UserAgent;
use JSON;

my $tubestatusurl = "http://api.tubeupdates.com/?method=get.status&lines=jubilee&format=json";
my $weatherurl = "http://api.openweathermap.org/data/2.5/forecast/daily?q=London,uk&cnt=1&mode=json&units=metric";

my $bridgeurl = "http://bridgeurl/api/apikey/lights/1/state";

my $tubebroken = '{"on":true, "sat":255, "bri":255, "hue":65527}';
my $raintoday = '{"on":true, "sat":255, "bri":255, "hue":47124}';
my $apocalypse = '{"on":true, "sat":255, "bri":255, "hue":58009}';

my $lightcounter = 0;

my $tubejson = get( $tubestatusurl );
my $decoded_tubejson = decode_json( $tubejson );

my $tubestatus = $decoded_tubejson->{response}{lines}[0]{status};

if ( $tubestatus =~ /good service/ ) {
print "Good status\n";
}

else {
print "Jubilee line problems\n";
$lightcounter = 1;
}

my $weatherjson = get( $weatherurl );
my $decoded_weatherjson = decode_json( $weatherjson );

my $weather = $decoded_weatherjson->{list}[0]{weather}[0]{main};
if ( $weather =~ /Rain/ ) {

print "Rain today\n";
$lightcounter += 2;

}

if ( $lightcounter == 1 ) {

setlight($tubebroken);

}
elsif ( $lightcounter == 2 ) {

setlight($raintoday);

}

elsif ( $lightcounter == 3 ) {

setlight($apocalypse);

}
sub setlight {

my ( $body ) = @_;
my $req = HTTP::Request->new( 'PUT', $bridgeurl );
$req->header( 'Content-Type' => 'application/json' );
$req->content( $body );
my $lwp = LWP::UserAgent->new;
my $response = $lwp->request( $req );
print $response->decoded_content;

}