Some JS Scripting UI Rules Examples

How do you know the timer isn’t rescheduled? If the door is closed when the timer runs it’s not going to even try to reschedule the timer. And the logging indicates that it clearly things that the item’s state is not OPEN and when it logs out it’s reading as ‘CLOSED’.

So you’ve really got just one problem that needs to be solved, but the solution might not be in this code at all. Why is the Item’s state reading as not being ‘OPEN’ when the timer goes off?

If what you are really after is a repeating timer, LoopingTimer is a better choice.

In my log, when I keep the windows OPEN, I have:

2022-02-03 17:57:41.921 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'GF_Bathroom_Finestra' changed from CLOSED to OPEN
2022-02-03 17:57:41.996 [INFO ] [automation.script.open door reminder] - Handling new door state for reminder: GF_Bathroom_Finestra = OPEN
2022-02-03 17:57:42.012 [INFO ] [automation.script.open door reminder] - Creating a reminder timer for GF_Bathroom_Finestra for 1m
2022-02-03 17:58:42.061 [WARN ] [.openhab.automation.script.sendalert] - ALERT: Finestra Bagno has been open for 1m
2022-02-03 17:58:42.076 [INFO ] [automation.script.open door reminder] - Rescheduling timer for Finestra Bagno because it's night
2022-02-03 17:58:42.151 [INFO ] [automation.script.open door reminder] - Creating a new reminder timer for GF_Bathroom_Finestra for 1m

but nothing happens later…

when moves to CLOSED:

2022-02-03 18:01:49.932 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'GF_Bathroom_Finestra' changed from OPEN to CLOSED
2022-02-03 18:01:49.977 [INFO ] [automation.script.open door reminder] - Handling new door state for reminder: GF_Bathroom_Finestra = CLOSED
2022-02-03 18:01:49.983 [INFO ] [automation.script.open door reminder] - Creating a reminder timer for GF_Bathroom_Finestra for 1m
2022-02-03 18:02:49.989 [WARN ] [automation.script.open door reminder] - GF_Bathroom_Finestra open timer expired but the door is CLOSED. Timer should have been cancelled.

Now I take a look at LoopingTimer…

JS Scripting has a limitation where it doesn’t allow threaded operations. Among those operations are the ability for the lambda acting on a timer to cancel itself. This might also extend to rescheduling the timer too.

In my original code I deliberately recreate the Timer instead of rescheduling it. But even so, there can be other things going on in the background that can cause problems which is why I created LoopingTimers in the first place.

All I can really offer is that this rule works for me as posted. I’m unable to reproduce the behavior you are seeing.

Hi again,
I modified the debug line into info into the library, and the strange thing about timerMgr in function reminderGenerator is:

alerting.sendAlert(name + ' has been open for ' + when);
logger.info('Rescheduling timer for ' + name + " because it's night");
timers.check(itemName, when, reminderGenerator(itemName, name, when));

the the log:

2022-02-04 17:47:01.973 [WARN ] [.openhab.automation.script.sendalert] - ALERT: Finestra Bagno has been open for 1m
2022-02-04 17:47:01.997 [INFO ] [automation.script.open door reminder] - Rescheduling timer for Finestra Bagno because it's night
2022-02-04 17:47:02.014 [INFO ] [tomation.script.rules_tools.timermgr] - Timer manager check called for GF_Bathroom_Finestra
2022-02-04 17:47:02.066 [INFO ] [tomation.script.rules_tools.timermgr] - Timer to be set for 2022-02-04T17:48:02.026+01:00[SYSTEM]
2022-02-04 17:47:02.070 [INFO ] [tomation.script.rules_tools.timermgr] - Cancelling timer GF_Bathroom_Finestra
2022-02-04 17:47:02.084 [INFO ] [tomation.script.rules_tools.timermgr] - Cancelling GF_Bathroom_Finestra

it seems to create a new timer and then cancel it.
Any help?

OK, based on those logs what’s happening is that check is called. The second log “Timer to be set for…” happens after what ever is passed as when is converted to a ZonedDateTime. The timer isn’t created yet.

Next it checks to see if there is already a timer associated with this key. Based on these logs, there is infact a timer already associated with this key.

Then it checks to see if the reschedule flag was passed in. Either reschedule was not passed in or false was passed in so it cancels the timer.

The root problem is that you’ve not told it to rechedule because you’ve not passed in the reschedule flag with true. Make sure to pass true as the fourth argument when calling check.

Note that when you add the TimerMgr to the cache, that same TimerMgr gets reused even if you save the file. So it’s possible that you have old versions of your timer and old versions of your timer lambdas still living in the cache. You can clear out the old TimerMgr by calling cache.put(key, null) from somewhere with key being the same as used in your rule. The cache is shared by all rules so you could do this from -Scratchpad- if you want.

I don’t know that I’ve ever really tested the repeated alerts part of the code since rewriting it in ECMAScript 2021 so it’s possible my original has a bug.

I’ve added the reschedule flag when calling the function “timer.check” and from logs, it reschedule the timer but after it went deleted.
I’m taking a look at the timerMgr.js and I see the part under function _notFlapping seems to the reschedule command goes into this:

  _notFlapping(key) {
   this.logger.info("Creating expire function"); //debug
    return function(context) {
      context.logger.debug('Timer expired for {}', key);
      if(key in context.timers && 'notFlapping' in context.timers[key]){
        context.logger.info('Calling expired function {}', context.timers[key]['notFlapping']);
        context.timers[key]['notFlapping']();
      }
      if(key in context.timers) {
        context.logger.info('Deleting the epired timer');
	delete context.timers[key];
	}
 }

but the context delete is didn’t take care about the timer rescheduled, so it delete it without any further check.
Now i’m trying to see if I can modify the function and pass the reschedule flag so I can bypass in case of rescheduling active… But I’m not a real programmer, so I see it very harder.

How can I delete it? I’m using your script with same values but if I try to do in scratchpad

cache.put(open_windows_reminder_tm, null);

I receive Script execution of rule with UID ‘scratchpad’ failed: ReferenceError: “cache” is not defined in at line number 2

That’s the function that gets called when the timer goes off. It calls the function you passed in as the third argument and does some cleanup.

The key is a string.

Also make sure the -Scratchpad- is using ECMAScript 2021, not 5.1 or Rules DSL.

I made a modification sending the flag “rescheduled” and seems working… I’ll send if someone interested:

_notFlapping(key,reschedule) {
    this.logger.debug("Creating expire function"); //debug
    return function(context) {
      context.logger.debug('Timer expired for {}', key);
      if(key in context.timers && 'notFlapping' in context.timers[key]){
        context.logger.debug('Calling expired function {}', context.timers[key]['notFlapping']);
        context.timers[key]['notFlapping']();
      }
      if(key in context.timers) {
        if(!reschedule) {
			context.logger.debug('Deleting the epired timer');
			delete context.timers[key];
		}
      }
    }
  }

 

  check(key, when, func, reschedule, flappingFunc) {
    this.logger.debug('Timer manager check called for {}', key);

    var timeout = timeUtils.toDateTime(when);
    this.logger.debug('Timer to be set for ' + timeout.toString());

    // timer exists
    if(key in this.timers){
      if(reschedule) {
        this.logger.debug("Rescheduling timer {} for {}", key, timeout.toString());
        this.timers[key]['timer'].reschedule(timeout); 
      }
      else  {
        this.logger.debug('Cancelling timer {}', key);
        this.cancel(key);
      }
      if(flappingFunc) {
        this.logger.debug('Running flapping function for {}', key);
        flappingFunc();
      }
    }

    // timer doesn't already exist, create a new one
    else {
      this.logger.debug('Creating timer for {}', key);
      var timer = actions.ScriptExecution.createTimerWithArgument(timeout, 
                                                                  this,
                                                                  this._notFlapping(key,reschedule));
      this.timers[key] = { 'timer': timer,
                           'flapping': flappingFunc,
                           'notFlapping': (func) ? func : this._noop };
      this.logger.debug('Timer created for {}', key);
    }
  }

I’ve found the problem while you’re replying to me… Many thanks.

Hello,

I have please a generell question:
I have many rules since openHAB 1.7x and within openHAB 3.02 I saw that I’m using the rule engine application/vnd.openhab.dsl.rule.
Do I have to migrate all my rules to another rule engine?
Or will this rule engine application/vnd.openhab.dsl.rule also be supported in the future?

THX for your answer.

Here is an sample rule I’m using

var java.util.concurrent.locks.ReentrantLock finishLock1 = new java.util.concurrent.locks.ReentrantLock()

val Number Spueler_OFF = 0
val Number Spueler_STANDBY = 1
val Number Spueler_ACTIVE = 2
val Number Spueler_FINISHED = 3

rule “Spuelmaschine_State”
when
Item Spuelmaschine_Power changed
then

if (Spuelmaschine_Power.state < 7)  
	{
	if (Spuelmaschine_OpState.state == Spueler_OFF) 
		{
		Spuelmaschine_OpState.postUpdate(Spueler_STANDBY)
		Spuelmaschine_Fertig_Status.postUpdate(5)
		}
	}		
	
[...]	

executeCommandLine(Duration.ofSeconds(10),"/opt/openhab/conf/scripts/lock_SpuelerLock.sh")

var String SpuelerOKExecute=executeCommandLine(Duration.ofSeconds(10),"/opt/openhab/conf/scripts/SpuelerLock.sh")

if (SpuelerOKExecute=="OK")  //richtige Syntax weil echo "OK" returned wird
	{				
	if (Spuelmaschine_OpState.state == 2)
			{								
			if (Spuelmaschine_Power.state < 23) 
				{
				Spuelmaschine_Fertig_Status.postUpdate(1)
				Thread::sleep(100)
				[...]
				
			}
			
	executeCommandLine("/opt/openhab/conf/scripts/rmdir_exists_SpuelerLock.sh")			

	}
	else
    {
	logInfo("Log","ELSEEnde SpuelerLock")
	}			

end
THX

Klaus

Not unless you want to.

thx

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.