OH3: Cancel timer not working as intended

  • Platform information:

    • OS: Unraid with Docker
    • Java Runtime Environment: Dont know
    • openHAB version: 3.2.0
  • Issue of the topic:
    I have tried different ways to cancel my timer. But no matter how it is not working as intended.
    I have a light that I wan’t to get a notification from if turned on for more than 30 minutes. If turned off before timeout it should cancel the notification. The rule works “half way” meaning that if the light has been on for more than 30 minutes I get the notification. Also if the light is turned off before timeout the notification is canceled.
    The problem is: If light is first turned on, then turned off, and then turned on again within the 30 minute timespan, the notification is send after 30 minutes from the first time the light is turned on. I wan’t it to cancel the timer the first time the light is turned off.

  • Items configuration related to the issue:

Dimmer Badevaerelse (Created in UI)
Trigger in UI: Item Badevaerelse changed
  • Rules code related to the issue (made in RuleDSL
var Timer BadevaerelseTimer = null
val actions = getActions("pushover", "pushover:pushover-account:XXXXXXXXXX")
  
BadevaerelseTimer?.cancel
	BadevaerelseTimer = createTimer(now.plusSeconds(1800)) [
		if ( Badevaerelse.state > 0 ) {
			actions.sendMessage("Lys Badeværelse","openhab")	
			}
		]

Thank you

Maybe post the full rule, as with the current piece of code the role will not work.

Also have a look at the documentation, where your usecase is already in the example:

Hi Matthias

I’m not sure what you mean. It is the whole rule. It is made in UI in OH3.

The rule is made as above within the script.

configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: Badevaerelse
      previousState: "0"
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >-
        var Timer BadevaerelseTimer = null

        val actions = getActions("pushover", "pushover:pushover-account:XXXXXXXXXX")
          
        BadevaerelseTimer?.cancel
        	BadevaerelseTimer = createTimer(now.plusSeconds(1800)) [
        		if ( Badevaerelse.state > 0 ) {
        			actions.sendMessage("Lys Badeværelse","openhab")	
        			}
        		]
    type: script.ScriptAction

Thank you

Hi Martin,

Timer in a dsl rule will only work if you defined the rule within a file, otherwise your variable / reference to the timer will not exists with the next execution of the rule.

If you want to stay with ui defined rules (what I would recommend) you can use JavaScript instead of dsl as a rule language and also try blockly, what will already produce the correct code for you

Also you need an if/else part in you rule, where depending on your dimmers state you either create our cancel the timer

Have tried with “else if” also, but then the rule doesn’t work at all.

else if ( Badevaerelse.state == 0 ) {
    BadevaerelseTimer?.cancel
      BadevaerelseTimer = null
  }
var Timer BadevaerelseTimer = null
val actions = getActions("pushover", "pushover:pushover-account:XXXXXXXXX")
  
BadevaerelseTimer?.cancel
	BadevaerelseTimer = createTimer(now.plusSeconds(1800)) [
		if ( Badevaerelse.state > 0 ) {
			actions.sendMessage("Lys Badeværelse","openhab")	
			}
		]
        else if ( Badevaerelse.state == 0 ) {
                  BadevaerelseTimer?.cancel
                  BadevaerelseTimer = null
  }

But I’m uncertain about the brackets and also where to put the “else if”

I would take a look at blockly, but it would be new to me.

As mentioned before: as per my knowledge due to limitations of how variables defined, a dsl rule defined via gui can never work with timers add you want.

They with blockly first and if you there are some issues, we will find a solution l.

Have you seen the documentation?

I have now tried different setups, and ended with putting the rule back into .rules file. And now it works again. I have changed from OH2 to OH3 and thought that I could just use UI in combination with RuleDSL the same way as in .rules file in OH2.

The solution for now was to create a .rules file like in OH2:

var Timer BadevaerelseTimer = null
val actions = getActions("pushover", "pushover:pushover-account:XXXXXXXX")

rule "Pushover Lys Badeværelse"
when
	Item Badevaerelse changed
then
	BadevaerelseTimer?.cancel
	BadevaerelseTimer = createTimer(now.plusSeconds(1800)) [
		if ( Badevaerelse.state > 0 ) {
			actions.sendMessage("Lys Badeværelse", "openHAB")
			}
		]
end

But thank you for your help Matthias

The difference between rules file and a dsl rule via GUI is, that in your file the BadevaerelseTimer variable is outside of the rule, but when defining the same rule via GUI the variable is defined inside the rule and therefore is only existing within one single rule execution.

Even you code seems to work as you want, I would still add some if / else condition to it, as right now even when you turn off the light, the timer will start to run (but just not sending a message). You can do something like:

if(newState== 0) {
  BadevaerelseTimer?.cancel()
} else {
  BadevaerelseTimer = createTimer(now.plusSeconds(1800)) [
			actions.sendMessage("Lys Badeværelse", "openHAB")
		]
}

Btw: If you would do it with blockly it would look like this:
image
and would produce the following code that you can also use in a javascript rule created via UI (using standard openHAB notifications and not pushover):

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

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

var zdt = Java.type('java.time.ZonedDateTime');

var notifications = Java.type('org.openhab.io.openhabcloud.NotificationAction');


if (event.itemState == 0) {
  if (typeof this.timers['MyTimer'] !== 'undefined') {
    this.timers['MyTimer'].cancel();
    this.timers['MyTimer'] = undefined;
  }
} else {
  if (typeof this.timers['MyTimer'] === 'undefined' || this.timers['MyTimer'].hasTerminated()) {
    this.timers['MyTimer'] = scriptExecution.createTimer(zdt.now().plusMinutes(30), function () {
      notifications.sendNotification('test@example.org','message');
      })
  }
}