Making a scene

Being able to define scenes is one of the most important aspects in a Home Automation (HA) system. Not being able to define your own scenes, things you want to happen automatically, degrades a HA system to nothing more than a big expensive monitoring system or just a remote control. That’s not Home Automation, that’s Home Control!

ReteScenes let you automate certain things. Lowering the roller shutters so that the temperature inside doesn’t become unpleasantly high, switching lights on or off based on motion, sunset, sunrise, sending notifications to your smart phone, etcetera. Anything is possible, as long as your HA system is equipped with the right sensors (information) to trigger that specific scene you’d like it to execute for you.

When I started (re)developing my system in NodeJS, one of the first items on my to-do list was finding some sort of starting point to create my own scenes. Preferably developed for Node.JS, with its own DSL (Domain Specific Language), the ability to embed simple if/else statements and calculations inside the scene definitions and crontab-ish triggered scenes were my most important conditions to look for. When I looked at HomA (a NodeJS based framework for building smart homes) when I started exploring whether NodeJS was a good choice for rewriting my HA system, I noticed that it uses nools as a rules engine; Nools is a rete based rules engine and when I read the documentation it looked like a good starting point. HomA also provided a good starting point in terms of combining MQTT & nools.

In fact, I shamelessly copied this file from the HomA repository and added the things I missed and changed what I wanted to do different. Here’s a couple of things worth mentioning about how I integrated nools in my HA system and how I got scenes to actually automate things.

Lets have a quick look at how easy it is to use nools as the scene engine or rules engine. I’ll take some of my own scenes as example.

rule testrule {
  when {
    m1: Message m1.t == 'testsensor/status' && m1.changedTo('open');
  }
  then {
    unchange(m1);
    log('Execute rule Office testlight on');
    execute('command','{"address":"testactor", "command":"ON"}');
 }
}

Easy right?… when the status of the testsensor (in this case a Visonic door/window sensor) on my desk changes to ‘open’ , the testactor (a PLCBUS on/off module) which is also on my desk, will switch on a light.

But with this scene, when I close the sensor again, nothing happens. I would need a second rule to switch off the light when the sensor changes back to ‘closed’, in a similar way as with the rule above.

But that won’t be necessary, cause I can add some code to the rule, like this:

rule testrule {
  when {
    m1: Message m1.t == 'testsensor/status' && m1.changed;
  } then {
    unchange(m1);
    log('Execute rule Office test open');
    if (m1.p === 'open') {
      execute('command','{"address":"testactor", "command":"ON"}');
    } else {
      execute('command','{"address":"testactor", "command":"OFF"}');
    }
  }
}

With this scene, the light ‘follows’ the door/window sensor. And it’s totally immune to a particular brand or technology – cause all those are virtually interconnected by my HA system.

Here’s another one; this one switches off the pump (with a Plugwise Circle) of the floor heating when the temperature in the living room exceeds the thermostat setpoint on my Honeywell Chronotherm :

rule floorpump_off {
  when {
    c: Clock isTrue(c.inMinutes([0,30]));
    m1: Message m1.t == 'otgw/roomtemp';
    m2: Message m2.t == 'otgw/roomsetpoint';
  } then {
    unchange(c);
    log('Execute rule Floor pump off?');
    if(m1.p > m2.p){
      execute('command','{"address":"236D7E", "command":"off"}');
    }
  }
}

Especially the ‘code’ part is very nice to have – you can even call functions defined elsewhere in the rules file; nice!

As I said earlier, there were some things I had to do to make nools work; here are some of the things I had to take care of.

The amount of topics

I’ve got a MQTT root topic called value/. This topic contains all the so-called device values available in my system. They’re all retained, so by letting the rules engine subscribe to the value/# topic, the rules engine will always have access to all the last-known device values. With device values I also mean a great deal of virtual device values, values not produced by physical devices, but mostly calculated ones. Examples of those virtual device values are: GPS locations of our house and ourselves, todays usages of power and gas, position of sun, moon, house mode (eco, deepsleep, awake) and so forth.

All this adds up to a total of more than 1000 device values – so in practice there’s always something changing, almost each second – a temperature, usage value, motion, location, whatever. But nools was just too busy with all those ever-changing values it had subscribed to. So the first task for me was to reduce the number of topics it would subscribe to: no longer subscribing to the value/# MQTT topic, but only those that nools really needs, based on the contents of my rules (scenes) file. So I wrote a small routine that parses the rules file and extracts those topics being actively used in the rules file. This reduced the number of topics nools had to subscribe to 15. This was a big relief for the Raspberry Pi on which nools initially ran, cause it drastically reduced the amount of CPU cost for nools 😉

Time related stuff

In Homa, the matching of the rules with the facts was only performed when a new MQTT message was received. This made it hard to implement rules that had a time trigger.
Taking into account that I’m doing my best to keep the workload for nools as small as possible, I added some additional code so that the minimal interval of matching is set to 1 minute.
Now I could do things like shown below, which is executing a script every 4th minute of the hour:

rule bwiredxml {
  when {
    c: Clock isTrue(c.isMinute(04));
  } then {
    forget(c);
    log('Execute rule Bwired XML');
    execute('command','bwiredxml');
  }
}

Triggers & conditions

A scene is triggered by something – a door opening or closing, motion, temperature, position of the sun, your smart phone, whatever. But that’s just a small part of the story – 90% of the scenes will also need one or more conditions that need to be met for the scene to be allowed to execute. Think of cases like lowering a roller shutter while the window is open – a temperature triggers the roller shutter going down, but when the (outward opening) window is open, you don’t want that to happen of course.

Or take the sunset scene for example; it will probably turn on some lights for you (front- and back door, garden), make a couple of roller shutters go down and do some more stuff you’d otherwise do manually. Using the calculated sunset time as a trigger is not good enough, cause that won’t take into account whether it’s cloudy or not (and therefore the brightness outside & inside). OTOH, what if there’s a heavy storm with lots of dark clouds  in the middle of the day? You don’t really want the sunset scene to execute either; so just a simple light sensor won’t suffice either.

Only when the measured light outside dropping below a certain minimal value and the current time being within a certain margin of the calculated sunset must allow a scene to be executed – for that, conditions come to the rescue. This is what  a sunset rule could look like:

rule sunset {
    when {
        m1: Message m1.t == 'mbtxls1/light' && m1.droppedBelow(170);
        c : Clock;
    } then {
      if(c.hoursIsBetween(15,22)) {
       ...
       ...
       ...
      }
    }

That’s better; now the scene will only be executed when it’s getting dark and time is between 15:00 and 22:59.

Even better would be this:

rule sunset {
    when {
        m1: Message m1.t == 'mbtxls1/light' && m1.p < 170;
        c : Clock;
    } then {
      if ((abs(sunset - c) < 1800) | (c - sunset > 1800)){
       ...
       ...
       ...
      }
    }

With this rule, the sunset scene would only be executed when dusk set in and:

  • the current time is within half an hour (1800 seconds) of the calculated sunset,
  • or
  • the calculated sunset is more than half an hour ago.

That should do the trick 😉

Creating scenes that will always do the right thing is probably the hardest thing to do, cause every time I think of a new scene which should automate things for us, the first thing I realize is that I should have more sensors to make it really work – always when it should, but never when it shouldn’t; that last part is the hardest and most important, cause there’s nothing more irritating then scenes getting in your way, for example switching off lights when you don’t want that to happen…

Sticking to the facts

Homa retracts a fact (if you’ve come this far reading and don’t know what this means, start reading the nools documentation now) after it has triggered a rule; but that’s not that handy actually, cause you might wanna use that same fact (e.g. that door sensor) as a condition as well – in that case, retracting will break things. So instead of doing

  when {
    m1: Message m1.t == 'testsensor/status' && m1.p == 'open';
  } then {
    forget(m1);
    ...
  }

it’s better to do it this way:

  when {
    m1: Message m1.t == 'testsensor/status' && m1.changedTo('open');
  } then {
    unchange(m1);
    ...
  }

This will no longer retract (forget) the fact, but just disable it to trigger more than once (unchange) and keep the fact available to be used in other scenes as a condition.

One small disadvantage (the way it’s working now) is that it’s still not possible to define multiple rules with the same trigger(s) – after triggering the first fact, the fact is being modified to not trigger again, but that’s being done while more rules are waiting to be evaluated; I’ll have to come up with some sort of solution for that.

If you want to see how powerful nools really is, have a look at these (non-Home Automation related) examples – they show how great nools really is; have fun!

 Phew, it was really hard to finish a post with all that soccer on TV 😉
Tagged , , . Bookmark the permalink.

3 Responses to Making a scene

  1. Marcel says:

    Hi Robert,

    Another great post that comes exactly on time. After reading up on home automation and planning to make my own (upcoming) home smarter, I recently decided to go the MQTT way in combination with Node.js on an RPi, possibly creating a Domoticz driver to handle some of the ZWave devices (for now).

    I was wondering: in order to separate the trigger from the individual commands, could you send out an MQTT message (e.g. /event/scene/sunset/activate) and react to that message in a separate rule? This rule could then send out an MQTT /event/shutter/close message which would subsequently check whether it’s safe to close the shutter. Of course, this multi-event mechanism would mean more facts to evaluate in nools, but I’m not sure now noticable this would be time-wise.

    Also, instead of nools reacting to every ‘Message’, could you preprocess the message into another type (e.g. scene, switch, group, …) and have nools react to those? Would that limit the nools load?

    As you can see, I’m full of questions (I have more…). Keep these blog posts coming! Thanks.

    Marcel

    Tip: The else statment in your second example states ON when it should be OFF. Too much football, perhaps? 😉

    • Another great post that comes exactly on time.

      Thanks, I hope it’ll be a good springboard for creating your own scene engine!

      I was wondering …

      Yes that’s possible; that’s what my execute() does actually – sending an MQTT message 😉

      …but I’m not sure how noticable this would be time-wise.

      Maybe it will be measurable, but I doubt if it will be noticable.

      … Would that limit the nools load?

      The load nools produces mostly depends on the number of session.match()-es that will be performed.
      So if you’d like to preprocess Messages and filter them, yes that would matter.
      If you’d want to ‘transform’ Messages but still feed them all to nools, it wouldn’t matter much.

      As you can see, I’m full of questions (I have more…). Keep these blog posts coming! Thanks.

      Questions are good, just keep ’em coming 😉

      The else statement should be OFF. Too much football, perhaps? 😉

      Yep… thanks, fixed.

  2. Marcel says:

    Hi Robert,

    Back again. 😉

    Tried Domoticz with a node-red “rule engine”, hoping the GUI aspect would make for a higher usability than using a ‘coding’ method, like using a rules engine. However, implementing rules in node-red is not as trivial/intuitive as I had hoped.

    Hence, I’m back to experiment with a rules engine (like nools). I’m having some difficulty wrapping my head around rete rule engines, but I think I’m getting the hang of it. Hmmm, perhaps a node-red/nools combo could work… 😉

    I was looking at the homA code you were referring to, but could not find an implementation for the ‘unchange’ function, that you mention using. Probably, this is one of the modifications that you mention making. Can you share your implementation?

    I’m also curious as to whether you encounter ‘problems’ with unchanging facts; e.g. in cases where there are multiple rules that fire upon one fact changing and the first rule to fire unchanging the fact.

    So, out of curiosity, is your HA system still running with the nools engine?

Leave a Reply

Your email address will not be published. Required fields are marked *