[Solved] OH2: time calculations -- doing my head in

  • Platform information:
    • Hardware: Raspberry Pi 3 Model B Rev 1.2_
    • System: Host: rpi3ohv2 Kernel: 5.4.51-v7+ armv7l bits: 32 Console: tty 2
    • Distro: Raspbian GNU/Linux 10 (buster)
    • OpenJDK Runtime Environment (Zulu 8.31.1.122-linux_aarch32hf) (build 1.8.0_181-b122)
  • Version: 2.5.10 (Build) (apt-get), text-based config
    • binding = astro,exec,logreader,network,ntp,systeminfo,fritzboxtr0641,expire1,mqtt1,weather1
    • ui = paper,basic,classic,restdocs
    • persistence = rrd4j,mapdb
    • action = mail,mqtt
    • transformation = map,javascript,xslt,scale,jsonpath

I am trying to evaluate a time calculation w/o success. The rule

rule "Sonoff: Shed_NLO_PIR timer"
    when
        Item Shed_NLO_PIR received update ON
    then
        val Integer remainingTimeThreshold = 60 * 1000

        if (now.millis - Shed_NLO_PIR_LUP.lastUpdate.millis < remainingTimeThreshold)
        {
            logInfo("Sonoff.2.05", "Shed_NLO_PIR_LUP now - LUP....: {} sec", now.millis - now.millis - Shed_NLO_PIR_LUP.lastUpdate.millis)
        }

… it produces an error:

2020-11-08 15:12:06.369 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Sonoff: Shed_NLO_PIR timer': cannot invoke method public abstract long org.joda.time.ReadableInstant.getMillis() on null

The more I think about this DateTime not being DateTimeType and all, I’d like to ideally convert all to millis() and work with that…0.

The calendar method is deprecated:

val DateTime lup = new DateTime ((Shed_NLO_PIR_LUP.state as DateTimeType).calendar.currentTimeMillis)

According to the docs, this shoudd work:

val Number lup = (Shed_NLO_PIR_LUP.state as DateTimeType).zonedDateTime.timeInMillis

…, but it doesn’t!
It errors with:

2020-11-08 15:36:08.152 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Sonoff: Shed_NLO_PIR timer': 'timeInMillis' is not a member of 'java.time.ZonedDateTime'; line 50, column 26, length 67

Any hints appreciated.

Unfortunately I cannot help in detail, but I hope there is helpful information at DateTime Conversion (openHAB 2.x)

1 Like

Maybe this would help?

The use of ZonedDateTime will future proof things for OH3 (no more Joda Time).

import java.time.temporal.ChronoUnit// put this before any rules or global variables

rule "Sonoff: Shed_NLO_PIR timer"
when
    Item Shed_NLO_PIR received update ON
then
    val remainingTimeThreshold = 60// don't cast unless you absolutely have too
    val seconds_since_last_update = (new DateTimeType(Shed_NLO_PIR_LUP.lastUpdate.toString)).zonedDateTime.until((new DateTimeType).zonedDateTime, ChronoUnit.SECONDS)
    if (seconds_since_last_update < remainingTimeThreshold) {
        logInfo("Sonoff.2.05", "Shed_NLO_PIR_LUP now - LUP....: {} sec", seconds_since_last_update)
    }
end

Thanks guys… I should have replied earlier…
I read the link anfeanger provided and found the solution.

This works:

val Number lup = (Shed_NLO_PIR_LUP.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

Final rule (just tested):

rule "Sonoff: Shed_NLO_PIR timer"
    when
        Item Shed_NLO_PIR_pxy received update ON
    then
        // setpoint value for initial and subsequent timer durations
        val PIR_Duration = (Shed_NLO_PIR_Timeout.state as DecimalType).intValue

        // ms when allowing rescheduling the timer before its scheduled end time
        val Integer rescheduleThreshold = 60 * 1000

        // get the last update time stamp for Shed_NLO_PIR_LUP
        val Number lup = (Shed_NLO_PIR_LUP.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

        logInfo(logPrefix + "2.01", "Shed_NLO_PIR_LUP now - LUP....: {}", now.millis - lup)


        if (Shed_NLO_PIR_Timer === null || Shed_NLO_PIR_Timer.hasTerminated())
        {
            logInfo(logPrefix + "2.02", "Shed_NLO_PIR..................: ON for {} minutes", PIR_Duration)
            Shed_NLO_PIR.postUpdate(ON)
            Shed_NLO_PIR_LUP.postUpdate(new DateTimeType())

            Shed_NLO_PIR_Timer = createTimer(now.plusMinutes(PIR_Duration), [|
                logInfo(logPrefix + "2.03", "Shed_NLO_PIR..................: OFF")
                Shed_NLO_PIR.postUpdate(OFF)
                Shed_NLO_PIR_LUP.postUpdate(new DateTimeType())
                Shed_NLO_PIR_Timer = null
            ])
        }
        else
        {
            /*
                logInfo(logPrefix + "2.04", "now.millis....................: {}", now.millis)
                logInfo(logPrefix + "2.05", "LUP...........................: {}", lup)
                logInfo(logPrefix + "2.06", "Shed_NLO_PIR_LUP now - LUP....: {}", now.millis - lup)
                logInfo(logPrefix + "2.07", "rescheduleThreshold...........: {}", rescheduleThreshold)
                logInfo(logPrefix + "2.08", "is lup + duration - now < 6000: {}", lup + (PIR_Duration * 60 * 1000) - now.millis)
            */
            // allow reschedule within 60 sec before end of timer
            if ((lup + (PIR_Duration * 60 * 1000) - now.millis) < rescheduleThreshold)
            {
                logInfo(logPrefix + "2.09", "if = .........................: true")
                logInfo(logPrefix + "2.10", "Shed_NLO_PIR rescheduled......: yes, for {} minutes", PIR_Duration)
                Shed_NLO_PIR_Timer.reschedule(now.plusMinutes(PIR_Duration))
                Shed_NLO_PIR_LUP.postUpdate(new DateTimeType())
            }
        }
end