Openhab 4.2.0 and timers in Javascript

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 the LoopingTimer if it doesn’t already exist. Then we call loop() only if it hasn’t terminated.
  • The way LoopingTimer works is it reschedules itself based on the return value of the timer function. When null is returned the timer exits. When a time is returned (can be anything supported by time.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.