Seite 1 von 1

null in Rule

Verfasst: 25. Okt 2021 13:01
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

Re: null in Rule

Verfasst: 25. Okt 2021 13:57
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.

Re: null in Rule

Verfasst: 26. Okt 2021 11:44
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...

Re: null in Rule

Verfasst: 26. Okt 2021 12:03
von peter-pan
Das geht vielleicht mit einem Timer, aber da ist @udo1toni der Spezialist. Der kann dir das genauer erklären.

Re: null in Rule

Verfasst: 26. Okt 2021 14:06
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!

Re: null in Rule

Verfasst: 26. Okt 2021 16:29
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.

Re: null in Rule

Verfasst: 26. Okt 2021 21:15
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

Re: null in Rule

Verfasst: 26. Okt 2021 23:28
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.

Re: null in Rule

Verfasst: 26. Okt 2021 23:45
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