Openhab 4 transform script->SCRIPT

Hi all,
sorry if the qestion is stupid but i read several messages on the forum and i don’t understand what i’m missing.
I upgraded to 4.0, i updated the shelly bindings and near everything works.
Only the transform js script was not working (as expected)
So i changed these items:

Number STAMPA3D_TIME 		"Tempo di stampa  [JS(cputime.js):%s]"     	<time>  	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTime" }
Number STAMPA3D_REMAINING 	"Tempo rimanente  [JS(cputime.js):%s]"   	<time>   	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTimeLeft" }

to

Number STAMPA3D_TIME 		"Tempo di stampa  [SCRIPT(graaljs:cputime.script):%s]"     	<time>  	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTime" }
Number STAMPA3D_REMAINING 	"Tempo rimanente  [SCRIPT(graaljs:cputime.script):%s]"   	<time>   	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTimeLeft" }

i restarted Openhab and now i get this error:

2023-03-21 12:39:30.596 [WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state '0' on item 'STAMPA3D_REMAINING' with pattern 'SCRIPT(graaljs:cputime.script):%s': Script type 'graaljs' is not supported by any available script engine.
2023-03-21 12:39:30.615 [ERROR] [ui.internal.items.ItemUIRegistryImpl] - transformation throws exception [transformation=org.openhab.core.automation.module.script.ScriptTransformationService@40921d58, value=0]
org.openhab.core.transform.TransformationException: Script type 'graaljs' is not supported by any available script engine.
        at org.openhab.core.automation.module.script.ScriptTransformationService.transform(ScriptTransformationService.java:119) ~[?:?]
        at org.openhab.core.ui.internal.items.ItemUIRegistryImpl.transform(ItemUIRegistryImpl.java:555) ~[?:?]
        at org.openhab.core.ui.internal.items.ItemUIRegistryImpl.getLabel(ItemUIRegistryImpl.java:463) ~[?:?]
        at org.openhab.core.io.rest.sitemap.internal.SitemapResource.createWidgetBean(SitemapResource.java:518) ~[?:?]
        at org.openhab.core.io.rest.sitemap.internal.SitemapResource.createPageBean(SitemapResource.java:475) ~[?:?]
        at org.openhab.core.io.rest.sitemap.internal.SitemapResource.getPageBean(SitemapResource.java:371) ~[?:?]
        at org.openhab.core.io.rest.sitemap.internal.SitemapResource.getPageData(SitemapResource.java:284) ~[?:?]
        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.doGet(AbstractHTTPServlet.java:222) ~[bundleFile:3.4.5]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:497) ~[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:772) ~[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) ~[?:?]

What i’m missing?
I have to install something for GraalVM?
Regards. and Thanks in advance.
Enrico.

There is no JavaScript of any kind there by default in OH 4. If you want to use the old Nashorn JS you need to install it. If you want to use the newer GraalVM JS you have to install it. Obviously, I recommend the newer one. If you choose Nashorn, use SCRIPT(js:cputime.script):%s.

Be aware that SCRIPT also supports Rules DSL, jRuby, and any other supported rules language now.

Also note that the actual names of the file must end with .script, not .js.

In addition, there is a bug right now where OH will not see new files in the transform folder unless it’s restarted. I think it’s been fixed and is available in the snapshots. If not it will be available soon.

Finally, it’s not completely clear what this transform does, but I would not be surprised that you could achieve what you want without it by defining the Item as type Number:Time and using DateTime label formatting to split it to HH:MM:SS for you.

1 Like

Note that both js and graaljs are owned by GraalJS.
If you want Nashorn, you’d need to use application/javascript;version=ECMAScript-5.1

I’m not sure what you are saying here.

SCRIPT(application/javascript;version=ECMAScript-5.1:cputime.script):%s

or

SCRIPT(graaljs:cputime.script):%s

in which case, if I’ve both Nashorn and JS Scripting installed how does it know which one I want to use for this transform?

is using NashornJS to run the JS transformation script.

as well as

SCRIPT(js:cputime.script):%s

is using GraalJS.

OK, then we should be consistent in the docs. In the main docs: the example talks about and shows using js but in the JS Scripting docs the example talks about using graaljs.

Given there is no other mention of the fact that there are two JS options, the natural assumption was that the main docs referred to Nashorn and the add-ons docs referred to JS Scripting. We should either change the add-on docs or the main docs to match and we should mention in the main docs that the mime type needs to be used for Nashorn (and it’s odd that it’s the only one that uses the mime type where the rest get a shortcut).

When the main docs were written, NashornJS was the default JS engine and had the js identifier. graaljs was introduced for GraalJS back then, because using the GraalJS MIME type was quite ugly.

Now with 4.0 GraalJS became the default JS engine and took over js, graaljs is kept.
You can provide a PR to openhab-js to update the JS Scripting docs.

Looking at the code (openhab-addons/bundles/org.openhab.automation.jsscriptingnashorn/src/main/java/org/openhab/automation/jsscriptingnashorn/internal/NashornScriptEngineFactory.java at 96038c664ab2fe5e766461568e62a7e0a5ddc33a · openhab/openhab-addons · GitHub), using nashornjs as alias should work. We just need to explain how to use NashornJS for SCRIPT transformation in the Nashorn addons docs. The main docs should be kept neutral.

Excellent! that’s much better.

Except excluding Nashorn from the main docs isn’t being neutral. It’s singling Nashorn out for exclusion. The main docs have examples for DSL, JS (without mention that there are two JS’s to choose from leaving the example ambiguous) and jRuby.

Given the confusion people are already demonstrating with the elimination of the JS Transformation, having a Nashorn example in the main docs will go a long way towards clearing that up.

I take this to mean we want to standardize on js, which makes sense.

IMO it’s neutral not to mention the different JS engines. The example in fact works for both GraalJS and NashornJS the same, the user doesn’t have to care about Nashorn or Graal here. Nashorn is only there to allow old scripts to still work, but no one knows when Nashorn stand-alone will leave us as well.

I disagree here because of the points above. People should just use SCRIPT(js:…) instead of JS(…), and don’t care about Nashorn.
Again, it doesn’t make sense to have separate Nashorn and Graal examples, since most JS transformations will use basic JS which is the same in both. FYI, I was able to switch all my JS transformations to Graal without having any compatibility problems.

Yes, we should standardise to js (which is GraalJS) and don’t care about Graal and Nashorn.

Except it expressly states

The script-prefix is js .

If someone installs only Nashorn JS and tries that it won’t work. And people are already hitting the forum having done just that.

You may not, but I’ve spent a significant amount of time over the past two weeks helping people who are having problems. That’s going to explode when 4.0 release happens. I’d much rather spend my time on other things than helping with confusion that could be fixed by adding one sentence to the main docs.

Agreed. I wouldn’t add a separate example. I’d just make it read:

The script-prefix is js for JS Scripting and nashornjs for JS Scripting (Nashorn).

2 Likes

Got it, thanks for your help for all the community members.

Looks good :+1:

I’ve just approved your JS Scripting docs PR, note that we need the same PR for openhab-js.

Hi sorry for writing again with 4.0M1 was working.

Number STAMPA3D_TIME 		"Tempo di stampa  [SCRIPT(graaljs:cputime.script):%s]"     	<time>  	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTime" }

i updated to M3 and again doesn’t work.

In the 4.0 docs seems that Java script scripting use prefix js, so i tested also with:

Number STAMPA3D_TIME 		"Tempo di stampa  [SCRIPT(js:cputime.script):%s]"     	<time>  	{channel="mqtt:topic:MQTT_CASA:3D_PRINTER:printTime" }

but sill get the error message:

Failed transforming the state '3461' on item 'STAMPA3D_TIME' with pattern 'SCRIPT(js:cputime.script):%s': Couldn't transform value because transformation service of type 'SCRIPT' is not available.

Java script scripting is installed:

288 │ Active │  80 │ 4.0.0.M3               │ openHAB Add-ons :: Bundles :: Automation :: JavaScript Scripting
289 │ Active │  80 │ 0.3.0.v20220506-1020   │ EdDSA-Java

and i also try with JavaScript Scripting (Nashorn)

289 │ Active │  80 │ 0.3.0.v20220506-1020   │ EdDSA-Java
...
294 │ Active │  80 │ 4.0.0.M3               │ openHAB Add-ons :: Bundles :: Automation :: JavaScript Scripting (Nashorn)

What i forgot to install?

Nothing but you missed a bunch of important things in the docs.

The script needs to be placed in the $OPENHAB_CONF/transform folder with the native extension for the chosen language, for example stringlength.js for a transformation using JS Scripting. . .

For the modern JS Scripting, the script file name is stringlength.js and the transformation is JS(stringlength.js)

Your file is not named correctly, which I think will still probably work. But the “JS” isn’t a mere prefix, it’s the whole transform. You don’t use “SCRIPT” anywhere now.

[JS(cputime.js):%s]
2 Likes

Thank you for the solution and for the link to docs!