Understanding Timer in Rules ... for multiple key detection from LCN for HUE-lights

Hello community,

my current task is to realize a rule-template for one LCN-key and a HUE-light/group. The key should offer the with the following functions:

  1. pressed short → toggle light On/Off
  2. pressed long → dim light Up or Down
  3. release long pressed → stop dimming
  4. pressed short + long → change the color of a HUE lamp
  5. double pressed short → now specified yet

LCN offers for one switch the values HIT (short) / MAKE (long) / BREAK (release). I used this to set some virtual releais with the LCN software:

  • HIT(short) → toggle Relais7
  • MAKE (long) → set Relais8 to ON
  • BREAK(release) → set Relais8 to OFF

The idear is to measure the time between to key hits. If this time is within an defind threshold (doublePressedTime) the second action will be performed (4. or 5.) otherwise the normal action (1.-3.) will be performed. So the first detection of a short pressed key (Relais7 changes it’s state) should start timer. If the timer terminates the normal action for this key (1. pressed short) should take action. But If a second trigger occurs, while the timer is running, the timer should be canceled and the second trigger action while be performed (4. or 5.).

Okay that’s the problem and the idear to solve this. This leads to the following code … without the actions to control the HUE lamp.

var keyPressedShort         = "Module11KGStef_Relais7";
var keyPressedLongOrRelease = "Module11KGStef_Relais8";
var doublePressedTime       = 600 ; // Unit = ms

var triggerTime = Java.type('java.time.Instant') ;
var triggerItemName;
var lastActionTime, currentActionTime, deltaActionTime ;
var actionState

var scriptExecution = Java.type('org.openhab.core.model.script.actions.ScriptExecution');
var zdt = Java.type('java.time.ZonedDateTime');

if (typeof this.timers === 'undefined') {
  this.timers = [];
}

// estimate the time between to trigger-events
if (lastActionTime == NaN) lastActionTime = 0;
currentActionTime = triggerTime.now().toEpochMilli() ;
deltaActionTime = currentActionTime - lastActionTime ;
lastActionTime = currentActionTime ;

if ((triggerItemName = event.itemName) == keyPressedShort) {
  if (deltaActionTime < doublePressedTime){
    // Timer is running/defined --> second time pressed within the doublePressedTime
    if (typeof this.timers['KeyTimer'] !== 'undefined') {
      this.timers['KeyTimer'].cancel();
      this.timers['KeyTimer'] = undefined;
      actionState = 'ShortTwice' ;
    }
  } else {
    // Not running Timer --> first hit ... so start a timer for doublePressedTime
    if (typeof this.timers['KeyTimer'] === 'undefined' || (this.timers['KeyTimer'].hasTerminated())) {
      actionState = 'FirstAction';
      this.timers['KeyTimer'] = scriptExecution.createTimer(zdt.now().plusNanos(doublePressedTime*1000), function () {
        if (typeof this.timers['KeyTimer'] !== 'undefined') {
          this.timers['KeyTimer'].cancel();
          this.timers['KeyTimer'] = undefined;
          actionState = 'ShortOnce' ;
          print (actionState) ;
        }
      })
    } else {
      this.timers['KeyTimer'].cancel();
      this.timers['KeyTimer'] = undefined;
      actionState = 'TimerProblems' ;
    }
  }
} else {
  // keyPressedLongOrRelease was hit ...
  if ((typeof this.timers['KeyTimer'] !== 'undefined') && ((this.timers['KeyTimer'].isRunning()) || this.timers['KeyTimer'].hasTerminated())) {
    this.timers['KeyTimer'].cancel();
    this.timers['KeyTimer'] = undefined;
    actionState = 'ShortPlus' ;
  }
  if (itemRegistry.getItem(keyPressedLongOrRelease).getState() == 'ON') {
    if (actionState == 'ShortPlus') {
      actionState = 'ShortPlusLong' ;
    } else {
      actionState = 'Long' ;
    }    
  } else {
    actionState = 'Released' ;
  }
};
print ('-----------------------------')
print (triggerItemName);
print(deltaActionTime) ;
print(actionState) ;

From my point of view the js script above should to what I want … but I can’t get a “ShortPlusLong” or a “ShortTwice” actionState alltough I pressed the key within the “doublePressedTime” a second time.

This relates to some questions:

  • Is there a possibility to add MilliSecond directly at “createTimer(zdt.now().plusxxx” (I can’t find a hint for that and NanoSeconds will realy make no sence)?
  • What exactly happends, when I start a timer?
    ** Will the script stop and will be continuied when the timer expires?
    ** Will only the code within scriptExecution.createTimer ( … ) backets executed, or this code plus the rest of the script?
    ** Will the scripped triggered a second time, while the timer is running (this is what I expect)?
  • Can someone see a logical ot syntaktical mistake within the script?

I would be greate, if someone can help me to fix this behavior.
Thanks an best regards
Stef

It’s the same amount of time, however it is expressed. But -

No. The Timer is created as an independent task to be executed at a future time, and the creating script/rule carries on without waiting.

In your example, the code between function () { .... } braces

Hello @rossko57

thanks for this info. SO from my point of view I can go one with NanoSeconds and don’t have to search further for am MilliSeconds-Statement.

Thanks
Stef

The Timer is created as an independent task to be executed at a future time, and the creating script/rule carries on without waiting.

Okay, understand. So I can check within the lower else clause …

} else {
  // keyPressedLongOrRelease was hit ...
  if ((typeof this.timers['KeyTimer'] !== 'undefined') && ((this.timers['KeyTimer'].isRunning()) || this.timers['KeyTimer'].hasTerminated())) {
    this.timers['KeyTimer'].cancel();
    this.timers['KeyTimer'] = undefined;
    actionState = 'ShortPlus' ;
  }
  if (itemRegistry.getItem(keyPressedLongOrRelease).getState() == 'ON') {
    if (actionState == 'ShortPlus') {
      actionState = 'ShortPlusLong' ;
    } else {
      actionState = 'Long' ;
    }    
  } else {
    actionState = 'Released' ;
  }
};

alternatively to the timer, the ActionState for ‘FirstAction’ to determine wheather a key was hit bevor or not. What happens, if I terminate a timer that is not running? Or with other words … do I have to check very time that a timer is !== undefined, or can I cancel it without a check?

Thanks
Stef

undefined.cancel() will throw an error, undefined does not have a cancel() method, only valid Timer objects will.

@rossko57 wrote

No. The Timer is created as an independent task to be executed at a future time, and the creating script/rule carries on without waiting.

That means I do have to insert within the action performet when the timer has expires all needed declaration, I had within the rule, again, or can I use the objects from the rule?

I think you’ve got to the point of “try it and see”.
When you create a Timer function, it “captures” the current environment to an extent, but there can be surprises.