In one of the code above I saw Duration.between(now, previousState.timestmap) (sic!)
It should be Duration.between(previousState.timestamp, now)
Basically, with a “pseudocode” Duration.between(now, 1-hour-ago) you’ll get a duration of NEGATIVE 1 hour, and with Duration.between(1-hour-ago, now) you’ll get a POSITIVE 1 hour. Just something to be aware of, as it has tripped me up too
got it, thanks! i added that to that code but it still throws an error:
cannot invoke method public abstract java.time.ZonedDateTime org.openhab.core.persistence.HistoricItem.getTimestamp() on null
latest code:
rule 'Heater runtime'
when
Item main_heater_power changed to ON
then
val previousState = main_heater_power.previousState(true)
logInfo("heater runtime", "Previous state result is " + previousState);
logInfo("heater runtime", "Previous state timestamp is " + previousState.timestamp)
val newDuration = Duration.between(previousState.timestamp, now)
logInfo("heater runtime", "Duration is " + newDuration);
end
OK I’ve tested that exact rule and I can see the same error message.
Firstly, you’ll probably see this in your log too: Previous state result is null
This means the call main_heater_power.previousState(true) returned null, which explains why you cannot call previousState.timestamp
Please check:
What is your DEFAULT persistence service? Is it really influxdb?
Is the item actually persisted to influxdb, on every change? If not, then the data might indeed be null / non existent from the persistence service
Try this version which specified “influxdb” specifically on the persistence call, and also converts the ZonedDateTime to string otherwise rulesdsl will complain
rule 'Heater runtime'
when
Item main_heater_power changed to ON
then
val previousState = main_heater_power.previousState(true, "influxdb")
logInfo("heater runtime", "Previous state result is {}", previousState);
logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
val newDuration = Duration.between(previousState.timestamp, now)
logInfo("heater runtime", "Duration is {}", newDuration.toString);
end
If you can’t rely on persistence, then you could keep track of the durations manually.
I find the code a bit strange though. Why trigger when it turned ON, and then find the time it turned OFF (from persistence)? Wouldn’t that calculate the OFF duration? I thought you wanted to know the ON duration?
Yeah, this is what I get when type “influx” in command line:
Connected to http://localhost:8086 version 1.8.10
i’ve used influx for years with openhab.
In the openhab console I have influxDB persistence layer checked.
I do not have previousState as an item or persisted. thought that was a val and did not need an item. May be confusing with var? If so, dang, I will add it as a number? Maybe thats the whole problem. Then there is newDuration as well…
And yes, I want the ON time so may have that backwards.
You need to persist the item main_heater_power on every change in your influxdb.persist configuration file.
previousState is the name of the persistence extensions’ method/function, not an item name.
Can you query the values for main_heater_power in influxdb directly? I am even more unfamiliar with influxdb although I do use it, I just stumble in the dark when it comes to influxdb management.
The Duration is an awesome class, please don’t do Duration → String → Parse the string to get the value… that’s horrible, not to mention error prone. The string representation of the Duration can include H, M, etc.
OK, that one is easy. Number is an object. ofMillis() requires a primitive long.
Duration.ofMillis(totalRuntime.longValue)
Yes, that’s correct. That was a typo on my part. Duration also has an abs() method that will return the absolute value of the Duration. That 's handy when you don’t know ahead of time which timestamp is earlier, or don’t care.
OH usually keeps up with Xtend so it’s probably supported. It wouldn’t work in this context though because we are dealing with Item states and NULL is not the same as null.
If you know programming I recommend either JS Scripting or jRuby (I use JS Scripting). If you are not a programmer I recommend Blockly. In all of these you get a more feature complete language to operate in and more access to OH than is available in Rules DSL (e.g. functions, libraries, access to Item metadata, rules that can call other rules, etc.). All three are also supported through the UI though Blockly, being graphical, is only supported through the UI.
Duration’s toString() outputs an ISO8601 duration formatted string. That one means 6 minutes, 1 second and 229392 nanoseconds (I think). 3 days, 4 hours, 5 minutes, 6 seconds would be P6DT4H5M6S. It’s really quite flexible and once you understand the “PT” part super easy to read.
JS Scripting uses the joda-js library which works almost the same as the Java ones only a bunch has been added to the helper library to auto convert stuff to ZonedDateTimes.
No that’s seconds and the decimal part is nanos. But like @jimtng points out, you don’t need to do any math at all. That’s why I recommend it’s use. I cringe every time I see someone converting times to epoch and tracking milliseconds.
To elaborate on this a little bit. A Number:Time’s default units are a seconds and it supports the same state description patterns as a DateTime. So if you postUpdate
This is one of those rare cases where you can have your cake and eat it too. Now you can get the nice pretty human readable view but you also can chart it because it’s just a number.