I have an item “Timer_Badezimmer” type number which defined the time in minutes to set the heater off.
With OH 3 it works without problems, in OH4 it looks like the type was changed.
This is the OH log :
2023-10-20 12:07:03.381 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (plusMinutes) on java.time.ZonedDateTime@1c4161f failed due to: Cannot convert ‘[object Object]’(language: JavaScript, type: d) to Java type ‘long’: Invalid or lossy primitive coercion.
at .:program(:24) ~[?:?]
at org.graalvm.polyglot.Context.eval(Context.java:399) ~[?:?]
at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458) ~[?:?]
…
2023-10-20 12:07:03.398 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID ‘0e0284f2af’ failed: org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (plusMinutes) on java.time.ZonedDateTime@1c4161f failed due to: Cannot convert ‘[object Object]’(language: JavaScript, type: d) to Java type ‘long’: Invalid or lossy primitive coercion.
Your hint doesn’t solve the problem, looks like as was remarked as wrong. I’m also no a Java guy so I have no clue what I can do that it works again. I’m not sure if it is a Java problem or a change in the definition of the items in OH4.
The OH 3 rule is definitely Nashorn ECMAScript 5.1 and it will not work in GraalVM ECMAScript 2022+ as written. They are different add-ons and mostly not compatible with each other without some extra work on your part which, ultimately, is work better spent rewriting your rules to take advantage of the helper library that comes with the newer add-on.
That will work but the more canonical way to write it would be:
var TimerMins = 'PT'+items['Timer_Badezimmer'].state+'M'; // create a duration string
cache.private.put('timer', ScriptExecution.createTimer(time.toZDT(TimerMins), turnOff);
Or, if Timer_Badezimmer were a Number:Time instead of just a Number:
there is almost never the need to import anything from Java, just about everything has been wrapped with pure JavaScript classes; all your interactions with OH should use the library instead of the raw Java Objects
time.toZDT() will intelligently handle almost everything that can be converted to a ZonedDateTime, see the table in the docs for details
don’t use this to save variables from one run to the next. Use the cache. Use cache.private if it’s limited to this one Script Action and use cache.shared if you need to share the value across multiple script actions. Note that Timers stored in the cache will be cancelled automatically if the Script becomes unloaded. No more orphaned Timers going off and throwing an exception because you edited the rule.
if you use openHABian, openhab_rules_tools is installed by default or can be installed through openhabian-config. This library has a bunch of utilities to make creation of and management of Timers easier. For example, assuming all turnOff does is send an OFF command to an Item at the given time you could use:
var { Deferred } = require('openhab_rules_tools');
Deferred().defer('NameOfLightItem', 'OFF', 'PT'+items.Timer_Badezimmer.state+'M', true);
Many thanks for the detail explanation. I use GraalVM ECMAScript 2022+.
I have installed today the “openhab_rules_tools” now and will make some tries but today I’m still busy but will check tomorrow more in detail and come back.
2023-10-22 09:27:00.335 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: Error: “Timer_Badezimmer2 (Type=NumberItem, State=1 s, Label=Timer-Badezimmer 2, Category=)” is an unsupported type for conversion to time.ZonedDateTime
Here I see two problems - first I got an error message about conversion. Not sure but it could be an result that the item includes now seconds and not minutes and the kind of it is part of the state (“1 s”)
Another question : If I put the command on the private stack, will it be executed straight away or does it have to be triggered explicitly?
Last but not least, I have add the add_on library - must I add an automatisation to handle it ?
Sorry for this amount of questions but I think it could help some other too to understand it a little bit more.
I have always struggled with the timers. It always seems so much code to do something so simple
I just put the timer value in minutes in the script rather than use an item.
This is a script I am using to execute a script on the server and I am using the latest scripting on OH4. Works for me.
var zmtimer=5
if (cache.private.exists('zmalarm') === false || cache.private.get('zmalarm').hasTerminated()) {
actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), "/usr/local/sbin/zoneminder-alarm.sh","on");
console.log("Zoneminder alarm ON: ");
cache.private.put('zmalarm', actions.ScriptExecution.createTimer('zmalarm', time.ZonedDateTime.now().plusMinutes(zmtimer), function () {
actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), "/usr/local/sbin/zoneminder-alarm.sh","off");
cache.private.remove('zmalarm');
console.log("Zoneminder alarm OFF: ");
}));
} else {
cache.private.get('zmalarm').reschedule(time.ZonedDateTime.now().plusMinutes(zmtimer));
console.log("Zoneminder alarm script running reschedule ");
};
for me the part to “test if exist” and to reschedule is interesting. But the basic idea is to have a items which can set flexible in the GUI. Here I have a problem with the conversion of the item to the Java function. If I use a number it works for me in all variants.
You might be running into a bug in the helper library depending on which version you are running. I know there was a bug with DateTime Items but maybe the same problem exists for Number Items. time.toZDT(items['Timer_Badezimmer2'].quantityState) should work around that bug.
The bug was that it doesn’t recognize the Item that’s passed to it as an Item, covers it to a string and of course the toString of an Item doesn’t make sense as a date time or time duration.
I don’t understand the question. There is no “stack”. at all. Do you mean the private cache? That’s just a place to store data. You don’t need to do anything special.
A slightly terser option could be
var zmtimer = 'PT5M'
...
cache.private.put('zmalarm', actions.ScriptExecution.createTimer('zmalarm', time.(zmtimer), function () {
The whole purpose of time.toZDT() is to make it so you don’t have to mess with plusMinutes this and minusSeconds that.
That error indicates a typo. “ScriptExecution”. Case matters.
There is no such thing as events in GraalVM JS. That’s a Nashorn thing.
`
var TimerMinutes = 2
console.info("Badezimmer Timer für "+TimerMinutes+" Minuten gestartet");
console.info("Badezimmer Timer trace 2");
function turnOff() {
items.SetPointTemperatire.sendCommand("13");
console.info("Badezimmer Timer nach "+TimerMinutes+" Minuten beendet");
items.getItem('Alexa_Sprachausgabe').postUpdate("Badezimmer heizen beendet");
items["Alexa_Sprachausgabe_Say"].sendCommand("ON");
}
console.info("Badezimmer Timer trace 3");
cache.private.put('timer2', ScriptExecution.createTimer(time.toZDT(items['Timer_Badezimmer'].quantityState), turnOff));
console.info("Badezimmer Timer trace 4");
I’m assuming Timer_Badezimmer is a Number:Time. Note how you don’t Java.type anything. The above shows three different ways to get an Item.
many thanks for your detail explanation. In all what you assumed you are right.
I register that the defaul Unit is seconds so I add the Unit “min” to the Item and it works now.
For me - I’m not really have knowledge about Java and the different versions - it’s hard if such a change comes up with a release change. But I guess that all who have made thinkings about it that there is a good reason to do it So in result I’m very happy that member like you helps us to have a progress and understand more about the platform.
Many thanks to all other who have give me hints what I can do and how to solve it. Udo’s way was easiest way and it works too.
What I learned again is that there are a lot of ways to do the same things
It’s not Java, it’s JavaScript. And it’s not a “release change”. It’s a wholly new automation add-on. It’s like going from Rules DSL to jRuby.
You don’t have to use the new JS Scripting. You can use the old Nashorn JavaScript. It’s a separate add-on though and it’s mainly there for legacy support. It should be considered deprecated. It only supports a version of the JS language that is nearly a decade old and there is no built in heloper library. It’ll take you roughly 50% more lines of code to do the same thing.
When rewriting your rules to use the new add-on, make sure you have a tab open with the docs for the new add-on. Every line of the rule should go through the following process:
Look at the relevant part of the docs for the line
Is there a new way to do the same thing? If not move to the next line.
Use the docs to figure out how to do it the new way.
For example:
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);