Wednesday, 11 October 2017

X10 Home Security DIY - Rules Engine

X10 Home Security DIY - Rules Engine

Adding a json-based rules engine to our Home Automation DIY means we can make any action happen when any IoT device event triggers it.  This is a practical and flexible alternative to a commercial solution, or to using the (too many!) apps for each of our IoT devices.

This means we can turn on a light when the front door opens, or send a text message when the alarm triggers: the possible combinations are almost infinite (which is why it is better to do this with rules than with a big 'if' statement).

I am quite pleased with this addition to our home security DIY - we now have our house reacting to us (through our presence), instead of us having to explicitly control it by pressing buttons or running apps.  By controlling our IoT devices using rules, the house seems quite artificially intelligent...

Here's how we do it:

1. Each IoT device has its own html section (a module), its own JavaScript file to control the actions and display elements, and its own Perl CGI file to control access to the device API.  The Perl CGI resides on the server (a linux host; for us it is a Raspberry Pi running Raspbian and Apache); the JavaScript is run on the client.

To facilitate rapid communication of each IoT device state, a json file is used.  For example, the json file for our security module (Security.json) looks like this:

{"X10security":"off"}

That is, it is a simple json-format list of key:value pairs, stored in a file.  Any time the Perl file executes a command, it updates the json file (and returns it to the JavaScript client so that the web interface is updated).

2. The rules engine, coded in Perl, reads the current state of all of the IoT modules from the json files, and stores it in a Perl hash (associative array).  The rules themselves are also in a json file, and are structured as follows:
  • a rule name, e.g. "name":"Rule1"
  • a set of trigger events, e.g. "front_door_opened", any of which will cause the rule to be evaluated
  • a set of conditions, all of which must be true in order for the rule to fire
  • a set of actions which are performed if the rule is fired
Conditions are tested against the list of module states from the json files, whose values are all stored in the hash (associative array) to make the lookup easy.  A json-format rule looks like this:

{ "name": "Rule3a Motion sensed", 
      "triggers": [ "motion_sensor_activated" ],
      "conditions": [ { "module": "security", "name": "X10security", "value": "off" },
        { "module": "devices", "name": "wemo", "value": "off" } ],
      "actions": [ { "module": "devices", "name": "wemo", "value": "blink" } ]
},
 
The above rule basically reads "If the motion sensor triggers, and security is off, and the wemo device is also off, then set the wemo device to blink.

The actual rule-to-state comparison is:

First, we check that the trigger event matches at least one of the rule triggers:

     foreach my $ruletrigger (@$ruletriggers) {
       if ($trigger eq $ruletrigger) { $triggerresult = true; }
    }

Then, we check that all of the conditions are true:

  if ($triggerresult eq true) {
    my $overallconditionresult = true;
    foreach my $rulecondition (@$ruleconditions) {
      # look up the value/state of the condition name
      my $stateactual = $state{$rulecondition->{'module'}}{$rulecondition->{'name'}} ;
      my $conditionresult = eval ('$stateactual ' . eq . ' $conditionactual');
      if (!$conditionresult) {
        $overallconditionresult = false;
      }      
   }
}

Finally, if all of the conditions are met, we perform all of the actions (which will involve calling the Perl functions for each of the listed modules to execute the action, just like calling them from the Javascript files).

if ($overallconditionresult) {
 foreach my $action (@$ruleactions) {
         my $ret = `/usr/bin/perl $action->{'module'} -d "$action->{'name'}=$action->{'value'}" `;
 }

Now, by coding up a (remarkably short) set of rules, we can automate all kinds of things across our devices.  We can send all manner of notifications if the door opens, or we can turn lights, tv's, plugs, etc on or off.  We have implemented a presence detection algorithm in just two rules, and from there we automatically turn the security system on when we leave, and off when we come home.  By adding a 'heartbeat' using cron, we can make things happen at specific times.

In a future update, I'll post the complete code for the rules engine (just 300 lines!).

Monday, 28 August 2017

X10 Home Security DIY - Adding voice control and linking up with online services

X10 Home Security DIY - adding Voice Control with Google Home

The last phase of the implementation involves linking up the Home Automation server with the online world, to take advantage of Google Home and services like IFTTT

IFTTT makes it possible to (for example) schedule a light to turn on at dawn/dusk, to link a Google Home custom voice command with X10, or to have our lights turn red when Manchester United score!

Note that this is the opposite of using IFTTT for notifications - now, we want an external event to trigger an internal action.  This must be accomplished securely - we do not wish to forward a port through our router unless it is highly secure.  Most IoT devices that link to outside web services do it through a secure tunnel: for example, using ngrok.

Using ngrok, we will create a secure tunnel to our internal server: specifically, to the x10cmd.pl CGI script that drives the X10 CM15A.

So the steps are as follows:
1. Create an IFTTT account if you do not have one from the notification section.
2. Add the Webhooks service to your IFTTT account.
3. Create an App (recipe) to trigger on the weather channel, with the "Then" action being the Webhook - fill in the destination IP with your ngrok address (see below). 
4. Install and run ngrok on your server to point to the server port and the CGI script that contains your x10cmd.pl file.

The URL field in the IFTTT app is as follows (use either http or https):
http://serverid.ngrok.io/x10cmd.pl
Where:
  serverid - the URL reported by ngrok when you start it on your server.

The Method field is GET or POST.  With GET you paste the parameters into the URL field (e.g. append ?device=mydev&cmd=mycmd to the url).  With POST you specify the Content Type as application-x-www-form-urlencoded, paste the parameters into the body (e.g. device=A3&cmd=ON) (Note there is no ? needed in the body unlike in the URL).
Where:
  mydev - your internal X10 device id (e.g. A3)
  mycmd - you internal X10 command (e.g. ON or OFF)

For a voice command to do the same using Google Home, create an IFTTT app that triggers on Google Assistant instead.  Here, you can use a custom phrase (like "OK Google, turn the A3 light ON").  You can even use parameters in the trigger phrase, so that it only takes one app to trigger all of your devices; to do this, select the "Say a phrase with a text ingredient" type of Google Assistant trigger, and place $ in they phrase, and "text ingredient" in the action to send the paramter on to ngrok.