Switch wireless devices reliable through Drools in OpenRemote

This post summarises my experience with having a wireless appliance switched on and off at certain parts of a day. This is a nice example of using Drools timers and internal facts.

Wireless suffers the problem of unreliable communication in a noisy, long distance environment. Even in the same room sometimes the transmission fails, this is my experience with EnOcean, Z-wave and WeMo. Luckily, this problem can be solved in Openremote in a smart way by sensing back the switch state and deciding if retransmission is necessary. This is an unique characteristics of Openremote architecture not commonly found in other home automation controllers. The algorithm presented below addresses this problem and is in my humble opinion a must have for everyone who has a need of reliable wireless switching.

First I’ve declared a fact which will be used by the switching algorithm state machine.

declare SocketState
  @role(event)
  on : int
  // -1: idle
  //  0: switching off
  //  1: switching on 
end

rule "Init SocketState"
when
then
  insert(new SocketState(-1));
end

Next, the rule for initiating switching on the appliance every Friday at 6:10 AM:

rule "Lamp on"
  timer(cron: 0 10 6 ? * fri) 
when
  Event(source=="SocketSwitching1.STATE", value=="off")
  $s: SocketState(on!=1)
then
  $s.setOn(1);
  update($s);
end

Note that the rule is executed only when socket is not yet turned on and not in switching StcketState. All it does is setting the SocketState fact into switching on state and pass it to other rules with update() statement.

Now, we will be sending wireless telegrams to the switching socket every 5 seconds as long as its state is off. Usually the light go on immediately, however when it is noisy few retransmissions are necessary.

rule "Lamp on execute"
  timer(int: 0s 5s) // repeat every 5 seconds
when
  $s: SocketState(on==1)
  Event(source=="SocketSwitching1.STATE", value=="off")
then
  execute.command("SocketSwitching1.ON"); // Light ON
end

Finally when the socket state is changed to on it is marked by SocketState fact:

rule "Lamp on executed"
when
  $s: SocketState(on==1)
  Event(source=="SocketSwitching1.STATE", value=="on")
then
  $s.setOn(-1);
  update($s);
end

The same algorithm is used for switching the socket off on 6:25 AM.

rule "Lamp off"
  timer(cron: 0 25 6 ? * fri) 
when
  Event(source=="SocketSwitching1.STATE", value=="on")
  $s: SocketState(on!=0)
then
  $s.setOn(0);
  update($s);  
end

rule "Lamp off execute"
  timer(int: 0s 5s)
when
  $s: SocketState(on==0)
  Event(source=="SocketSwitching1.STATE", value=="on")
then
  execute.command("SocketSwitching1.OFF"); // Light OFF
end

rule "Lamp off executed"
when
  $s: SocketState(on==0)
  Event(source=="SocketSwitching1.STATE", value=="off")
then
  $s.setOn(-1);
  update($s);
end

Because ON and OFF states sent by socket can easily be missed in noisy environment an extra bit of robustness is achieved by declaring the ON state in case when non-zero power measurement message arrives:

rule "SocketSwitching1 state ON through power"
// Set state ON when power is not 0
when
  Event(source=="SocketSwitching1.POWER", value!="0")
  Event(source=="SocketSwitching1.STATE", value!="on")
then
  execute.command("SocketSwitching1.ON");
end

The last touch is to retract the last power measurement fact so the repeating same values are processed by the rules engine. Without this rule when power measurement value stays the same the “SocketSwitching1 state ON through power” rule will be triggered only once (this is due to implementation of Drools engine within Openremote).

rule "SocketSwitching1 retract last power measurement"
salience -100 // Because of retracting of the event
when
  $s : Event(source=="SocketSwitching1.POWER",$v:value)
then
  retract($s);
end

Integrating Belkin’s WeMo switch and motion with Openremote

WeMo-Sw+MoBelkin offers WiFi enabled power switch and motion sensor for less than $80 http://www.belkin.com/. They connect with home WiFi network and are visible on LAN. Furthermore, they use the UPnP (Universal Plug and Play) protocol which details are easy to find with a Google search. At this moment UPnP protocol is not directly available within Openremote therefore for this integration I’ve used the Shell execution protocol to run curl in order to turn the switch position and also to sensor motion and switch state. It is possible to switch the socket through generic TCP/IP protocol, however reading back a status is unreliable. This is due to the fact that the reply is frequently split into 2 TCP/IP messages, the first part with an HTTP UPnP header and the second one with the message body itself. The TCP/IP implementation in Openremote receives only the first packet frequently missing the second part with an actual status. Nevertheless, it works good with the Shell execution protocol with the only disadvantage that a caution must be taken when porting this code on a different platform. For this integration example I’ve used the professional OpenRemote eBox which is running the Linux Voyage OS. I assume that WeMo switches are already connected with the home WiFi LAN which is achieved by following the standard installation procedure as provided by Belkin. Next I’ve used Android Fing app to scan my network and discover WeMo IP addresses which will be needed when configuring Openremote commands. Apparently WeMo devices’ ethernet address starts with EC:1A:59 and this information simplifies locating them within a crowded LAN environment like mine is. After discovering the IP addresses of WeMo devices please use the following steps to use them in the Openremote designer.

  1. create following files on the OR controller:
    root@orb:/root/wemo# ls -l
    -rw-r--r-- 1 root root 272 Feb  4 13:17 get.xml
    -rw-r--r-- 1 root root 300 Feb  4 13:17 off.xml
    -rw-r--r-- 1 root root 301 Feb  4 13:17 on.xml
    -rwxr-xr-x 1 root root 737 Feb  4 17:39 post.sh
    -rwxr-xr-x 1 root root 737 Feb  4 19:58 post_get.sh
    
    root@orb:~/wemo# cat get.xml
    
    
    root@orb:~/wemo# cat off.xml 
    0
    
    root@orb:~/wemo# cat on.xml 
    1
    
    root@orb:~/wemo# cat post.sh 
    #!/bin/sh
    curl -0 -A '' -v -X POST -H 'Accept: ' -H 'Content-type: text/xml; charset="utf-8"' -H "SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"" --data @`dirname $0`/$3.xml http://$1:$2/upnp/control/basicevent1 
    
    root@orb:~/wemo# cat post_get.sh 
    #!/bin/sh
    curl -0 -A '' -v -X POST -H 'Accept: ' -H 'Content-type: text/xml; charset="utf-8"' -H "SOAPACTION: \"urn:Belkin:service:basicevent:1#GetBinaryState\"" --data @`dirname $0`/$3.xml http://$1:$2/upnp/control/basicevent1 
    

    For my integration I’ve put these files in /root/wemo directory on my eBox. For doing this you need to use ssh login. Of course you can place these files in any desired location as long as you adjust parameters of the Shell execution command.

  2. Create following openremote commands in the designer:
    WeMo.mo.shell.get

    WeMo.sw.shell.get

    WeMo.sw.shell.off

    WeMo.sw.shell.on

    As you can see my WeMo devices are at IP addresses 192.168.1.10 (switch) and 192.168.1.11 (motion).

  3. Crate 2 sensors: WeMo.mo (command WeMo.mo.shell.get, type Custom) and WeMo.sw (command WeMo.sw.shell.get, type Custom)

Now WeMo devices should be available in the Openremote designer.