Hi, i updated my oh 2.5 install to latest oh3 yesterday. I changed much of myyy rules, but i still have problems with date and time conversions…
Here is my rule which is giving me errors:
// Rollos morgens nicht vor xx Uhr hoch Mo-Fr
rule "SunriseLimit on --- Mo-Fr und Sa-So"
when
Time cron "0 30 6 ? * MON-FRI" or
Time cron "0 0 7 ? * SAT-SUN"
then
SunriseLimit.sendCommand(ON)
Thread::sleep(100) // give persistence time to catch up
logInfo("Rollosteuerung", "6:30 Uhr bzw. 7:00 Uhr morgens nacher SunriseLimit " + SunriseLimit.state)
if (now.isAfter((Rollos_up_time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) {
if (Rollos_is_day.state != ON) {
logWarn("Rollosteuerung", "--- 1 --- Achtung !!! - Fehler --- Rollos hätten jetzt hochfahren müssen --- Rollos Auffahrzeit bereits erreicht: " + Rollos_up_time.state)
}
}
end
Error
[{
"resource": "/i:/rules/rollosteuerung.rules",
"owner": "_generated_diagnostic_collection_name_#0",
"code": "org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types",
"severity": 8,
"message": "Type mismatch: cannot convert from long to ChronoZonedDateTime<?>",
"startLineNumber": 31,
"startColumn": 18,
"endLineNumber": 31,
"endColumn": 93
}]
// Variablen Stromzähler Wärmepumpe
var lastUpdate = null
var lastCount = 0
var ticksBetween = 5
var smoothingSamples = 2
var total = 0.0d
var average = 0.0d
val samples = newLinkedList()
rule "Stromzaehler Waermepumpe Berechnung Watt"
when
Item HeatPump_Stromzaehler received command
then
//logInfo("Stromzaehler Waermepumpe Berechnung Watt", "Stromzähler received Update")
/*
* Take 2: React on every input, get the time inbetween and calculate power based
* on time passed.
* Very granular, calculation takes about 20ms on Raspi3B
*/
var newCommand = (receivedCommand as DecimalType).intValue
var tickDiff = newCommand - lastCount
if (lastUpdate !== null && lastCount > 0) {
if (tickDiff == ticksBetween) {
var nowMillis = now.toInstant().toEpochMilli()
var lastUpdateMillis = (lastUpdate as DateTime).toInstant().toEpochMilli()
var diff = nowMillis - lastUpdateMillis
var watt = ((1800.0 / diff) * 1.0 * ticksBetween) as Double
// Calculate moving average
if (samples.size == smoothingSamples) {
total -= (samples.first as Double).doubleValue
samples.removeFirst
}
total += watt
samples.addLast(watt)
average = total / samples.size
HeatPump_Stromzaehler_Watt.postUpdate(average)
}
else {
logInfo("Waermepumpe Berechnung Watt", "Tick difference not what we expected (" + ticksBetween + "): " + tickDiff)
}
}
lastUpdate = now()
lastCount = newCommand
end
// Variablen Stromzähler Wärmepumpe
var ZonedDateTime lastUpdate = null
var lastCount = 0
var ticksBetween = 5
var smoothingSamples = 2
var total = 0.0d
var average = 0.0d
val samples = newLinkedList()
rule "Stromzaehler Waermepumpe Berechnung Watt"
when
Item HeatPump_Stromzaehler received command
then
//logInfo("Stromzaehler Waermepumpe Berechnung Watt", "Stromzähler received Update")
/*
* Take 2: React on every input, get the time inbetween and calculate power based
* on time passed.
* Very granular, calculation takes about 20ms on Raspi3B
*/
var newCommand = (receivedCommand as DecimalType).intValue
var tickDiff = newCommand - lastCount
if (lastUpdate !== null && lastCount > 0) {
if (tickDiff == ticksBetween) {
var nowMillis = now.toInstant().toEpochMilli()
var lastUpdateMillis = lastUpdate.toInstant().toEpochMilli()
var diff = nowMillis - lastUpdateMillis
var watt = ((1800.0 / diff) * 1.0 * ticksBetween) as Double
// Calculate moving average
if (samples.size == smoothingSamples) {
total -= (samples.first as Double).doubleValue
samples.removeFirst
}
total += watt
samples.addLast(watt)
average = total / samples.size
HeatPump_Stromzaehler_Watt.postUpdate(average)
}
else {
logInfo("Waermepumpe Berechnung Watt", "Tick difference not what we expected (" + ticksBetween + "): " + tickDiff)
}
}
lastUpdate = now
lastCount = newCommand
end
What changed?
The top where you declared the variable lastUpdate, define its type too.
Rules DSL has syntactic sugar where, if it’s a getter function (i.e. starts with “get” and returns a value), you can call it as if accessing the data member directly. Also, if there are no arguments, the parens are optional.
So .zonedDateTime, .getZonedDateTime(), and .getZonedDateTime are all equivalent and valid.
Notice that you didn’t just change how you called getZonedDateTime() here. You also dropped the toInstant.toEpochMilli.
This is one of my pet peeves. It is almost never better to mess with epoch. It’s more work, more code, harder to understand, requires way more code, and I can count on one hand the number of use cases where its necessary (and none of those cases exist in OH rules).
if (lastUpdate !== null && lastCount > 0) {
if (tickDiff == ticksBetween) {
var diff = java.time.Duration.between(now, lastUpdate).toMillis()
var watt = ((1800.0 / diff) * 1.0 * ticksBetween) // it'll already be a BigDecimal, no need to cast to Double
// Calculate moving average
...
Just about every problem on this thread has to do with getting those milliseconds. So why bother?