Home Automation

I have been wanting to set up a home automation system for some time now and have finally taken my first steps.

Do a bit of investigation on this subject, and you will quickly be overwhelmed with options. The grand daddy of these systems is a power-line based system called X10.  Unfortunately, X10 has always had significant reliability issues. In recent years there have been numerous wireless based options that have entered the marketplace such as Lutron, Zwave and Zigbee, all of which tend to be expensive and are far from full-proof.

Wanting to find a solution that was reasonably priced and solidly reliable, I finally settled on a system called Insteon by Smarthome.  Basically, the Insteon platform is a much improved version of X10.

To start my foray into this area, I purchased five Insteon light switches, two access points and a controller. The access points provide two primary functions. The first and most important function is to couple the two electrical phases that power my home, thus allowing Insteon commands to freely travel to all locations in the house. The second function is to accept RF signals from non power-line devices such as remote controls and then convert those signals into Insteon signals that are then transmitted throughout the Insteon network. The light switches are replacements for standard switches that can both send and receive signals across the Insteon network. Last but not least is the controller which is the device that sends commands across the network to control Insteon devices such as the light switches mentioned previously.

After receiving my order, I installed my new light switches (if you don’t know what you’re doing, I strongly recommend that you hire a licensed electrician to do this for you), my access points and the controller. Once all these pieces were in place, I set about creating rooms and scenes and registering my switches with my controller. I’ll pause here to say that none of these steps are difficult, simply follow the instructions provided with your devices and you’ll be up and running in no time.

One of the first things I wanted to setup was automatic control of my outdoor lights. I did this by creating a room called Outside and then created a scene called Porch Lights and added the appropriate switches. Fortunately the controller supports the ability to program automatic on and off events based on time. That said, that support is limited to set times on set days of the week. For me, that wasn’t enough. I wanted to turn my lights on and off based on sunrise and sunset which means the time will change each and every day. I also wanted the ability to automatically adjust for daylight savings without having to manually intervene.  After a bit of study, I found that the controller does not appear to support either of these functions.

To achieve my goal, I decided to write a simple script to make the system do what I wanted.The folks over at Smarthome offer a number of units (PLM, PLC-Serial, PLC-USB) that are designed to allow developers to communicate with devices on an Insteon network, but since I already had the SmartLinc controller, I decided to use that as my primary interface. In order to achieve this, I needed to figure out what was being passed through the web interface when I clicked on the button to turn my porch lights on and off. There are many ways to achieve this and I chose to install the Live HTTP Headers Firefox add-on which would allow me to see exactly what was being passed from my browser to the PowerLinc Controller web application. Once I figured out what to pass to the web application, I set about writing my script. In this instance, I chose PHP as my scripting language and Linux as the platform for execution but you can achieve the desired outcome using any operating system and just about any scripting language such as Ruby, Bash, Python, or Perl.

Here’s my code:

#!/usr/bin/php -q
<?php
/*x10-porchlight.log >&1
*
* Notes: This script was written and tested on Linux (kernal: 2.6.22-14-generic)
* and using PHP CLI 5.2.3-1ubuntu6.3
*
* In order for the script to work correctly, ensure you have ample diskspace
* and ensure that you have Create, Write and Delete rights on your filesystem
* in the folder where the script will be executed; it will also be necessary
* to ensure that the script is set to execute.
*
* It should also be noted that the offset may be off on the dates that time
* changes between DST and STD time. I could build additional logic to account
* for this but it adds too much unnecessary complexity.
*
* -------------------------------------------------------------------------
* Calculate the sunrise and sunset time for Houston, Texas
* Latitude = 29.45 North
* Longitude = 95.21 West
* Zenith = 90.8333
* Offset DST = -5
* Offset = -6
* -------------------------------------------------------------------------
*
**/
$lng = 29.45;
$lat = -95.21;
$zen = 90.8333;
$when = time();
$now = date("D M d Y H:i",$when);
$base_path = "/root/php-scripts/x10/";
$self = "x10-porchlight.php";
$log = $base_path . "x10-porchlight.log";
$my_sunrise_offset = " - 20 minutes";
$my_sunset_offset = " + 20 minutes";
$porch_lights_on = "http://192.168.1.5/0?1149=I=0";
$porch_lights_off = "http://192.168.1.5/0?1349=I=0";
// Let's setup our offset for our calculation. To do this correctly, we need
// to adjust for daylight savings if it is in effect. I've accounted for
// 2008-2011; beyond this, the script will need to be adjusted or the time
// will default to standard time.
// Site: http://www.infoplease.com/spot/daylight1.html
$offset = set_offset($when);
// Let's figure out what time today's sunrise and sunset occur
$today_sunrise = date_sunrise($when, SUNFUNCS_RET_STRING, $lng, $lat, $zen, $offset);
$today_sunset = date_sunset($when, SUNFUNCS_RET_STRING, $lng, $lat, $zen, $offset);
// Now we need to figure out what it is we're supposed to be doing. Since the
// script could be run manually or automatically, we need to set for as much
// as possible and then make a determination as to our course of action.
if ($when >= strtotime($today_sunrise . $my_sunrise_offset) &&
$when < strtotime($today_sunset . $my_sunset_offset)) {
if ($_SERVER["argv"][1] == "-d") {
echo $now . " - Ensuring porch lights are offn";
$page_data = get_data($porch_lights_off);
} else {
echo $now . " - Turning off porch lightsn";
$page_data = get_data($porch_lights_off);
echo $now . " - Setting up AT job for today's sunetn";
if (date("mj") == "1031") {
$output = shell_exec("echo '" . $base_path . $self . " >> " . $log .
" >&1' | at 23:30");
} else {
$output = shell_exec("echo '" . $base_path . $self . " >> " . $log .
" >&1' | at " . $today_sunset .
$my_sunset_offset);
}
}
} elseif ($when < strtotime($today_sunrise . $my_sunrise_offset)) {
if ($_SERVER["argv"][1] == "-d") {
echo $now . " - Doing nothingn";
} else {
echo $now . " - Setting up AT job for today's sunrisen";
$output = shell_exec("echo '" . $base_path . $self . " >> " . $log .
" >&1' | at " . $today_sunrise .
$my_sunrise_offset);
}
} elseif ($when >= strtotime($today_sunset . $my_sunset_offset)) {
if ($_SERVER["argv"][1] == "-d") {
echo $now . " Doing nothingn";
} else {
echo $now . " - Turning on porch lightsn";
$page_data = get_data($porch_lights_on);
$when = time() + (24 * 60 * 60);
$tomorrow_sunrise = date_sunrise($when, SUNFUNCS_RET_STRING, $lng,
$lat, $zen, $offset);
echo $now . " - Setting up AT job for tomorrow's sunrisen";
$output = shell_exec("echo '" . $base_path . $self . " >> " . $log .
" >&1' | at " . $tomorrow_sunrise .
$my_sunrise_offset);
}
} else {
echo "I'm confused and don't know what to do!n";
}
// -------------------------------------------------------------------------
//Function to get the data from a URL
function get_data($url)
{
$ch = curl_init();
$timeout = 5;
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$timeout);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function set_offset($when)
{
if ($when > strtotime("09 March 2008") &&
$when < strtotime("02 November 2008")) {
$offset = -5;
} elseif ($when > strtotime("08 March 2009") &&
$when < strtotime("01 November 2009")) {
$offset = -5;
} elseif ($when > strtotime("14 March 2010") &&
$when < strtotime("07 November 2010")) {
$offset = -5;
} elseif ($when > strtotime("13 March 2011") &&
$when < strtotime("06 November 2011")) {
$offset = -5;
} else {
$offset = -6;
}
return $offset;
}
?>

I’m sure my code could be refined, but it serves its purpose and does so well.  If you read through the script you’ll see that there are a number of actions taking place.

  • I have young kids and they play with the lights so I wanted to ensure that the porch lights are off during the day, so every hour I run the script with a -d switch which runs one piece of code to force the lights off. Since the SmartLinc Controller doesn’t show device status (e.g. on or off), what I’m doing here is a bit of a brute force tactic, but it works.
  • We don’t do Halloween so I check to see if it’s October 31st and if it is, don’t turn the lights on until 11:30PM.
  • Daylight savings is a moving target so I check the date and adjust our offset accordingly.
  • Last but not least, I’m writing everything out to a log file so I can make sure that everything is behaving correctly.

That’s all there is to it! 

My next tasks will be to:

  • Find a way to check the status of my garage door, and if I have inadvertently left it open have my controller automatically close it while considering various condition possibilities such as time of day and house alarm status, etc.
  • Automatically turn the house lights on when we open a perimeter door if the alarm is set so we don’t walk into a dark house. I might even try to do this based on the time of day. In other words, only do this if it’s after sunset and before sunrise.

Happy coding!

~GT~