No return value when using setTimeout in JavaScript rule

  • Platform information:
    • Hardware: Dockerized Openhab on a Proxmox VM Host
    • OS: Debian Bullseye
    • Java Runtime Environment: 17.0.7
    • openHAB version: 4.0.0
  • Issue of the topic:
    I’m trying to setup an irrigation configuration. To start the various sections, I plan to set timeouts every day at midnight (or when the irrigation is activated in general).

I have the configuration UI and stuff working, and also the timeout is working, however, I want to store the timeoutIDs in the cache to be able to cancel them, when they are already set and the general irrigation control is switched of, but unfortunately I seem to be too stupid to figure out how to do that :confused:

My timer snipped looks like that at the moment:

let irrigationTimerIds = [];
function process_sections(section, currentIrrigationOffset, totalIrrigationTime) {
   ...
   var switchOnFunction = function(log){
         log.error("===========Switched ON=============");
   }
   var timerOnId = setTimeout(switchOnFunction, 10000, log);
   log.error("TimerOn: " + timerOnId);
   ...
   irrigationTimerIds.push(timerOnId);
}

...
wateringSections.forEach((section) => {
   process_sections(section, currentIrrigationOffset, totalIrrigationTime);
});
cache.shared.put("irrigationTimers", irrigationTimerIds);
...

The function is happily executed after 10s, but the log ouput for TimerOn is always undefined

2023-08-05 19:12:19	TimerOn: undefined

Given all my research, that should work fine and setting the timeOut like so is also working in the browser console. So question is … what’s my error? Any hints?

Every time the rule runs you create a new empty irrigationTimersIDs and replace what was there with this new one, effectively throwing away the old one that had the ID from the last Timer created.

You need to see if "irrigationTimers" already exists and push to that one and only create a new one on that first timer.

See JavaScript Scripting - Automation | openHAB, in particular that first example with the anonymous function.

var counter = cache.private.get('counter', () => ({ 'times': 0 }));

If the entry in the cache for counter is null, the entry is initialized with the result of the function call and that gets returned.

Note, it’s is almost pointless to put the timer ID from a call to setTimeout into the shared cache. The JavaScript timer manager is limited to that specific Script Action/Condition. You cannot, for example, cancel a timer created in one rule from another rule. Use the private cache.

You can share openHAB Timers created using createTimer between rule though.

Thx for jumping in here @rlkoshak!

Maybe I’m still missing some concepts here. The irrigationTimersID was indeed ment as temporary storage, as I push it later to the cache where I planned to retrieve it from in the next run of the rule.
The idea was to cancel all pending timers and create them from scratch each start of day. Then push the whole list of timerIds (is that possible) to the cache in order to be able to cancel them when the config is changed during the day, which would trigger a rerun of that rule. Thus it would be fine to overwrite the previous one I think … or did I miss something?

Would a variable defined with let be still available in the next run of the rule?

Got the cache point. Will change this as anyway the same rule is doing the cancellation of the timers.

Now one last question. The following part, always returns "undefined" as value for "timerOnId". Thus apparently there is no value stored in the list that I could push to the cache. I can see the timer is working properly due to the log entry that it writes after it expires, but for whatever reason, I seem to fail in getting an ID that I could use to cancel it later.
Any idea why it’s like that?

var timerOnId = setTimeout(switchOnFunction, 10000, log);
log.error("TimerOn: " + timerOnId);

Thanks a lot for your help! It’s always quite enlightening to read your advise :wink:

also learning javascript but i think its related to this [jsscripting] Fix timerId not returned by JS timer methods by florian-h05 · Pull Request #15308 · openhab/openhab-addons · GitHub

Your code doesn’t do that though. Walk through it step-by-step:

  1. create the variable irrigationTimerIds and initialize it as an empty array
  2. for each section
    a. create a timer and save ID to timerOnId
    b. push timerOnId to irrigationTimerIds
  3. push irrigationTimerIds to the shared cache

now the rule triggers again

  1. create the variable irrigationTimerIds and initialize it as an empty array
  2. for each section
    a. create a timer and save ID to timerOnId
    b. push timerOnId to irrigationTimerIds
  3. push irrigationTimerIds to the shared cache

Nothing from that first run of the rule was saved. They were simply replaced with the new stuff from the second run of the rule, leaving the timers from the first run still out there and running with no ability to cancel or otherwise interact with them.

If it’s a UI rule, you can’t use let at all because the context is reused on the next run of the rule. The whole point of let is that it prevents you from declaring a new variable with the same name as a variable that already exists. So on the second rule it’ll complain that the variable already exists.

In a file based rule, you can use let because the context is not reused in the same way. The variables are not preserved from one run to the next.
Same goes for const.

I always use openHAB Timers and never use setTimeout so I have no idea. According to the docs it should return a number representing the timer ID.

Given when that was merged, to pick up that change I think you’ll have to install openhab-js manually and change the add-on settings to pull the installed version instead of the version built into the add-on.

It was included with 4.0.1, see Release openHAB 4.0.1 · openhab/openhab-distro · GitHub.

Hi there,

thx for your input. @stamate_viorel, thx for pointing to that issue! for whatever reason I seem to have missed that one in my research. That seems to be exactly my case and I’m indeed on 4.0.0 at the moment. Seems to be time to upgrade :).

You’re right regarding what I’ve posted. I skipped the cancellation part as I anyway couldn’t test it yet due to the missing timerOnId.

I’m on file based rules, so should be fine I guess. Thx for clarifying!

Are you refering to createTimer here? Being in JavaScript and following the docs setTimeout seemed to me the only way to go.

setTimeout is much more primitive and has a number of limitations compared to openHAB Timers created using createTimer. You can’t:

  • reschedule them without cancelling and recreating
  • share them between Script Actions/Script Conditions/Rules (at least in UI rules)
  • can’t query them to see if they are still scheduled, have run in the past, is running, or was canceled