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!).