Update to OH33 - Problems with date and time conversions

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
}]

Can someone give me a hint?

if (now.isAfter(Rollos_up_time.state as DateTimeType).zonedDateTime) {
...

I’m not sure if it should be zonedDateTime or getZonedDateTime.

Thanks, i changed this:

if (now.isAfter((Rollos_up_time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) {

to this

if (now.isAfter((Rollos_up_time.state as DateTimeType).getZonedDateTime())) {

and now it seems to work. Errors are gone.


But now i have a new problem:

var lastUpdateMillis = (lastUpdate as DateTime).toInstant().toEpochMilli()

Error:

[{
	"resource": "/i:/rules/strom_heatpump_2.rules",
	"owner": "_generated_diagnostic_collection_name_#0",
	"code": "org.eclipse.xtext.diagnostics.Diagnostic.Linking",
	"severity": 8,
	"message": "DateTime cannot be resolved to a type.",
	"startLineNumber": 33,
	"startColumn": 44,
	"endLineNumber": 33,
	"endColumn": 52
}]

Can someone help me here too?

What is lastUpdate? show more code!

Here you are:

// 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
					var lastUpdateMillis = lastUpdate.toInstant().toEpochMilli()

lastUpdate is already a ZonedDateTime, no need to cast it. Besides, “DateTime” is not a correct type. There’s DateTimeType but that’s something else.

Now i get this error:

The method toInstant() is undefined for the type Object

Try this:

// 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.

The error is in the line of your first answer

var lastUpdateMillis = lastUpdate.toInstant().toEpochMilli()

The 3rd line from bottom doesn´t show any error message - also your new one doesn´t show any error.

I updated my answer above

I have included the changes of your edited answer - now in vs code i don´t get any error.

Seems to work now, thanks a lot.

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?