Timer script Not Cancelling if state changes

@rlkoshak it seems like my cut/paste is removing the first line when I paste for some reason. I noticed that it also didn’t paste the first of the two lines from the log file. That first line for the logger was supposed to be there.

OK so thanks for the explanation of what I should log and why, but I’m unclear on exactly how to do that. The examples I see seem to all be different. The first line after importing the logger is:
var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
So do I do this?
logger.info(ScriptExecution = Java.type);

Ah, I just figured out that the closing `'s can’t be on the next line.

var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
logger.info("My rule has started running...");

...
logger.info("this.timer = " + this.timer);

@rossko57

“Thing status is not a reliable guide to real availability, nor is it intended to be.”

If Thing status is not intended to be a reliable guide for devices that have gone offline, what is?

That depends on the device and the local interpretation of ‘online’. How would you expect a Thing representing a sleeping battery smoke alarm to look? If we smash it with a hammer, what would you expect then?

Treat it as telling you about the pathway to a device. Have I got all the resources needed to await a communication from the device? Then its ONLINE.

Good question. For us it’s pretty much all zwave. I would want to know if it fails polling.

zwave doesn’t really poll, the devices report autonomously. The last thing you want is twice as much mesh traffic, implied by active poling.

You can detect missing reports if you know when to expect them. Generally people will look for missing periodic battery reports or similar.
This doesn’t really have much to do with Things.

You might review this thread -

That thread explains a lot, thanks. Not the answer I wanted though. I’m coming from Vera where we would get pretty reliable notifications for things offline.

I’m more concerned with grid-powered devices, though we DO have some leak sensors which would certainly be good to know if were offline.

It seems like failed polling would work but I just tested that & I’m not getting anything. I also tried sending a REFRESH command, which I saw in another post put that had no effect. Strange, you’d think there would be some built-in status report for zwave devices which would do this. That’s what I thought polling would do. Poll, no answer, alert!

Polling is undesirable in zwave technology. It uses twice as many transactions as autonomous reporting, and it’s useless for battery devices which are an essential part of zwave.

Don’t like it, use wired or other technology.

Or live with it, learn how to predict and/or control those unsolicited device reports. Detecting missing updates at the openHAB end is then fairly trivial in comparison.

OK back on logging & the timer:

@rlkoshak I have some logging working (small miracle) but I’m stuck about 1/2 way down the list you provided of what to log. I don’t know how to get the value of this.timer inside of runme. Right now the logs I have just show whatever text I inserted like this:

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Scratch");

var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
logger.info("Thing Offline Alert Rule Started");

var ZonedDateTime = Java.type("java.time.ZonedDateTime");
 
var now = ZonedDateTime.now();

var runme = function(){
logger.info("Function");
  
var Actions = Java.type("org.openhab.core.model.script.actions.Things");
var mailActions = Actions.getActions("mail","mail:smtp:SMTP_Server");
mailActions.sendHtmlMail("skye@bpsgreenhomes.com", "Thing Offline", "A device has gone offline. Please contact BPS at monitors@bpsgreenhomes.com or 910-470-8203");

    this.timer = undefined; // reset the timer back to undefined
}
logger.info("timer = undefined");

if(this.timer === undefined) {
    this.timer = ScriptExecution.createTimer(now.plusSeconds(30), runme);
}
logger.info("Timer Created");

else {
    this.timer.reschedule(now.plusSeconds(30));
}

Append this.time to the string

logger.info("this.timer = " + this.timer);

The output doesn’t really matter. What matters is whether it is undefined or if it’s different from this.timer outside of the runme function.

I get good logs that show teh script running & email sent after the timer ends, until I add the logger.info line after the If line. Then I get this error:

2021-06-14 15:03:30.880 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Alert_Thing_Offline' failed: <eval>:26:0 Expected an operand but found else
else {
^ in <eval> at line number 26 at column number 0

You’ve a syntax error somewhere. Maybe a missing closing bracket or something. Without seeing the current code :man_shrugging:

That’s what I figured. Should be simple, but I don’t see it:

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Scratch");

var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
logger.info("Thing Offline Alert Rule Started");

var ZonedDateTime = Java.type("java.time.ZonedDateTime");
 
var now = ZonedDateTime.now();

var runme = function(){
logger.info("Email Sent");
  
var Actions = Java.type("org.openhab.core.model.script.actions.Things");
var mailActions = Actions.getActions("mail","mail:smtp:SMTP_Server");
mailActions.sendHtmlMail("skye@bpsgreenhomes.com", "Thing Offline", "A device has gone offline. Please contact BPS at monitors@bpsgreenhomes.com or 910-470-8203");

    this.timer = undefined; // reset the timer back to undefined
}
logger.info("this.timer = " + this.timer);

if(this.timer === undefined) {
    this.timer = ScriptExecution.createTimer(now.plusSeconds(30), runme);
}
logger.info("this.timer = " + this.timer);

else {
    this.timer.reschedule(now.plusSeconds(30));
}

You can’t put anything between the closing bracket of an if statement and the else.

I thought I was supposed to log the first line after the If & the first line after the Else.

OK these are the logs I get if I make the device offline and then put it back online before the timer has expired it still sends the email. That’s the problem:

2021-06-14 15:39:54.190 [INFO ] [org.openhab.model.script.Scratch    ] - Thing Offline Alert Rule Started
2021-06-14 15:39:54.199 [INFO ] [org.openhab.model.script.Scratch    ] - this.timer = undefined
2021-06-14 15:39:54.202 [INFO ] [org.openhab.model.script.Scratch    ] - this.timer = org.openhab.core.model.script.internal.actions.TimerImpl@5b9675
==> /var/log/openhab/events.log <==
2021-06-14 15:39:54.189 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'zwave:device:Z-Wave_Serial_Controller:node4' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Node is not communicating with controller
2021-06-14 15:40:01.678 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'zwave:device:Z-Wave_Serial_Controller:node4' changed from OFFLINE (COMMUNICATION_ERROR): Node is not communicating with controller to ONLINE
==> /var/log/openhab/openhab.log <==
2021-06-14 15:40:24.198 [INFO ] [org.openhab.model.script.Scratch    ] - Email Sent

So the rule doesn’t run. What are the triggering conditions for the rule?

It’s all but impossible to figure out a problem like this if you don’t have a basic understanding of how your own code works. It would be well worth your time to spend it studying this code to understand it because otherwise we will spend many many more hours going back and forth with you trying something blindly without understanding why and making mistakes as a result and me needing to correct those mistakes just to answer one simple question: is the value of this.timer the same inside runme and it is outside of runme. And I don’t have many more hours to help with this.

Simplify. Make sure you understand what every line of code in this rule does. Make sure you understand the basic rules of syntax for the language. There are tons of freely available courses and tutorials out there to help with that. Slowly rebuild the rule little by little. Experiment to make sure you understand what the lines are really doing.

I’ll give you a little bit of help. As currently written in post 22 the code does the following:

  1. import the logger
  2. import ScriptExecution so you can create a Timer
  3. log to indicate the rule started
  4. import ZonedDateTime so you can create a Timer
  5. save now to a variable, now is the current date/time
  6. define a function named runme that gets called by the timer
    a. log to indicate when runme is called
    b. import Actions
    c. import Mail Actions
    d. send the email
    e. set this.timer to undefined so a new timer will be created the next time the rule runs
  7. log out the value of this.timer so you can see whether a new timer should be created or if the timer should be rescheduled
  8. if this.timer === undefined
    a. create a new timer for 30 seconds and set the handle to this.timer
  9. if this.timer !== undefined
    a. reschedule the existing timer for another 30 seconds.

Notice what might be missing here. Nowhere is the timer ever cancelled. An email will always be sent because you have no code to cancel the timer when the Thing goes back online. Is that the root problem? If so, the problem is that you didn’t implement it.

Also notice how the indentation is used in the list above. It makes it way easier to see what’s going on. Do the same in your code (the lines under runme between the { } should be indented).

The rule runs but it sends the email, even if the Thing goes back online before the timer expires.

I just realized I could post ALL the code:

triggers:
  - id: "1"
    configuration:
      thingUID: zwave:device:Z-Wave_Serial_Controller:node4
      status: OFFLINE
    type: core.ThingStatusChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >+
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Scratch");


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

        logger.info("Thing Offline Alert Rule Started");


        var ZonedDateTime = Java.type("java.time.ZonedDateTime");
         
        var now = ZonedDateTime.now();


        var runme = function(){

        logger.info("Email Sent");
          
        var Actions = Java.type("org.openhab.core.model.script.actions.Things");

        var mailActions = Actions.getActions("mail","mail:smtp:SMTP_Server");

        mailActions.sendHtmlMail("skye@bpsgreenhomes.com", "Thing Offline", "A device has gone offline. Please contact BPS at monitors@bpsgreenhomes.com or 910-470-8203");

            this.timer = undefined; // reset the timer back to undefined
        }

        logger.info("this.timer = " + this.timer);


        if(this.timer === undefined) {
            this.timer = ScriptExecution.createTimer(now.plusSeconds(30), runme);
        }

        else {
            this.timer.reschedule(now.plusSeconds(30));
        }

        logger.info("this.timer = " + this.timer);


    type: script.ScriptAction

Yes. Didn’t you want it to run a second time, to reschedule the timer?

So here you’ve asked it to run only when the Thing changes to OFFLINE.
It will not run when the Thing changes to ONLINE.

I don’t think that’s all you’ve got to look at though; if it only reschedules the Timer then it just puts off the email, it doesn’t go away.
You’re going to need to take different actions depending on what the Thing status changed to, ONLINE or OFFLINE.

Yes I realized it was just rescheduling and replaced that line with:

this.timer.cancel();

Which seems to work.

And I do realize now that it doesn’t trigger again to cancel the email when the Thing comes back online. My history with these types of rules is in Vera using Reactor which includes an action when the condition is no longer true & I thought this script might be doing that somehow.

I DO like the alternate structure you propose but fear it’s another rabbit hole for me because I find this code so difficult to understand (it IS starting to get better though).

So here are my questions regarding that:

Triggering from any Thing: I would really like to trigger off of any Thing status change. When making the rule in the UI though, you can only pick one Thing for the trigger. I looked at the docs where it talks about Thing triggers but there is nothing there about triggering from any Thing status. Is this a peice of code or is there some other way of doing this?

Cancelling the timer and examining the new status: Will my line above work to cancel the timer? I assume checking the status of all things is another line of code I need.

Regarding Things popping in & out and the reliability of ONLINE/OFFLINE status, it seems that it will work good enough for our use-case for most of our devices. For our battery-powered devices, I can look for missing battery reports as you suggested previously.