OpenHAB rule tools - timerMgr exception with ZonedDateTime

  • Platform information:
    • Hardware: AMD64
    • OS: Ubuntu 23.04
    • Java Runtime Environment: Zulu17.44+15-CA
    • openHAB version: 4.02 (And also occurred on 4.00 & 4.01)
    • OpenHAB JS version 4.5.1
    • OpenHAB rule tools 2.0.2

I have been playing around with the TimerMgr from OpenHAB rules tools, as part of the process of replacing my old ‘CreateTImerWithArguments’ based rules before that is completely deprecated - But I didn’t just want to replace like-for-like, I was keen to leverage some of the additional functionality available, especially with the global cache, and the Snazzy TimerMgr utility (Thanks @rlkoshak )

Anyway I have been trying to cross a pretty basic use case hurdle of passing a ZonedDateTime into the TimerMgr, and keep on getting an exception thrown at me as follows:

2023-08-16 21:38:06.330 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: Error: "2023-08-16T21:38:16.328+12:00[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime
	at <js>.toZDT(/node_modules/openhab.js:2) ~[?:?]
	at <js>.check(/etc/openhab/automation/js/node_modules/openhab_rules_tools/timerMgr.js:46) ~[?:?]
	at <js>.:program(<eval>:36) ~[?:?]
	at org.graalvm.polyglot.Context.eval(Context.java:399) ~[?:?]
	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458) ~[?:?]
	at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426) ~[?:?]
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262) ~[java.scripting:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
	at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
	at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:71) ~[?:?]
	at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]
	at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:68) ~[?:?]
	at org.openhab.core.automation.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1188) ~[?:?]
	at org.openhab.core.automation.internal.RuleEngineImpl.runNow(RuleEngineImpl.java:1039) ~[?:?]
	at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:381) ~[?:?]
	at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:398) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
	at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.4.5]
	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.4.5]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.4.5]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.4.5]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.4.5]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.4.5]
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298) ~[bundleFile:3.4.5]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217) ~[bundleFile:3.4.5]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:517) ~[bundleFile:4.0.4]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273) ~[bundleFile:3.4.5]
	at org.ops4j.pax.web.service.spi.servlet.OsgiInitializedServlet.service(OsgiInitializedServlet.java:102) ~[bundleFile:?]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) ~[bundleFile:9.4.50.v20221201]
	at org.ops4j.pax.web.service.spi.servlet.OsgiFilterChain.doFilter(OsgiFilterChain.java:100) ~[bundleFile:?]
	at org.ops4j.pax.web.service.jetty.internal.PaxWebServletHandler.doHandle(PaxWebServletHandler.java:310) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[bundleFile:9.4.50.v20221201]
	at org.ops4j.pax.web.service.jetty.internal.PrioritizedHandlerCollection.handle(PrioritizedHandlerCollection.java:96) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:722) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) ~[bundleFile:9.4.50.v20221201]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) ~[bundleFile:9.4.50.v20221201]
	at java.lang.Thread.run(Thread.java:833) ~[?:?]
2023-08-16 21:38:06.333 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'timermgrtest' failed: org.graalvm.polyglot.PolyglotException: Error: "2023-08-16T21:38:16.328+12:00[SYSTEM]" is an unsupported type for conversion to time.ZonedDateTime

I have created a quick/simple UI based rule file as follows to test various options in the search of a fix, including calling the createtimer directly with ZDT, etc (See 5 different ‘test cases’ at the bottom of the file.

What I have found is the the first 3 cases run just fine, including TimerMgr always works just fine if I pop in a Duration value instead, such as “PT10S”.

Test Rule:

//const { time } = require('openhab');
var {timerMgr} = require('openhab_rules_tools');
var timers = cache.shared.get('heatpump_tm', () => new timerMgr.TimerMgr());

// Log versions
var {helpers} = require('openhab_rules_tools');
console.info(utils.OPENHAB_JS_VERSION);
console.info(helpers.OHRT_VERSION);


var schedtime = time.ZonedDateTime.now().plusSeconds(10);
var schedfoz = time.toZDT(schedtime).withFixedOffsetZone();

console.info(schedtime);
console.info(schedfoz);

var tmran = function(iname,newtime) {
  return () => {
    console.info(`Test Timer Rule 1: Timer "${iname}" was scheduled for ${newtime}"`);
  };
}

var tmreschedule = function(iname,newtime) {
  return () => {
    console.info(`Test Timer Rule 1: Timer "${iname}" was running - rescheduled for ${newtime}"`);
  };
}


//All of the following work as expected with or without the "const { time } = require('openhab');" at the beginning of the file
//timers.check("testtimer3","PT10S",tmran("testtimer3",schedtime),true,tmreschedule("testtimer3",schedtime), 'heatpump_tm_'+"testtimer3");
//actions.ScriptExecution.createTimer("testtimer1", schedtime, tmran("testtimer1",schedtime));
//actions.ScriptExecution.createTimer("testtimer2", schedfoz, tmran("testtimer2",schedfoz));


// Either of the following fail when executed, unless I add in the "const { time } = require('openhab');" at the beginning of the file
timers.check("testtimer4",schedtime,tmran("testtimer4",schedtime),true,tmreschedule("testtimer4",schedtime), 'heatpump_tm_'+"testtimer4");
//timers.check("testtimer5",schedfoz,tmran("testtimer5",schedfoz),true,tmreschedule("testtimer5",schedfoz), 'heatpump_tm_'+"testtimer5");

However, I also seem to have found a “solution”, and that is adding in the following at the top of the Rule file

const { time } = require('openhab');

Problem is, that I don’t see this in the examples, and trying to follow the code (My rule file->timerMgr.js->helpers.js) I have no clue as to why it was failing nor why my ‘fix’ is working, other than maybe I was picking up a different java ‘time’ library perhaps?
Any ideas/thoughts welcome.

When I uncomment that first line, everything seems to run just fine, log as follows:

2023-08-16 21:53:13.390 [INFO ] [ab.automation.script.ui.timermgrtest] - 4.5.1
2023-08-16 21:53:13.390 [INFO ] [ab.automation.script.ui.timermgrtest] - 2.0.2
2023-08-16 21:53:13.392 [INFO ] [ab.automation.script.ui.timermgrtest] - 2023-08-16T21:53:23.390+12:00[SYSTEM]
2023-08-16 21:53:13.392 [INFO ] [ab.automation.script.ui.timermgrtest] - 2023-08-16T21:53:23.390+12:00
2023-08-16 21:53:23.391 [INFO ] [ab.automation.script.ui.timermgrtest] - Test Timer Rule 1: Timer "testtimer5" was scheduled for 2023-08-16T21:53:23.390+12:00"
2023-08-16 21:53:23.392 [INFO ] [ab.automation.script.ui.timermgrtest] - Test Timer Rule 1: Timer "testtimer1" was scheduled for 2023-08-16T21:53:23.390+12:00[SYSTEM]"
2023-08-16 21:53:23.393 [INFO ] [ab.automation.script.ui.timermgrtest] - Test Timer Rule 1: Timer "testtimer2" was scheduled for 2023-08-16T21:53:23.390+12:00"
2023-08-16 21:53:23.394 [INFO ] [ab.automation.script.ui.timermgrtest] - Test Timer Rule 1: Timer "testtimer4" was scheduled for 2023-08-16T21:53:23.390+12:00[SYSTEM]"
2023-08-16 21:53:23.397 [INFO ] [ab.automation.script.ui.timermgrtest] - Test Timer Rule 1: Timer "testtimer3" was scheduled for 2023-08-16T21:53:23.390+12:00[SYSTEM]"

JS Scripting Config is as follows:

See Parsing RFC DateTime string doesn't work with 4.5.1 add-on but works with npm installed library · Issue #288 · openhab/openhab-js · GitHub. The good news is there is a bug with handling items.someDateTimeItem in time.toZDT() which might be the case here.

The bad news is I’m seeing some weirdness with this specific error where for the same version of openhab-js it occurs when using the built in library but not when using the npm installed library. I still need to run some tests and see if I can figure out a way that it can be reproduced by others.

In the mean time use Duration strings. I actually find them to be cleaner and less complicated most of the time.

That is indeed weird.

I know one way to fix it is to install openhab-js via npm and changing the add-on to not use the built in one. I never tried adding a require. This could point to a problem with the way the libraries are injected into the rules.

None of the examples import it because it’s supposed to be imported automatically when “Use Built-in Variables” is selected.

1 Like

Cheers for the quick response. FYI managed to reproduce it on another computer as well (Just in case it was environmental):

  • Clean new build of OpenHAB 4.01 (Only binding is JS Scripting)
  • npm installation of OpenHAB rule tools
  • Cut ‘n’ paste of Test JS script

Exact same behaviour. All environment aspects are the same (JDK, Ubuntu version etc, just desktop edition not server this time)

Might stick to using the ZDT for now, and include the ‘require’ statement as a workaround, as this better aligns to the rules that I was playing with (The above was just a simple test rule to try any figure out what I was doing wrong).

Now that I have reproduced it on a play system, I’m happy to test any fixes, or perform other investigative actions as required. Let me know.
Cheers - Glen

1 Like