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