I have a rule for calculating the minutes until sunset, this is quite useful for switching off heavier load items as the day ends and solar power diminishes.
The rule below worked fine with OH2, but with the migration to OH3(I did a clean install) this rule does not work. It is the ONLY thing that does not work with OH3 thus far, which quite impressed me.
Items:
Number MinutesUntilSunset "Minutes Until Sunset [%.0f min]"
Rule:
rule "Minutes Until Sunset"
when
Time cron "0/30 * * * * ?"
then
MinutesUntilSunset.postUpdate((new DateTime(Sunset_Time.state.toString).millis - new DateTime(Current_DateTime.state.toString).millis) / 60000)
end
I am aware that OH3 now uses Javas native date/time format but I just cannot get my head around it. Any help appreciated!
As documented in the release notes, DateTime no longer exists in openHAB 3. Itâs been replaced by ZonedDateTime. See Rules | openHAB and DateTime Conversion (openHAB 3.x) for lots of examples of working with ZonedDateTime.
Given that we have ZonedDateTimes now, the best approach would probably be to use the stuff built into the classes anyway.
java.time.temporal.ChronoUnit
// in the rule
var minsToSunset = ChronoUnit.MINUTES.between(now, Sunset_Time.state.getZonedDateTime)
You might need to cast Sunset_Time.state to a DateTimeType.
I have a similar problem to calculate the minutes between the last state change (saved in a DateTime item) and now. I want to write the rule as ECMA script but I can not calculate with the variable now and the DateTime item.
ECMA code:
var logger = Java.type(âorg.slf4j.LoggerFactoryâ).getLogger(âorg.openhab.model.script.Rules.Experimentsâ);
var now = new Date(new Date() - 10000)
var lastAction = itemRegistry.getItem(âSpuelmaschine_OpStateLastChangeâ).getState()
Thanks @rlkoshak appreciate the pointer, although Java Time is quite a big change(for me) it certainly has benefits and a lot more built in functionality. You were correct Sunset_Time.state needed to be cast.
Working rule:
import java.time.temporal.ChronoUnit
rule "Minutes Until Sunset"
when
Time cron "0/30 * * * * ?"
then
var minsToSunset = ChronoUnit.MINUTES.between(now, (Sunset_Time.state as DateTimeType).getZonedDateTime)
MinutesUntilSunset.postUpdate(minsToSunset)
var minsPastSunrise = ChronoUnit.MINUTES.between((Sunrise_Time.state as DateTimeType).getZonedDateTime, now)
MinutesPastSunrise.postUpdate(minsPastSunrise)
end
There is no more DateTime. It has been replaced with ZonedDateTime. To get now use ZonedDateTime.now(). Once oyu do that the above will work in all the languages.
var ZonedDateTime = Java.type("java.time.ZonedDateTime");
var ChronoUnit = Java.type("java.time.temporal.ChronoUnit"0;
var now = ZonedDateTime.now();
var minsToSunset = ChronoUnit.MINUTES.between(now, (Sunset_Time.state as DateTimeType).getZonedDateTime());
Thank you Rick,
this was the solution. One last Question:-)
My timestamp (here Sunset_Time) is in the past, so the result is negativ between jetzt1 (now) and my timestamp. When I change the two parts in the expression I get the following error in the log:
Script execution of rule with UID 'Java_Test' failed: TypeError: (itemRegistry.getItem("Spuelmaschine_OpStateLastChange").getState() ,> jetzt1).getZonedDateTime is not a function in <eval> at line number 45
And here my expression:
var laufzeitMinuten = ChronoUnit.MINUTES.between((itemRegistry.getItem('Spuelmaschine_OpStateLastChange').getState(), jetzt1).getZonedDateTime());
If this is JavaScript, that line is a little overly complex. All the Itemâs current states are stored in an items dict. Also, the state of a DateTime Item is not a ZonedDateTime. But you can get a ZonedDateTime from it. So
var laufzeitMinuten = ChronoUnit.MINUTES.between(items['Spuelmaschine_OpStateLastChange'].getZonedDateTime(), jetzt1);
Not Iâm assuming that jetzt1 is itself also already a ZonedDateTime.
Even if that wasnât a problem, your parens are in the wrong place and the expression is essentially meaningless.