What’s the state of Strom_Waschmaschine at all of these times? How is this rule triggered, on changes to Strom_Waschmaschine? I suspect what’s happening is Strom_Waschmaschine is changing but remaining under 0.300. But you create a new timer no matter what Strom_Waschmaschine is if the previous timer has terminated.
if(items.Strom_Waschmaschine.quantityState.greaterThan(Quantity('300 W')) {
//create timer if not yet done
if (cache.private.exists('WMTimer') === false || cache.private.get('WMTimer').hasTerminated()) {
cache.private.put('WMTimer', actions.ScriptExecution.createTimer('WMTimer', now.plusMinutes(12), switch_off, ));
console.info('WaschOFF timer created');
}
//reschedule if timer active and washingmachine still running
else if (cache.private.get('WMTimer').isActive()){
cache.private.get('WMTimer').reschedule(now.plusMinutes(6));
console.info('WaschOFF timer rescheduled');
}
}
else {
console.info('WaschOFF timer NOT rescheduled');
}
Other notes:
- Why are you using the shared cache? Is there another rule that needs access to this Timer? If not, use the private cache.
- Shouldn’t it be rescheduling when it’s using more that
0.300 ?
The code as written but it’s a little overly complicated.
First of all, if I were to write this I’d use openHAB Rules Tools Announcements LoopingTimer.
var {LoopingTimer} = require('openhab_rules_tools');
function switch_off () {
if(items.getItem("Strom_Waschmaschine").quantityState.lessThan(Quantity('0.300 W'))) {
console.info('WaschOFF timer run out Washingmachine switched off now');
items.Keller_WaschmaschineOnOff.sendCommandIfDifferent('OFF');
return null;
}
else {
return 'PT6M';
}
}
if(items.getItem("Strom_Waschmaschine").quantityState.lessThan(Quantity('300 W'))) {
var timer = cache.private.get('WMTimer', () => LoopingTimer());
if(timer.hasTerminated()) timer.loop(switch_off, time.toZDT('PT12M'));
}
How it works:
- Import
LoopingTimer - We use the private cache except where we need to share a value between rules
- The
cache.x.get()method will take an optional second argument that is a function that gets called to populate the property in the cache. So we use this to create theLoopingTimerif it doesn’t already exist. Then we callloop()only if it hasn’t terminated. - The way
LoopingTimerworks is it reschedules itself based on the return value of the timer function. Whennullis returned the timer exits. When a time is returned (can be anything supported bytime.toZDT()it reschedules itself at that time. - We use
time.toZDT()and ISO8601 Duration strings to define the time for the Timer to run.
All the timer management stuff is handled by LoopingTimer.
But if you did it inline the code could be simpler. If you schedule the timer initially for the same amount as you reschedule it the code can reduce to:
function switch_off () {
console.info('WaschOFF timer run out Washingmachine switched off now');
items.getItem('Keller_WaschmaschineOnOff').sendCommandIfDifferent('OFF');
}
var TIMER_NAME = 'WMTimer';
if((items.getItem("Strom_Waschmaschine").quantityState.lessThan(Quantity('300 W'))) {
const timer = cache.private.get(TIMER_NAME);
if(timer === null || timer.hasTerminated()) {
cache.private.put(TIMER_NAME, actions.ScriptExecution.createTimer(TIMER_NAME, time.toZDT('PT12M'), switch_off);
console.info('WaschOFF timer created');
}
else {
timer.reschedule(time.toZDT('PT6'));
console.info('WaschOFF timer rescheduled');
}
}
else {
console.info('WaschOFF timer NOT rescheduled');
}
While it’s not really less lines of code, it’s a bit shorter because you are not constantly needing to reference the cache over and over sometimes multiple times on the same line of code. Also, because we use it several times, it’s a good idea to set the key for the timer in the cache to a variable to avoid typos.