OH3: date time replacements

  • OH3 on rpi4

I am migrating from v2 to v3, and converting rule files gets the better of me.

In v2 I had val startOfDay = now.withTimeAtStartOfDay
In v3 this seems to be val startOfDay = ZonedDateTime.now().with(LocalTime.MIDNIGHT)

However, when it gets to ZevaTempMin.postUpdate(ZevaTemperature.minimumSince(startOfDay).state) I get this error:

The error makes no sense to me.
Reading this DateTime Conversion (openHAB 3.x) does not enlighten me either.

What is the correct way to either specify the proper startOfDay, or this line:
ZevaTempMin.postUpdate(ZevaTemperature.minimumSince(startOfDay).state) in OH3 speak.

Also, I have added these two lines to the rule file:

import java.time.ZonedDateTime
import java.time.LocalTime

Are these required? I think so; just to get confirmation.

Also, is this Zeva_LastReboot.postUpdate(new DateTimeType()) now Zeva_LastReboot.postUpdate(ZonedDateTime.now())

Any hints appreciated.

The error comes from VSCode editor.
VSC too needs to understand the differences between OH2 and OH3. Is it still connecting to an OH2 instance or something? Failing to connect to OH3 LISP service? This influences how it tries to validate your code, and the autocomplete options it offers you (which are helpful for this datetime stuff)

But that does means you can try out your modified rule in OH3, and look for “real” problems in OH3’s openhab.log

Oh dear…

I just realised that I was modifying the OHv2 file; duh.
All reverted back now.

Hmm, this is now a VScode question: can I edit OH2 and OH3 files in different windows?
I found a settings.json which has the OH settings; host, etc.

Can I have a second one for OH3, and where to put it?


[update]

Settings can be added to the workspace folder (here the OH3 machine):

{
	"folders": [
		{
			"path": "."
		}
	],
	"settings": {
		"openhab.connection.host": "192.168.1.68",
		"openhab.connection.port": 8080,
		"openhab.connection.basicAuth.password": "",
		"openhab.languageserver.remoteEnabled": true,
		"openhab.languageserver.remotePort": 5007,
		"openhab.consoleCommand": "ssh maxg@%openhabhost% -p 8101",
		"workbench.iconTheme": "openhab",
		"git.enableSmartCommit": true,
		"editor.rulers": [
			48, 80
		],
		"files.trimTrailingWhitespace": true,
		"security.workspace.trust.untrustedFiles": "open",
		"editor.bracketPairColorization.enabled": true,
		"editor.guides.bracketPairs": true,
		"openhab.connection.authToken": "",
		"workbench.startupEditor": "none"
	}
}

which in my case sits in openhab folder where the rules, items folders are.

Well, neither one of these works:

ZevaLastUpdate.postUpdate(new DateTimeType(now.toString))
ZevaLastUpdate.postUpdate(ZonedDateTime.now())

the first one returns an error:

2022-11-01 22:33:46.206 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'zeva-5' failed: 2022-11-01T22:33:46.203180+10:00[Australia/Brisbane] is not in a valid format. in zeva

the second one says:

2022-11-01 22:40:46.207 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'zeva-5' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.model.script.actions.BusEvent.postUpdate(org.openhab.core.items.Item,org.openhab.core.types.State) on instance: null in zeva

Any hints?

What about ZevaLastUpdate.postUpdate(new DateTimeType())? When it’s not given anything, the constructor will assume now.

I would have expected the now.toString version to work though.

For the second one, what about ZevaLastUpdate.postUpdate(now.toString)? I know that DateTimeType’s toString will return an ISO8601 and Java’s ZonedDateTime returns a slightly different formatting standard. Maybe postUpdate only supports the latter.

When I switched to OH 3 I also switched languages so I am not sure about these problems. I suspect that the following will work too: ZevaLastUpdate.postUpdate(now.toLocalDateTime.toString). That will strip the timezone from the String which is usually the part that causes problems. It will assume the system timezone.

Note, if all that this rule is doing is updating a DateTime Item with now when an Item linked to a Channel updates or changes, you can achieve this with the timestamp profile and avoid the rule.

1 Like

Thank you!
Yes, that works!

I don’t know what I was reading; probably too much :slight_smile:
I copied the rule over and started changing things I thought I need changing, and didn’t even try what I had, which was ZevaLastUpdate.postUpdate(new DateTimeType()).

I will have a crack at the timestamp profile (later today).

Which language did you switch too?

JS Scripting. Of all the currently supported rules languages it meets all my requirements:

Requirement Rules DSL ECMAScript 5.1 Blockly Jython Java jRuby JS Scripting (ECMAScript 11) Groovy
Well known outside of OH X X X X X X X
Supports third party libraries X X X ? X X ?
Comes with a helper library by default NA NA NA? X NA?
Modern version of the language X X X X X X
Good support for UI rules/rule templates Lacks “global vars” X X Not supported in UI at all Support getting better X ?

For me, support for third party libs and UI support were the most important because I want to support and promote the use of rule templates which can only be written through the UI.

While the syntax and approach is a little different, the helper library for JS Scripting makes coding rules as easy as it is in Rules DSL. There are somethings that just work, and others that cause consternation, just like Rules DSL. :wink:

My second choice, given my requirements, would be Blockly, but I’ve long since grown out of graphically programming using blocks. But if you are not a programmer, I highly recommend it.

1 Like

Hi, i have a similar problem since update from oh2.5 to oh3:

rule "Update Sole-VL Temperatur Min-Werte"
    when
        Item HeatPump_Temperature_6 received update
    then
        var Number Min
        var String tmp
        var SimpleDateFormat df = new SimpleDateFormat( "dd.MM., HH:mm" ) 

        if (HeatPump_Temperature_6.state instanceof DecimalType) {
            Min = (HeatPump_Temperature_6.minimumSince(now.minusDays(5), "rrd4j").state as DecimalType)
            tmp = (Math::round(Min.floatValue*10.0)/10.0) + " °C (" + df.format(HeatPump_Temperature_6.minimumSince(now.minusDays(5), "rrd4j").timestamp) + " )"
            HeatPump_Sole_VL_Min.postUpdate(tmp)
        }
end

Gives me following error:

2022-12-20 11:26:56.591 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'heizung_anzeigen_usw-11' failed: Cannot format given Object as a Date in heizung_anzeigen_usw

Any idea which of the rule lines is the problem and has to be changed?

I cleaned up your rule… try this:

rule "Update Sole-VL Temperatur Min-Werte"
when
    Item HeatPump_Temperature_6 received update
then
    if (!(HeatPump_Temperature_6.state instanceof DecimalType)) {
      return
    }

    val df = java.time.format.DateTimeFormatter.ofPattern("dd.MM., HH:mm") 
    val minimum = HeatPump_Temperature_6.minimumSince(now.minusDays(5), "rrd4j")
    val value = minimum.state as DecimalType
    val displayString = value.format("%.1f") + " °C (" + minimum.timestamp.format(df) + ")"

    HeatPump_Sole_VL_Min.postUpdate(displayString)
end

Hi, i get this two erros now:

The method or field DateTimeFormatter is undefined

The method or field state is undefined for the type DecimalType

First one for 1. val line and second one for 4. val line

Second error is gone with this change: value.format(“%.1f”)
without .state.

I told you I’m not used to rulesdsl :wink: I’ve updated my post above.

Thanks again, you are a very big help for me, even you don´t use rulesdsl.

Know it works.

And now hopefully my last problem with date and time…

errors from log: i think the came from the same rule

2022-12-20 13:48:00.431 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'temperatur_anzeigen-5' failed: Could not cast NULL to org.openhab.core.library.types.DateTimeType; line 99, column 24, length 30 in temperatur_anzeigen

2022-12-20 13:48:48.924 [WARN ] [b.core.model.script.actions.BusEvent] - Cannot convert '2022-12-20T13:48:48.923887+01:00[Europe/Berlin]' to a state type which item 'Xiaomi_Temp_7_last_connection' accepts: [DateTimeType, UnDefType].

Error from vscode

The method toInstant() is undefined for the type DateTimeItem

rule

rule "Update Xiaomi Temp time since"
when
    Time cron "0 * * * * ?"
then
	if(SystemStarting.state == OFF) {
	gXiaomiZeit.members.forEach[ DateTimeItem lastTime |
    	//val lastMillis = (lastTime.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli
		val lastMillis = lastTime.toInstant().toEpochMilli()
        val mins = (now.toInstant().toEpochMilli() - lastMillis) / 60000
        //val split = lastTime.split("_") // so we can reconstruct the time_since Item name
		val split = lastTime.name.split('_')
        (split.get(0)+"_"+split.get(1)+"_"+split.get(2)+"_time_since").postUpdate(mins.toString)
    ]
	}
end

Errors are from this line: val lastMillis…

You can’t call toInstant on a DateTimeItem. See the class documentation here:

You need to call (lastTime.state as DateTimeType).getZonedDateTime().toInstant().toEpochMilli()

That was my change… You can see my old rule commented with //

Problem was the missing () because now it is the rule like before my changes - only with added ()

Thanks again!

But this error in the openhab-logs is still there:

2022-12-20 14:00:01.268 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'temperatur_anzeigen-5' failed: Could not cast NULL to org.openhab.core.library.types.DateTimeType; line 99, column 21, length 30 in temperatur_anzeigen

You’d also need to check to see if state is DateTimeType before casting it, because it can be UnDefType (NULL/UNDEF).

I know it is tedious! That’s why I don’t like rulesdsl / javascript

In jruby you’d do it like this:

rule "Update Xiaomi Temp time since" do
  every :minute
  only_if { SystemStarting.state.off? }
  run do
    gXiaomiZeit.members.select(&:state?).each do |last_time|
      age = Time.now - last_time.state # You get a Java Duration here
      name_parts = last_time.name.split("_")[0..2] + ["time_since"]
      items[name_parts.join("_")].update(age.to_minutes.to_s)
    end
  end
end

In fact you shouldn’t do it using a cron rule. Just do it when the member items updated, like this:

rule "Update Xiaomi Temp time since" do
  updated gXiaomiZeit.members
  run do |event|
    age = Time.now - event.state # You get a Java Duration here
    name_parts = event.item.name.split("_")[0..2] + ["time_since"]
    items[name_parts.join("_")].update(age.to_minutes.to_s)
  end
end

logInfo(“temp-anz-5 rule test”,"lastTime : "+lastTime)

Result was: lastTime :

Xiaomi_Temp_1_last_connection (Type=DateTimeItem, State=NULL, Label=Temp 1, Category=clock, Groups=[gXiaomiZeit])

So i have no value in lastTime.

I think i had it with change on every update, but then i didn´t get the right values inside my sitemap.

When value changes every hour, i will not get “last change 5 minutes before”, it will still be 0 minutes until the next update. So i used cron to renew the values every minute.

Next time, open a separate topic…we’ve gone way off topic here I think.

I am feeling a bit lost because I don’t really understand what your items are and what they’re supposed to do. I’m only responding to some syntax errors here and there. If you want to ask about the overall logic design, we’d need to understand and know a lot more background info.

And best to start a new topic for that.

Ok, i did that: