Timer script Not Cancelling if state changes

OH3 on RPi4:

I have a rule for sending an email alert, in this case when a Thing goes offline. The script sends an email after the time expires which is working, but I thought it was supposed to reset and not send the email if the Thing is connected again so teh rule is no longer triggered. That’s not working. I get an email after the timer expires in either case.

One thing I’m not clear on is teh first line. Is it supposed to refer to another file that I’m supposed to have in place?

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

var runme = function(){
var Actions = Java.type("org.openhab.core.model.script.actions.Things");
var mailActions = Actions.getActions("mail","mail:smtp:SMTP_Server");
mailActions.sendHtmlMail("email@email address.com", "Email Subject", "Email Body");
    this.timer = undefined; // reset the timer back to undefined
}

if(this.timer === undefined) {
    this.timer = ScriptExecution.createTimer(now.plusSeconds(900), runme);
}
else {
    this.timer.reschedule(now.plusSeconds(900));
}```

No, that line is bringing in the Java class that is part of openHAB that has the createTimer function.

Add some logging to see if the if statement is always running or it’s trying to reschedule the timer. Also add some logging to see what this.timer is inside runme.

For legibility, you should always indent after a {.

Sorry, pretty much of a noob here. I’m not sure what you mean by adding logging. I don’t see anything about what the script is doing in the regular logfile page.

I suspect that the example code where ever it is you learned to use this.timer has examples of logging. This also has some examples.

You can not be successful writing openHAB rules if you don’t use logging.

There are dozens of other examples. You will be hard pressed to find any JavaScript rule posted to this forum that doesn’t show logging.

@rikoshak Thanks for replying. I found the thread which contained the timer script I used & saw that rossko57 had told the OP that he had enabled logging but wasn’t using it so that’s why my script doesn’t have any.

I have stumbled through and at least understand the concept of how to add logging but didn’t get far. I have changed the rule & script a little to make it easier to test. Here’s what I have:

logger.info(event);
logger.info(this.timer)


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

var runme = function(){
var Actions = Java.type("org.openhab.core.model.script.actions.Things");
var mailActions = Actions.getActions("mail","mail:smtp:SMTP_Server");
mailActions.sendHtmlMail("XXXXX@gmail.com", "New Timer", "Email Body");
    this.timer = undefined; // reset the timer back to undefined
}

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

and the logs I get when I run it:

2021-06-04 07:02:36.794 [INFO ] [org.openhab.rule.Timer_Test         ] - undefined

The way I read this, when the rule runs it sets the timer to undefined. I assume that’s the the 2nd logfile entry.

Then, if the timer is undefined, it creates the new timer. I know it does this because I get the email, but I don’t see a log entry for that.

Then there is the final part which isn’t working. If the rule is no longer triggered, the timer should stop and the email should not be sent. I don’t understand how that is supposed to work. And the “Else” line makes no sense at all to me. It looks like it’s supposed to add 20 seconds to the timer if it’s not undefined but the script starts with setting the timer to undefined.

The function does not get executed until you call it by name.

Examples are usually provided to aid understanding. What is your still-mysterious original supposed to do?

Sorry, definitely not trying to be mysterious, just really clueless…

The rule trigger for this test is when an item turns on. What I’m trying to get working ultimately is a rule that has triggers for each Thing if the state changes to OFFLINE. I was trying to do this with the Log Reader, and I think you were helping me in another post with that but I couldn’t get it to work so I thought this would be simpler.

Rule:

When: Thing changes to OFFLINE

Then: Run timer script, send email when timer expires, unless Thing is back online (rule is no longer triggered), then cancel timer and don’t send email.

Let me offer an alternate structure.

Trigger off any Thing status change.

Cancel any existing timer, regardless.

Examine the new status.

If it is OFFLINE, then set up a Timer to send an email in future. (No need to check status here.)

Note that if a sick Thing keeps bouncing on and off quicker than your timer, you’ll never get an email. Whether that can happen depends on the mystery Thing. Thing status is not a reliable guide to real availability, nor is it intended to be.

If that is the full script after adding logging, you missed a line. Before you can use logger you need to import it and define it.

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

That line needs to be before any use of logger.

And we need to see the following to understand what is going on:

What to log Why
First line after importing the logger to see that the rule is running
first line inside runme to see the timer running
value of this.timer inside of runme to see if this.timer inside of runme is the same as this.timer outside of runme and that it’s not undefined
first line after the if to see which branch is being taken when the rule runs
first line after the else to see which branch is being taken when the rule runs

@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