Cannot post update to item; Saving timer persistence for notifications

I’ve written a rule in the UI, javascript, that sends a Telegram notification every time a door opens. This works right now. I now want to optimize this rule so that if the same door opens again within a few seconds, don’t send a repeat notification.

The idea is to save to a new Item, the timestamp of the last sent notification. When the rule fires off, load this timestamp and compare to current time. If the difference is greater than 10s, send notification and update the time. If less than 10s, do nothing.

I first created a new Item, ‘Telegram_Last_Notification’ of type DateTime. There’s also a rule that triggers when any of the doors’ state change to run a script. In that script, I’m trying to follow the documentation, which indicates I can do the following, but this doesn’t work:

// Get last time we sent telegram message
var lastUpdateItem = itemRegistry.getItem("Telegram_Last_Notification");   <-- works
log.info("LAST UPDATE: " + lastUpdateItem.lastUpdate);    <-- works

// Update item and store "now" as the latest message sent
lastUpdateItem.postUpdate(new DateTimeType());  <-- does not work
//  TypeError: lastUpdateItem.postUpdate is not a function in <eval> at line number 1

Any ideas on why postUpdate doesn’t exist on this Item object?

Which JavaScript? The ECMAScript 5.1 that comes with openHAB or the JS Scripting add-on that provides ECMAScript 11. If the former, I recommend using the latter, especially if you are just getting started. This looks like ECMAScript 5.1 which is not well documented.

Rules DSL does a lot of magic in the background. In fact, the Item does not have a postUpdate nor a sendCommand method. In ECMAScript 5.1, you have to use the actions (note the actions require Strings as the two arguments.

events.postUpdate("Telegram_Last_Notification", new DateTimeType().toString());

In ECMAScript 11, a lot of work was done to make the interaction with the openHAB API simpler and behave more like “pure” JavaScript instead of a mix of JS and Java (in the above, events and DateTimeType are both Java, not JavaScript). Posting an update there would be

var lastUpdateItem = items.getItem("Telegram_Last_Notification");
lastUpdateItem.postUpdate(time.toZDT().toString());

See the docs for the add-on for full details. JavaScript Scripting - Automation | openHAB

There might be other and simpler approaches. For example, you could use the follow Profile to update the timestamp on your DateTime Item when the Channel that reports the state of the door changes state.

You could use a local variable that persists across the runs of the rule. In ECMAScript 5.1

    this.lastRun = (this.lastRun === undefined) ? null : this.lastRun;
    ...
    this.lastRun = new DateTimeType();

In ECMAScript 11 there is a cache which exists outside of the rule and can be used to share data between scripts.

cache.put("lastRun", new DateTimeType());
cache.get("lastRun");

You could have a Switch Item with an expire. When you send the Telegram command the Switch to ON. Expire will turn it OFF after the configured time. Then you just need to check to see if that Switch is ON or OFF to determine whether to send the telegram.

You can use Debounce to only forward the OPEN/CLOSED to another proxy Item after it remains OPEN/CLOSED for a certain amount of time.

If you are using the newer JS Scripting add-on, you can install libraries. I’ve an openhab-rules-tools library you can install using npm that includes a rate_limit class. This class lets you set a time between events such that if another event happens too soon, it’s ignore.

Had no idea there was another javascript option. I have installed the addon; checking it out but my first test script of simply console.log("hello") resulted in this:

14:41:25.343 [WARN ] [g.internal.OpenhabGraalJSScriptEngine] - Failed to retrieve script script dependency listener from engine bindings. Script dependency tracking will be disabled.

Not sure what to do here. Might restart OH later.

I thought about something like this, but channel updates would include open->closed and closed->open, and I only want notifications when the door is opened. Yes, I could include a simple if state == open probably if this is more efficient.

Ooh, that is nice. Then I don’t have to use Persistence for this one thing to track timestamps.

That sound quite advanced/clever. Will take a look at that one! Thank you.

Restarting OH didn’t seem to help my simple script.

That’s a known and harmless warning (notice it’s not an error). You can ignore that. There is already an issue open to address that but I don’t know it’s status.

Ok. Found out I didn’t have the right logging enabled. Did this and now I can see console.log’s:

log:set DEBUG org.openhab.automation.script

I’m seeing this toZDT() used all over the docs you linked, but every time I try to use it, I get this error:

console.log(time.toZDT().toString());
TypeError: (intermediate value).toZDT is not a function

Which version of OH are you running. If it’s not the latest release it’s not yet there. On the left side of the docs select the version of the docs that match the version of OH you are running. Instead of using time.toZDT() use time.ZonedDateTime.now().

Here’s the code I ended up with. Does exactly what I need it to do. Thanks for the help!

var doorItem = items.getItem(event.itemName);
var prettyItemName = doorItem.label;
var prettyState = doorItem.state == "OPEN" ? "Opened" : "Closed";

console.log("DOOR: " + prettyItemName + " is " + prettyState);

// Get last time we sent telegram message
var lastUpdateItem = cache.get("Telegram_Last_Notification");
console.log("LAST SENT NOTIFICATION: " + lastUpdateItem);

var lastUpdateDT = time.ZonedDateTime.parse(lastUpdateItem.toString());
var oneMinuteAgoDT = time.ZonedDateTime.now().minusMinutes(1);

if (oneMinuteAgoDT > lastUpdateDT) {
  console.log("LAST UPDATE > 1m SENDING")
  actions.Things.getActions("telegram", "telegram:telegramBot:abac81b150").sendTelegram(prettyItemName + " " + prettyState);
  cache.put("Telegram_Last_Notification", time.ZonedDateTime.now().toString());
}