OH3 timer query (action only if 10s expires and condition is still true)

  • Platform information:
    • Hardware: Raspberry pi 4
    • OS: raspbian
    • Java Runtime Environment: 8
    • openHAB version: 3.1
  • Issue of the topic:

Good afternoon. I’m a complete newbie to openhab and I’m strugging to get my first project off the ground. My goal is to get my alexa to sound an alarm/say something when a contact sensor attached to my front door has detected that the door has been left open for 10 seconds. I’m using a Tuya contact sensor which I have correctly configured with openHAB thanks to the users here, and I’ve also correctly configured alexa using the appropriate channels + links.

My issue is when creating the rule I can’t make the appropriate timer work. The contact sensor is an item called Tuya_test, where an ON state means the door is open. This is the config of my rule so far using the GUI:

triggers:
  - id: "1"
    configuration:
      itemName: Tuya_Test
      state: ON
      previousState: OFF
    type: core.ItemStateChangeTrigger
conditions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: |-
        var Timer timer = null

        rule "timer"
        when
            Item Tuya_Test.state === ON //Use whatever trigger
        then
            (if timer === null) {
                timer = createTimer(now.plusSeconds(10)), [ |
                    if (Tuya_Test.state === ON) {
                      output = 1
                    } //Do the timer stuff
                    timer = null // cancel the timer
                ])
            }
        end
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: ThomassEchoDot_Speak
      command: The door is open
    type: core.ItemCommandAction

If I remove the conditions section then alexa talks whenever the door is opened, but crucially I want the condition to test that the door has been opened continuously for 10 seconds and only then to do the action. I’m sorry if this has already been covered somewhere but I’ve been trying to make a sense of related posts with no luck for the past few days.

Thanks for your help in advance!!

You can’t copy-paste a complete files based rule into the script section of a GUI entered rule. Choose one method of working, for sanity.

Are you saying that for this section of code it needs to be reduced to just the ‘then’ part? ie from

to

        then
            (if timer === null) {
                timer = createTimer(now.plusSeconds(10)), [ |
                    if (Tuya_Test.state === ON) {
                      output = 1
                    } //Do the timer stuff
                    timer = null // cancel the timer
                ])
            }
        end
    type: script.ScriptCondition

Less than that, if you have chosen GUI rule entry throw away the then-end as well, the GUI takes care of the structure.

But beware; that timer global variable isn’t going to work in a GUI rule. You’ve got nowhere “outside of the rule” to define it.

Ah I see okay thank you.

In that case is it possible to create a 10s timer if I’m just using the GUI alone? Or do I need to create a global variable to do what I want?

See

I’m happy to try either approach (.rules or JS) but I’m lost as to where to start. Is there a good example of a .rules file I could use as a template perhaps?

You copy-pasted such an example (file based) into the UI rule in your first post?

Thanks for your help rossko - I’ve managed to come up with a javascript solution. In case its helpful for anyone else who stumbles on this thread I’ll post my (noob-friendly) solution, i.e., the rule I created using the openHAB GUI (note the proximity door sensor and alexa were already set up with their required channels - don’t forget to check advanced to see them all!).

The Rule will detect then the proximity door sensor is triggered to “ON” (and thus open), and if this state persists for 10 seconds, a function defined in this rule will be triggered that sends various commands to my Alexa Echo (change to max volume, say a predefined sentence). This will then repeat until the door is closed again/the proximity sensor is seen to be ‘OFF’.

The below code was put into an ECMAscript (javascript) under the ‘Then’ section of the Rules UI

var mytimer = (mytimer === undefined) ? null : mytimer; // this defines the timer variable
var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
var ZonedDateTime = Java.type("java.time.ZonedDateTime"); // two required javascript functions to be loaded
var event
myObj = ir.getItem(event.itemName) // fetches the event relevant to the rule (in my case the proximity door sensor set in the 'When' section of the UI.

function warnOpenDoor() {
  myObj = ir.getItem(event.itemName)
  //events.sendCommand("ThomassEchoDot_Speak", myObj.getState()) //debug
  //events.sendCommand("ThomassEchoDot_Speak", "test speaking") // debug
  var EchoDotVolume = ir.getItem("ThomassEchoDot_Volume")
  //events.sendCommand("ThomassEchoDot_AlarmSound","ECHO:system_alerts_alarming_03")
  while (myObj.getState() == ON) {
    events.sendCommand("ThomassEchoDot_Volume", "100")
    //events.sendCommand("ThomassEchoDot_AlarmSound","ECHO:system_alerts_rhythmic_02")
    events.sendCommand("ThomassEchoDot_Speak", "Warning: Front Door Open. Beware of the cats.")
    events.sendCommand("ThomassEchoDot_Volume", EchoDotVolume.state)
    java.lang.Thread.sleep(10000)
  }
  mytimer = null
}

var now = ZonedDateTime.now()
if (myObj.getState() == ON) {
  //events.sendCommand("ThomassEchoDot_Speak", "Open") // debug
  
  mytimer = ScriptExecution.createTimer(now.plusSeconds(10), warnOpenDoor)
}
else {
  //events.sendCommand("ThomassEchoDot_Speak", "Close") // debug
  if (mytimer != undefined) {
    mytimer.cancel();
    mytimer = undefined;
  }

}

That’s for posting your code. The more examples we have the better. I’ve a few comments though which might make the code posted even better.

  • When posting a rule to the forum, consider whether it makes sense to post just the Script Action or to post the entire rule. You can access the code for the whole rule from the Code tab of the Rule Designer page.

  • JavaScript is a bit loosey-goosey in what it will allow you to get away with. But in general I’ve seen it recommended to always close your lines of code with a ;.

  • var event: what is this line doing? event should already exist in the rule as one of the variables that gets inserted into the context when the rule is triggered. You don’t need to redeclare it.

  • In the function warnOpenDoor, you redefine and populate myObj. myObj should already be available in that function but if it’s not, pass it as an argument to the function. Avoid duplicate lines of code.

function warnOpenDoor(myObj) {

You can use createTimerWithArgument or you can use a function generator which would look like:

`function warnOpenDoorGenerator(myObj){
return function(){
// your existing code goes here
}
}

  • This is more of a style thing, but I prefer to use the items dict to get an Item’s state. It makes for shorter and clearer code over all. Also, the items dict gets updated as the Items change states. I’m not sure the Item Object you’ve pulled from the ItemRegistry get’s updated the same way.
var EchoDotVolume = items["ThomassEchoDot_Volume"];
function warnOpenDoor() {
  while(items[event.itemName] == ON) {
    ...
    events.sendCommand("ThomassEchoDot_Volume", EchoDotVolume);
    java.lang.Thread.sleep(10000);
  }
  myTimer = null;
}
  • Use the identity comparison when comparing to undefined.

    if(mytimer !== undefined){