null in Rule

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
harteknut
Beiträge: 235
Registriert: 3. Dez 2019 08:21
Answers: 9

null in Rule

Beitrag von harteknut »

Hallo zusammen,
ich habe eine (m.E.) ungewöhnliche Auffälligkeit, zu der ich in den Foren nix finde, und daher versuche ich es mal mit einem neuen Thema. Evtl. ist es ja nur eine Kleinigkeit, und Ihr klärt mich schnell auf.
Zur Umgebung:
openHAB 3.1.0 als openhabian auf Raspberry Pi 4B mit 12 Bindings, 74 Things und 450 Items. Läuft seit dem Update auf OH3 echt super und sehr unauffällig. Nur eins versteh ich nicht:
Nach jedem Neustart lässt sich eine der Rules (die normalerweise per Timer einmal pro Minute die Zeit an ein Display schickt) nicht mehr ausführen. Im Log erscheint folgende Fehlermeldung:

Code: Alles auswählen

2021-10-24 19:38:02.051 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'anzeige-3' failed: null in anzeige
Die Logeinträge wiederholen sich jede Minute, da die Rule minütlich aufgerufen wird, bis ich nix anderes tun als die Rule unverändert (!) nochmal zu speichern, damit sie aktualisiert wird. Dann erscheint

Code: Alles auswählen

2021-10-24 19:40:27.795 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'anzeige.rules'
im Log und alles läuft wie gewünscht. Bis zum nächsten Neustart...

Hier der Code der Rule:

Code: Alles auswählen

rule "Anzeige Müllkalender (Zeile 3)"
	when
		// Item Regeltrigger changed from OFF to ON or 
		Time cron "0 0/1 * * * ?" or
		Item Tonne_muss_raus changed
	then
		val akt_Zeit = String::format( "%1$ta, %1$td. %1$tb %1$tk:%1$tM h", ZonedDateTime.now() ) //now().toString
		val nae_Muell = Ephemeris.getNextBankHoliday("/etc/openhab/services/muellkalender.xml")
		naechster_Feiertag.postUpdate(akt_Zeit) 
		if  ((Ephemeris.isBankHoliday(ZonedDateTime.now(), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON)) {
			actions.sendDynamicText(3, ("heute: " + nae_Muell))	}
		else {
			if  ((Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON) && (now.isAfter(ZonedDateTime.now().with(LocalTime.MIDNIGHT).plusMinutes(1140)))) {
				actions.sendDynamicText(3, ("morgen: " + nae_Muell)) }
			else {
				actions.sendDynamicText(3, (akt_Zeit) )}}
end
Würde mich super freuen, wenn das jemand erklären könnte.
Grüße in die Runde
Simon

Benutzeravatar
peter-pan
Beiträge: 2769
Registriert: 28. Nov 2018 12:03
Answers: 30
Wohnort: Schwäbisch Gmünd

Re: null in Rule

Beitrag von peter-pan »

Ich weiss zwar nicht, ob das ursächlich oder überhaupt damit zusammenhängt, aber müsste, die "actions" nicht erst einmal initialisiert werden ? Z.B. so:

Code: Alles auswählen

rule "Send dynamic Text to GT10D hourly"
when
    Time cron "0 0 * * * ?"
then
    val actions = getActions("lcn","lcn:module:b827ebfea4bb:S000M012")
    actions.sendDynamicText(1, "Test 123 CO₂ öäü߀") // row 1
end
Ich könnte mir auch vorstellen, das zum Zeitpunkt des Neustarts irgend ein Wert (Variable/Item) noch nicht geladen ist.
Pi5/8GB(PiOS Lite 64-bit(bookworm)/SSD 120GB - OH4.3.5 openhabian

harteknut
Beiträge: 235
Registriert: 3. Dez 2019 08:21
Answers: 9

Re: null in Rule

Beitrag von harteknut »

Ich glaube, da bist Du dem richtigen Thema auf der Spur...

Die Initialisierung der action mittels getActions mache ich weiter oben in der *.rules-Datei. Hintergrund: Ich schreibe jede Zeile des GT4D in einer separaten Rule, die unterschiedlich getriggert werden:
  • Zeile 1: Ist- und Tagesmax-Temperatur
  • Zeile 2: Regen- bzw. Schneefallsituation oder ggf. Unwetterwarnung
  • Zeile 3: Datum und Uhrzeit oder ggf. Mülltonnenhinweis
Ganz zu Beginn der Datei definiere ich alle Variablen und auch die action.
Kann ich das Ausführen der rules bzw. Einlesen der *.rule-Datei nach Start nicht irgendwie verzögern? Da war doch was, ich komm aber nicht drauf...

Benutzeravatar
peter-pan
Beiträge: 2769
Registriert: 28. Nov 2018 12:03
Answers: 30
Wohnort: Schwäbisch Gmünd

Re: null in Rule

Beitrag von peter-pan »

Das geht vielleicht mit einem Timer, aber da ist @udo1toni der Spezialist. Der kann dir das genauer erklären.
Pi5/8GB(PiOS Lite 64-bit(bookworm)/SSD 120GB - OH4.3.5 openhabian

harteknut
Beiträge: 235
Registriert: 3. Dez 2019 08:21
Answers: 9

Re: null in Rule

Beitrag von harteknut »

Ich glaube, ich werde mal das hier probieren:

Code: Alles auswählen

sudo systemctl edit openhab2.service
dann

Code: Alles auswählen

[Service]
ExecStartPre=-/bin/bash -c '/usr/bin/find ${OPENHAB_CONF} -name "*.rules" -exec /usr/bin/rename.ul .rules .rules_away {} \\;'
ExecStartPost=/bin/sleep 240
ExecStartPost=-/bin/bash -c '/usr/bin/find ${OPENHAB_CONF} -name "*.rules_away" -exec /usr/bin/rename.ul .rules_away .rules {} \\;'
TimeoutStartSec=360
Ich berichte dann obs hilft!

Benutzeravatar
udo1toni
Beiträge: 15265
Registriert: 11. Apr 2018 18:05
Answers: 245
Wohnort: Darmstadt

Re: null in Rule

Beitrag von udo1toni »

ja, das ist die schmutzige Version :) Die Dateien werden umbenannt, wenn openHAB startet. Nach vier Minuten [sleep(240)] werden die Dateien wieder mit den ursprünglichen NAmen versehen, woraufhin openHAB sie einliest und beginnt, die Rules auszuführen, sobald die entsprechenden Trigger auftreten.
Die interessantere Frage wäre allerdings, was da null ist. Und ich kann mir gut vorstellen, dass es tatsächlich mit der Initialisierung der actions zusammenhängt.
Es mag sein, dass man das außerhalb der Rule erledigen kann, sicherer ist es aber, das innerhalb der Rule zu erledigen. Den Handle bekommst Du gewöhnlich innerhalb weniger Millisekunden, und das ist dann auch zuverlässig. Selbst wenn es beim ersten Mal schief geht, beim zweiten Mal hast Du eine gültigen Handle.
openHAB4.3.5 stable in einem Debian-Container (bookworm) (Proxmox 8.4.1, LXC), mit openHABian eingerichtet

harteknut
Beiträge: 235
Registriert: 3. Dez 2019 08:21
Answers: 9

Re: null in Rule

Beitrag von harteknut »

Hm, die "schmutzige Version" will ich natürlich nicht... ;)
Daher nochmal eine Rückfrage:
Ich hatte die Definition der actions ja extra aus den einzelnen Rules rausgenommen weil ich dachte, es wäre besser die nur einmal zu initialisieren und nicht bei jedem Triggerereignis erneut (erst recht nicht dann, wenn eine der Rules jede Minute aufgerufen wird).
Stört es nicht, wenn ich die in jeder der drei Zeilen bei jedem Aufruf neu initialisiere?

Hier mal die ganze Datei:

Code: Alles auswählen

import java.time.ZonedDateTime
var String zeile_1 = ""
var String zeile_2 = ""
var actions = getActions("lcn","lcn:module:edb2e504:S000M019")

rule "Anzeige Temperatur (Zeile 1)"
	when
		Item OWMLocalWeatherAndForecastCurrentTemperature changed or
		Item OWMLocalWeatherAndForecastForecastTodayMaxTemperature changed or
		Time cron "13 3  0/1 * * ?" // jede Stunde um 03m 03s
	then
		val T_akt = String::format("%.1f",(OWMLocalWeatherAndForecastCurrentTemperature.state as QuantityType<Number>).floatValue)
		val T_max = String::format("%.1f",(OWMLocalWeatherAndForecastForecastTodayMaxTemperature.state as QuantityType<Number>).floatValue)
		zeile_1 = (T_akt + " °C   (" + T_max + " °C)")
		actions.sendDynamicText(1, zeile_1)
end

rule "Anzeige Niederschlag / Unwetter (Zeile 2)"
when
	Item OWMLocalWeatherAndForecastForecastTodayRain changed or
	Item OWMLocalWeatherAndForecastForecastTodaySnow changed or
	Item DWDUnwetterWarnungenWarning1 changed or
    Time cron "0 0/1 * * * ?" //"23 3  0/1 * * ?" // jede Stunde um 03m 03s
then
		val Summe = String::format("%.0f",((OWMLocalWeatherAndForecastForecastTodayRain.state as QuantityType<Number>).floatValue + (OWMLocalWeatherAndForecastForecastTodaySnow.state as QuantityType<Number>).floatValue))
		val Schnee = String::format("%.0f",(OWMLocalWeatherAndForecastForecastTodaySnow.state as QuantityType<Number>).floatValue)
		val Regen = String::format("%.0f",(OWMLocalWeatherAndForecastForecastTodayRain.state as QuantityType<Number>).floatValue)
		zeile_2 = ("init")
		if (DWDUnwetterWarnungenWarning1.state == OFF) {
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  ==  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  ==  0|"mm")) { 
				zeile_2 = ("  trocken") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  >  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  ==  0|"mm")) {
				zeile_2 = (" Regen: " + Regen + " mm ") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  ==  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  >  0|"mm")) {
				zeile_2 = (" Schnee: " + Schnee + " mm ") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  >  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  >  0|"mm")) {
				zeile_2 = ("Schneeregen: " + Summe + " mm") }
			}
		else {
			zeile_2 = (DWDUnwetterWarnungenEvent1.state.toString)
			}
		actions.sendDynamicText(2, zeile_2)
end

rule "Anzeige Müllkalender (Zeile 3)"
	when
		Time cron "0 0/1 * * * ?" or
		Item Tonne_muss_raus changed
	then
		val akt_Zeit = String::format( "%1$ta, %1$td. %1$tb %1$tk:%1$tM h", ZonedDateTime.now() )
		val nae_Muell = Ephemeris.getNextBankHoliday("/etc/openhab/services/muellkalender.xml")
		naechster_Feiertag.postUpdate(akt_Zeit) 
		if  ((Ephemeris.isBankHoliday(ZonedDateTime.now(), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON)) {
			actions.sendDynamicText(3, ("heute: " + nae_Muell))	}
		else {
			if  ((Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON) && (now.isAfter(ZonedDateTime.now().with(LocalTime.MIDNIGHT).plusMinutes(1140)))) {
				actions.sendDynamicText(3, ("morgen: " + nae_Muell)) }
			else {
				actions.sendDynamicText(3, (akt_Zeit) )}}
end

rule "Tonnenstatus setzen" // Update Tonnenstatus um 3
    when
        Time cron "0 3 0 1/1 * ?"
    then
		if  (Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) {
				Tonne_muss_raus.sendCommand(ON) }
end

Benutzeravatar
udo1toni
Beiträge: 15265
Registriert: 11. Apr 2018 18:05
Answers: 245
Wohnort: Darmstadt

Re: null in Rule

Beitrag von udo1toni »

Wie gesagt... die Variable actions (das sollte übrigens KEINE Variable sein, sondern eine Konstante) sollte in der Rule definiert sein, also lokal statt global. Variablen und Konstanten definiert man nur dann global, wenn dies notwendig ist.

Abgesehen davon ist es eventuell sinnvoller, die Rules etwas anders zu gestalten, nämlich, den Teil, der den Text schreibt, in eine eigene Rule auszulagern.

Code: Alles auswählen

//import java.time.ZonedDateTime
var String zeile_1 = ""
var String zeile_2 = ""
var String zeile_3 = ""

rule "Anzeige Temperatur (Zeile 1)"
when
    Item OWMLocalWeatherAndForecastCurrentTemperature changed or
    Item OWMLocalWeatherAndForecastForecastTodayMaxTemperature changed or
    Time cron "13 3  * * * ?"                    // jede Stunde um 03m 03s
then
    val T_max = String::format("%.1f",(OWMLocalWeatherAndForecastForecastTodayMaxTemperature.state as Number).floatValue)
    val T_akt = String::format("%.1f",(OWMLocalWeatherAndForecastCurrentTemperature.state as Number).floatValue)
    zeile_1 = T_akt + " °C   (" + T_max + " °C)"
end

rule "Anzeige Niederschlag / Unwetter (Zeile 2)"
when
    Item OWMLocalWeatherAndForecastForecastTodayRain changed or
    Item OWMLocalWeatherAndForecastForecastTodaySnow changed or
    Item DWDUnwetterWarnungenWarning1 changed or
    Time cron "0 * * * * ?"                      // "23 3  0/1 * * ?" // jede Stunde um 03m 03s
then
    val Number nRain  = (OWMLocalWeatherAndForecastForecastTodayRain.state as Number).floatValue
    val Number nSnow  = (OWMLocalWeatherAndForecastForecastTodaySnow.state as Number).floatValue
    val String Summe  = String::format("%.0f",nRain + nSnow)
    val String Schnee = String::format("%.0f",nSnow)
    val String Regen  = String::format("%.0f",nRain)
    zeile_2 = "init"
    if(DWDUnwetterWarnungenWarning1.state == OFF) {
        if (nRain == 0 && nSnow == 0) zeile_2 = "  trocken"
        if (nRain >  0 && nSnow == 0) zeile_2 = " Regen: " + Regen + " mm"
        if (nRain == 0 && nSnow >  0) zeile_2 = " Schnee: " + Schnee + " mm"
        if (nRain >  0 && nSnow >  0) zeile_2 = "Schneeregen: " + Summe + " mm"
    } else                            zeile_2 = DWDUnwetterWarnungenEvent1.state.toString
end

rule "Anzeige Müllkalender (Zeile 3)"
when
    Time cron "0 * * * * ?" or
    Item Tonne_muss_raus changed
then
    val akt_Zeit = String::format("%1$ta, %1$td. %1$tb %1$tk:%1$tM h", ZonedDateTime.now())
    val nae_Muell = Ephemeris.getNextBankHoliday("/etc/openhab/services/muellkalender.xml")
    naechster_Feiertag.postUpdate(akt_Zeit) 
    if(Ephemeris.isBankHoliday(ZonedDateTime.now(), "/etc/openhab/services/muellkalender.xml") && Tonne_muss_raus.state == ON)
        zeile_3 = "heute: " + nae_Muell
    else if(Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml") && Tonne_muss_raus.state == ON && now.getHour > 18)
        zeile_3 = "morgen: " + nae_Muell
    else
        zeile_3 = akt_Zeit
end

rule "Tonnenstatus setzen" // Update Tonnenstatus um 3
when
    Time cron "0 3 0 * * ?"
then
    if(Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) {
        Tonne_muss_raus.sendCommand(ON) }
end

rule "Send lcn Display"
when
    Time cron "15 * * * * ?" // Minütlich 15 Sekunden nach
then
    val actions = getActions("lcn","lcn:module:edb2e504:S000M019")
    actions.sendDynamicText(1, zeile_1)
    actions.sendDynamicText(2, zeile_2)
    actions.sendDynamicText(3, zeile_3)
end
Die meisten Klammern sind nicht notwendig. Die cron Ausdrücke können einfacher geschrieben werden. Das Casting nach Number ist hinreichend, auch bei QuantityType.
Vermutlich wäre es sinnvoll, die verschiedenen Items auf gültige Werte zu prüfen, aber nach spätestens einer Minute Laufzeit sollten keine Probleme auftreten.
openHAB4.3.5 stable in einem Debian-Container (bookworm) (Proxmox 8.4.1, LXC), mit openHABian eingerichtet

harteknut
Beiträge: 235
Registriert: 3. Dez 2019 08:21
Answers: 9

Re: null in Rule

Beitrag von harteknut »

Ist angepasst, danke Dir!

Code: Alles auswählen

import java.time.ZonedDateTime
var String zeile_1 = ""
var String zeile_2 = ""

rule "Anzeige Temperatur (Zeile 1)"
	when
		Item OWMLocalWeatherAndForecastCurrentTemperature changed or
		Item OWMLocalWeatherAndForecastForecastTodayMaxTemperature changed or
		Time cron "13 3  0/1 * * ?" // jede Stunde um 03m 13s
	then
		val actions = getActions("lcn","lcn:module:edb2e504:S000M019")
		val T_akt = String::format("%.1f",(OWMLocalWeatherAndForecastCurrentTemperature.state as QuantityType<Number>).floatValue)
		val T_max = String::format("%.1f",(OWMLocalWeatherAndForecastForecastTodayMaxTemperature.state as QuantityType<Number>).floatValue)
		zeile_1 = (T_akt + " °C   (" + T_max + " °C)")
		actions.sendDynamicText(1, zeile_1)
end

rule "Anzeige Niederschlag / Unwetter (Zeile 2)"
when
	Item OWMLocalWeatherAndForecastForecastTodayRain changed or
	Item OWMLocalWeatherAndForecastForecastTodaySnow changed or
	Item DWDUnwetterWarnungenWarning1 changed or
        Time cron "23 3  0/1 * * ?" // jede Stunde um 03m 23s
then
		val actions = getActions("lcn","lcn:module:edb2e504:S000M019")
		val Summe = String::format("%.0f",((OWMLocalWeatherAndForecastForecastTodayRain.state as QuantityType<Number>).floatValue + (OWMLocalWeatherAndForecastForecastTodaySnow.state as QuantityType<Number>).floatValue))
		val Schnee = String::format("%.0f",(OWMLocalWeatherAndForecastForecastTodaySnow.state as QuantityType<Number>).floatValue)
		val Regen = String::format("%.0f",(OWMLocalWeatherAndForecastForecastTodayRain.state as QuantityType<Number>).floatValue)
		zeile_2 = ("init")
		if (DWDUnwetterWarnungenWarning1.state == OFF) {
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  ==  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  ==  0|"mm")) { 
				zeile_2 = ("  trocken") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  >  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  ==  0|"mm")) {
				zeile_2 = (" Regen: " + Regen + " mm ") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  ==  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  >  0|"mm")) {
				zeile_2 = (" Schnee: " + Schnee + " mm ") }
			if ((OWMLocalWeatherAndForecastForecastTodayRain.state  >  0|"mm") && (OWMLocalWeatherAndForecastForecastTodaySnow.state  >  0|"mm")) {
				zeile_2 = ("Schneeregen: " + Summe + " mm") }
			}
		else {
			zeile_2 = (DWDUnwetterWarnungenEvent1.state.toString)
			}
		actions.sendDynamicText(2, zeile_2)
end

rule "Anzeige Müllkalender (Zeile 3)"
	when
		Time cron "0 0/1 * * * ?" or // jede Minute
		Item Tonne_muss_raus changed
	then
		val actions = getActions("lcn","lcn:module:edb2e504:S000M019")
		val akt_Zeit = String::format( "%1$ta, %1$td. %1$tb %1$tk:%1$tM h", ZonedDateTime.now() )
		val nae_Muell = Ephemeris.getNextBankHoliday("/etc/openhab/services/muellkalender.xml")
		naechster_Feiertag.postUpdate(akt_Zeit) 
		if  ((Ephemeris.isBankHoliday(ZonedDateTime.now(), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON)) {
			actions.sendDynamicText(3, ("heute: " + nae_Muell))	}
		else {
			if  ((Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) && (Tonne_muss_raus.state == ON) && (now.isAfter(ZonedDateTime.now().with(LocalTime.MIDNIGHT).plusMinutes(1140)))) {
				actions.sendDynamicText(3, ("morgen: " + nae_Muell)) }
			else {
				actions.sendDynamicText(3, (akt_Zeit) )}}
end

rule "Tonnenstatus setzen" // Update Tonnenstatus um 3
    when
        Time cron "0 3 0 1/1 * ?"
    then
		if  (Ephemeris.isBankHoliday(ZonedDateTime.now().plusDays(1), "/etc/openhab/services/muellkalender.xml")) {
				Tonne_muss_raus.sendCommand(ON) }
end


Antworten