openhab / openhab-js

openHAB JavaScript Library for JavaScript Scripting Automation
https://www.openhab.org/addons/automation/jsscripting/
Eclipse Public License 2.0
38 stars 31 forks source link

historicState is not working but calling PersistenceExtensions.historicState works #246

Closed rkoshak closed 1 year ago

rkoshak commented 1 year ago

This one is weird.

No matter what I try, this always returns undefined.

  const hState = i.history.historicState(time.toZDT().minusDays(7), 'rrd4j');

However, this works.

  const PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');
  const hState = PersistenceExtensions.historicState(i.rawItem, time.toZDT().minusDays(7), 'rrd4j');

So there is something weird going on with the library. But the code is basically the same as used by the library so the only thing I can think of is that _historicItemOrNull is going wrong, but that returns null, not undefined if there is no result.

I'm stumped.

Expected Behavior

Using the library to query for a historicstate returns null if there is a problem or a JS HistoricState Object.

Current Behavior

The full script action:

console.loggerName = 'org.openhab.automation.rules_tools.Presence Sim';
osgi.getService('org.apache.karaf.log.core.LogService').setLevel(console.loggerName, 'DEBUG');

var PersistenceExtensions = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');

items.PresenceSimulation.members.forEach((i) => {
//  const hState = i.history.historicState(time.toZDT().minusDays(7), 'rrd4j'); // doesn't work
  const hState = PersistenceExtensions.historicState(i.rawItem, time.toZDT().minusDays(7), 'rrd4j');
  console.debug('Reply event is ' + utils.dumpObject(hState));
  if(hState === undefined || hState === null) {
    console.warn(i.name + ' does not have a historic state for ' + '7' + ' days ago');
  }
  else {
    console.debug('Commanding ' + i.name + ' to ' + hState.state);
    if(i.state != hState.state) {
      i.sendCommand(hState.state.toString());
    }
    else {
      console.debug(i.name + ' is already ' + hState.state);
    }
  }
});

The working logs:

2023-02-21 11:36:50.701 [INFO ] [.openhab.automation.openhab-js.utils] - Dumping object...
2023-02-21 11:36:50.703 [INFO ] [.openhab.automation.openhab-js.utils] -   typeof obj = object
2023-02-21 11:36:50.705 [INFO ] [.openhab.automation.openhab-js.utils] -   Java.isJavaObject(obj) = true
2023-02-21 11:36:50.707 [INFO ] [.openhab.automation.openhab-js.utils] -   Java.isType(obj) = false
2023-02-21 11:36:50.708 [INFO ] [.openhab.automation.openhab-js.utils] -   Java.typeName(obj.getClass()) = org.openhab.persistence.rrd4j.internal.RRD4jItem
2023-02-21 11:36:50.709 [DEBUG] [.automation.rules_tools.Presence Sim] - Reply event is undefined
2023-02-21 11:36:50.711 [DEBUG] [.automation.rules_tools.Presence Sim] - Commanding MasterBedroomLights_Power to OFF

The not working logs:

2023-02-21 11:38:57.902 [INFO ] [.openhab.automation.openhab-js.utils] - Dumping object...
2023-02-21 11:38:57.902 [INFO ] [.openhab.automation.openhab-js.utils] -   typeof obj = undefined
2023-02-21 11:38:57.903 [INFO ] [.openhab.automation.openhab-js.utils] -   Java.isJavaObject(obj) = false
2023-02-21 11:38:57.904 [INFO ] [.openhab.automation.openhab-js.utils] -   Java.isType(obj) = false
2023-02-21 11:38:57.904 [INFO ] [.openhab.automation.openhab-js.utils] -   value = undefined
2023-02-21 11:38:57.905 [DEBUG] [.automation.rules_tools.Presence Sim] - Reply event is undefined
2023-02-21 11:38:57.906 [WARN ] [.automation.rules_tools.Presence Sim] - MasterBedroomLights_Power does not have a historic state for 7 days ago

Context

I'm rewriting a bunch of my rule templates for OH and openhab-js 4.0. The code above is for the Presence Simulation rule template.

Your Environment

florian-h05 commented 1 year ago

Only reason I can explain the undefined is, that something goes wrong and this is caught by the try_catch block:

https://github.com/openhab/openhab-js/blob/c2173854d360df7280fb027b68b2911e06fa4f47/items/item-history.js#L221-L227

Can you please and remove the try catch statement and try again?

rkoshak commented 1 year ago

I should have thought to do that. I've been rather thick this week. Yes, there is an exception, though it's weird too.

  │2023-02-21 13:35:58.148 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:                                                                      │
  │org.graalvm.polyglot.PolyglotException: Error: Failed to create "quantityState": QuantityError: Failed to create QuantityType from ON: java.lang.NumberFormatException: │
20│Invalid BigDecimal value: ON                                                                                                                                            │ro
r:│        at <js>._historicItemOrNull(/openhab/conf/automation/js/node_modules/openhab/items/item-history.js:412) ~[?:?]                                                  │
20│        at <js>.historicState(/openhab/conf/automation/js/node_modules/openhab/items/item-history.js:223) ~[?:?]                                                        │
20│        at <js>.:=>(<eval>:7) ~[?:?]                                                                                                                                    │
20│        at <js>.:program(<eval>:6) ~[?:?]                                                                                                                               │
20│        at org.graalvm.polyglot.Context.eval(Context.java:399) ~[?:?]                                                                                                   │  
00│        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458) ~[?:?]                                                             │03
20│        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426) ~[?:?]                                                             │
20│        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262) ~[java.scripting:?]                                                                    │
20│        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutoc│
20│loseable.java:53) ~[?:?]                                                                                                                                                │
20│        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEng│
20│ineWithInvocableAndAutoCloseable.java:78) ~[?:?]                                                                                                                        │
20│        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutoc│
20│loseable.java:53) ~[?:?]                                                                                                                                                │
20│        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEng│
20│ineWithInvocableAndAutoCloseable.java:78) ~[?:?]                                                                                                                        │
20│        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:71) ~[?:?]                                  │
20│        at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]                                                                                                       │
20│        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:68) ~[?:?]                                   │
20│        at org.openhab.core.automation.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1180) ~[?:?]                                                          │
20│        at org.openhab.core.automation.internal.RuleEngineImpl.runNow(RuleEngineImpl.java:1031) ~[?:?]                                                                  │
20│        at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:328) ~[?:?]                                                                  │
20│        at org.openhab.core.automation.rest.internal.RuleResource.runNow(RuleResource.java:342) ~[?:?]                                                                  │
20│        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]                                             │
20│        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.4.5]                                           │ro
r:│        at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.4.5]                                         │
20│        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.4.5]                                                    │
20│        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.4.5]                                                    │
20│        at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.4.5]                                                │
20│        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298) ~[bundleFile:3.4.5]                                         │
00│        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217) ~[bundleFile:3.4.5]                                                │30
20│        at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) ~[bundleFile:3.1.0]                                                                             │
20│        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273) ~[bundleFile:3.4.5]                                               │
20│        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.46.v20220331]                                                        │
20│        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:550) ~[bundleFile:9.4.46.v20220331]                                                    │
20│        at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:74) ~[bundleFile:?]                               │
20│        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.46.v20220331]                                                 │
20│        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.46.v20220331]                                                   │
20│        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.46.v20220331]                                               │
20│        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.46.v20220331]                                             │
20│        at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.46.v20220331]                                            │
20│        at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.46.v20220331]                                             │
20│        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.46.v20220331]                                            │
20│        at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:294) ~[bundleFile:?]                                            │
20│        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.46.v20220331]                                              │
20│        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501) ~[bundleFile:9.4.46.v20220331]                                                     │
20│        at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.46.v20220331]                                             │
20│        at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.46.v20220331]                                              │
20│        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.46.v20220331]                                             │  
  │        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.46.v20220331]                                             │
  │        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.46.v20220331]                                                 │
  │        at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:90) ~[bundleFile:?]                           │
  │        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.46.v20220331]                                               │
  │        at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.46.v20220331]                                                                       │
20│        at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.46.v20220331]                                                    │ro
r:│        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) [bundleFile:9.4.46.v20220331]                                                            │
20│        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) [bundleFile:9.4.46.v20220331]                                                              │
20│        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) [bundleFile:9.4.46.v20220331]                                                    │
20│        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) [bundleFile:9.4.46.v20220331]                                    │
20│        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) [bundleFile:9.4.46.v20220331]                                                              │
20│        at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) [bundleFile:9.4.46.v20220331]                                                           │
20│        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) [bundleFile:9.4.46.v20220331]                                         │
00│        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) [bundleFile:9.4.46.v20220331]                                       │48
20│        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) [bundleFile:9.4.46.v20220331]                                      │
20│        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) [bundleFile:9.4.46.v20220331]                                             │
20│        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) [bundleFile:9.4.46.v20220331]                       │
20│        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) [bundleFile:9.4.46.v20220331]                                               │
20│        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) [bundleFile:9.4.46.v20220331]                                          │
20│        at java.lang.Thread.run(Thread.java:833) [?:?]                                                                                                                  │
20│2023-02-21 13:35:58.178 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'presence-sim' failed: org.graalvm.polyglot.PolyglotException│
20│: Error: Failed to create "quantityState": QuantityError: Failed to create QuantityType from ON: java.lang.NumberFormatException: Invalid BigDecimal value: ON          │

I think all that needs to be done is to not throw an exception when failing to create the Quantity. It looks like that was the original intent.

I can confirm that removing the throw does now return the ON/OFF states I expect and my rule now works. But I don't know why the exception was thrown in the first place. I'll happily submit a PR if there isn't some side effect I'm not seeing.

florian-h05 commented 1 year ago

The catch you removed was from a previous PR created by me, and when I rethink it, the catch at this place is/was not needed so it can be removed. So please open a PR to remove that catch I linked above.

The new error that you run in has been solved by #243, can you please install the latest version from GitHub? The README explains how to do so.

rkoshak commented 1 year ago

Removing the catch makes sense but wont continuing to throw the exception still mean that I can't pull ON as a historic state? Instead of silently failing with an undefined return value the exception will just bubble up to the calling rule. I don't see any additional changes in #243 to handle the exception.

florian-h05 commented 1 year ago

I don't see any additional changes in https://github.com/openhab/openhab-js/pull/243 to handle the exception.

All exceptions that occur during Quantity creation are wrapped inside QuantityError (you can see this is your exception stack message), #243 fixes the design mistake I made when I implemented quantityState. If a QuantityError is thrown, it will be caught and null will be returned. If any other Exception occurs, it is rethrown. Means: Your exception is handled.