Seite 1 von 2

Joda vs. Java time

Verfasst: 21. Aug 2021 22:36
von OliverCJ
Guten Abend zusammen,

nachdem ich nun einige Monate OH3 parallel zu OH2.5 genutzt habe um mich erstmal vertraut zu machen, habe ich heute den Pi mit OH2.5 abgeschaltet... Im Grunde funktioniert jetzt alles so wie vorher. Nur mit der Umstellung auf JavaTime hapert es noch in einer rule...

Okay, so ganz richtig lief die eh nie, aber ich habe zumindest Werte bekommen... Zur Zeit bekomme ich gar nichts :-(

Hier mal die alte rule unter 2.5:

Code: Alles auswählen

import org.joda.time.LocalTime
import java.util.ArrayList
import java.util.Map
import java.util.HashMap
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.Lock

var Map <String, Double> FuelPricesMap = new HashMap<String, Double>()    // Stationsnamen und Preise für den Kraftstoff
var ArrayList <StringItem> FuelPriceItems =  new ArrayList<StringItem>()

val telegramAction = getActions("telegram","telegram:telegramBot:openHAB")


rule "Benzinpreise Info"
when
	Item FuelPricesMap received update
then
	val LocalTime afternoon = new LocalTime(15, 0)  //Jeden Nachmittag um 15:00 Uhr
  	val LocalTime evening = new LocalTime(19, 0)    //Jeden Abend um 19:00 Uhr
  	
	if (now.toLocalTime().isAfter(afternoon) && now.toLocalTime().isBefore(evening)) {   //zwischen 15 und 19 Uhr
		telegramAction.sendTelegram("Günstigster Sprit: " + TK_Tankstelle_1)               //Erste Tankstelle im Array = günstigste Tankstelle
	}
		else if (TK_Tankstelle_1.state.toString.contains ("0,00")) {
    	postUpdate(TK_Tankstelle_1, "Geschlossen")
	   	}
end


rule "Benzinpreise Update"
when
	Item TK_Stat1_E10 received update or
	Item TK_Stat2_E10 received update or
    Item TK_Stat3_E10 received update or
    Item TK_Stat4_E10 received update
then
    var Lock lock = new ReentrantLock()
	
	var i = 0
	
	lock.lock()

	logInfo("Tankerkönig", "Benzinpreise -> Update der Werte")
    
	// Zuweisung der Display Items in das Array
    FuelPriceItems.add(0, TK_Tankstelle_1)
    FuelPriceItems.add(1, TK_Tankstelle_2)

	// aktuelle Preise aus den Items in die Map übernehmen    
    FuelPricesMap.put("HEM", (TK_Stat1_E10.state as DecimalType).doubleValue())
    FuelPricesMap.put("ESSO", (TK_Stat2_E10.state as DecimalType).doubleValue())
    FuelPricesMap.put("ARAL", (TK_Stat3_E10.state as DecimalType).doubleValue())
	FuelPricesMap.put("ESSO Köln", (TK_Stat4_E10.state as DecimalType).doubleValue())

	// Map nach Preisen sortieren und die Werte dann dem Anzeigearray zuweisen
        for (PriceEntry : FuelPricesMap.entrySet.sortBy[value]) {
            FuelPriceItems.get(i).postUpdate(String::format("%s: %.3f €", PriceEntry.getKey(), PriceEntry.getValue()))
             i = i+1
        }
	lock.unlock()
end
Ich habe bisher folgende Änderungen vorgenommen:

Code: Alles auswählen

import java.time.LocalDateTime
import java.util.ArrayList
import java.util.Map
import java.util.HashMap
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.locks.Lock

var Map <String, Double> FuelPricesMap = new HashMap<String, Double>()    // Stationsnamen und Preise für den Kraftstoff
var ArrayList <StringItem> FuelPriceItems =  new ArrayList<StringItem>()


rule "Benzinpreise Info"
when
	Item FuelPricesMap received update
then
	val telegramAction = getActions("telegram","telegram:telegramBot:openHAB")
	val LocalDateTime afternoon = new LocalDateTime(15, 0)  //Jeden Nachmittag um 15:00 Uhr
  	val LocalDateTime evening = new LocalDateTime(19, 0)    //Jeden Abend um 19:00 Uhr
  	
	if (now.toLocalDateTime().isAfter(afternoon) && now.toLocalDateTime().isBefore(evening)) {   //zwischen 15 und 19 Uhr
		telegramAction.sendTelegram("Günstigster Sprit: " + TK_Tankstelle_1)               //Erste Tankstelle im Array = günstigste Tankstelle
	}
		else if (TK_Tankstelle_1.state.toString.contains ("0,00")) {
    	postUpdate(TK_Tankstelle_1, "Geschlossen")
	   	}
end


rule "Benzinpreise Update"
when
	Item TK_Stat1_E10 received update or
	Item TK_Stat2_E10 received update or
    Item TK_Stat3_E10 received update or
    Item TK_Stat4_E10 received update
then
    var Lock lock = new ReentrantLock()
	
	var i = 0
	
	lock.lock()

	logInfo("Tankerkönig", "Benzinpreise -> Update der Werte")
    
	// Zuweisung der Display Items in das Array
    FuelPriceItems.add(0, TK_Tankstelle_1)
    FuelPriceItems.add(1, TK_Tankstelle_2)

	// aktuelle Preise aus den Items in die Map übernehmen    
    FuelPricesMap.put("HEM", (TK_Stat1_E10.state as DecimalType).doubleValue())
    FuelPricesMap.put("ESSO", (TK_Stat2_E10.state as DecimalType).doubleValue())
    FuelPricesMap.put("ARAL", (TK_Stat3_E10.state as DecimalType).doubleValue())
	FuelPricesMap.put("ESSO Köln", (TK_Stat4_E10.state as DecimalType).doubleValue())

	// Map nach Preisen sortieren und die Werte dann dem Anzeigearray zuweisen
        for (PriceEntry : FuelPricesMap.entrySet.sortBy[value]) {
            FuelPriceItems.get(i).postUpdate(String::format("%s: %.3f €", PriceEntry.getKey(), PriceEntry.getValue()))
             i = i+1
        }
	lock.unlock()
end
Mir ist klar, dass das mit den globalen Variablen nicht mehr funktioniert. Aber da bekomme ich gar keinen Fehler. Den Fehler bekomme ich in der ersten rule bei LocalTime bzw jetzt immer noch bei LocalDateTime:
tankerkoenig.JPG
Irgendwas scheine ich hier (https://community.openhab.org/t/datetim ... 3-x/107197) noch falsch zu verstehen...
Kann mir jemand auf die Sprünge helfen?

Danke und Gute Nacht
Oliver

Re: Joda vs. Java time

Verfasst: 22. Aug 2021 05:53
von udo1toni
OliverCJ hat geschrieben: 21. Aug 2021 22:36 Mir ist klar, dass das mit den globalen Variablen nicht mehr funktioniert.
Was soll denn da nicht mehr funktionieren? Globale Variablen funktionieren wunderbar. Allerdings nur innerhalb der *.rules Dateien. Wenn Du eine Rule (egal welches Progammiermodell) über die UI erzeugst, stehen keine globalen Variablen zur Verfügung. Du kannst höchstens den Inhalt von Variablen aus dem vorherigen Lauf der selben Rule "retten", was aber nur einen Teil der benötigten Funktionalität abdeckt - Stichwort Timer.
OliverCJ hat geschrieben: 21. Aug 2021 22:36 Den Fehler bekomme ich in der ersten rule bei LocalTime bzw jetzt immer noch bei LocalDateTime:
Wenn, dann müsstest Du vermutlich eher sowas verwenden:

Code: Alles auswählen

val afternoon = new ZonedDateTime().withHour(15).withMinute(0).withSecond(0),withNano(0)
Eventuell wäre eine Variante auch diese hier:

Code: Alles auswählen

val afternoon = ZonedDateTime.now().with(LocalTime.MIN).withHour(15)
Nimm den aktuellen Zeitpunkt, aber den frühestmöglichen Zeitpunkt dieses Tages, und rechne 15 Stunden drauf. Damit ist dann ebenfalls sichergestellt, dass auch die Sekunden auf 0 stehen.

Aber es wäre ohnehin viel einfacher, die Stunde zu prüfen:

Code: Alles auswählen

if(now.getHour > 14 && now.getHour < 19) {   //zwischen 15 und 19 Uhr
Problematisch wird das nur, wenn die Zeiten nicht mehr auf der vollen Stunde liegen. Da hilft dann get:

Code: Alles auswählen

if(now.get(SECOND_OF_DAY) >= 15*60*60 + 0*60 + 0 && now.get(SECOND_OF_DAY) < 19*60*60 + 0*60 + 0) {   //zwischen 15 und 19 Uhr
Zu beachten ist der Unterschied zwischen getHour > 14 und get(SECOND_OF_DAY) >= 15*60*60. Im ersten Fall wird ja nur die Stunde geprüft, um 14:59:59 ist die Stunde gleich 14 und um 15:00:00 ist sie gleich 15, während im zweiten Fall ja auf die Sekunde geschaut wird. Deshalb muss auch auf >= getestet werden.

Re: Joda vs. Java time

Verfasst: 22. Aug 2021 13:53
von OliverCJ
Hallo Udo,

ich hatte mehrfach gelesen, dass globale Variablen eben nicht mehr funktionieren. Vielleicht verwende ich auch den falschen Ausdruck. Ich meine die Variablen, die ich innerhalb der rules-Datei definiere, aber noch vor der ersten rule. Tatsächlich hatte ich damit nach der Umstellung Probleme, während es aber funktioniert, wenn ich die Definition innerhalb der rule vornehme...

Ist vielleicht nicht ganz das Gleiche, aber auch bei der Telegram-Action funktioniert folgende Definition NICHT mehr:

Code: Alles auswählen

val telegramAction = getActions("telegram","telegram:telegramBot:openHAB")

rule "Send telegram with question"
when
    Item gAnwesende changed from ON to OFF
then
    
    if (gLicht.state == ON) {
        telegramAction.sendTelegramQuery("Es ist keiner mehr zu Hause, aber es sind noch Lampen an. Möchtest Du sie ausschalten?", "Reply_Lights", "Ja", "Nein")
    }
end
Ich musste das so umstellen:

Code: Alles auswählen

rule "Send telegram with question"
when
    Item gAnwesende changed from ON to OFF
then
    val telegramAction = getActions("telegram","telegram:telegramBot:openHAB")
    if (gLicht.state == ON) {
        telegramAction.sendTelegramQuery("Es ist keiner mehr zu Hause, aber es sind noch Lampen an. Möchtest Du sie ausschalten?", "Reply_Lights", "Ja", "Nein")
    }
end
Aber egal, das soll hier ja gar nicht Thema sein... Vielen Dank auf jeden Fall für die Anregungen! Ich schau mir das morgen mal an. Ist nicht die wichtigste rule, da ich seit 1,5 Jahren eh kaum noch Auto fahre...

Re: Joda vs. Java time

Verfasst: 22. Aug 2021 17:44
von udo1toni
Nein, das ist schon korrekt, das sind globale Variablen (bzw. in diesem Fall eine globale Konstante). Das Problem ist an dieser Stelle aber, dass das Objekt eigentlich nicht als globales Objekt erlaubt ist. Unter OH2.5 hat das aber tatsächlich auch funktioniert.
Etwas wie

Code: Alles auswählen

var Timer tTimer = null

rule "Timer benutzen"
when
    Item bla changed
then
    if(tTimer !== null)
        return;
    tTimer = createTimer(now.plusSeconds(1), [ |
        tTimer = null
    ])
end
sollte aber funktionieren. Auch die Definition von gewöhnlichen globalen Konstanten sollte wie gewohnt arbeiten. Die action ist aber halt keine gewöhnliche Konstante :)

Re: Joda vs. Java time

Verfasst: 5. Nov 2021 17:22
von klausO
Hallo zusammen,
auch ich hadere mit der Umstellung der Zeitsyntax von Openhab2.5 auf Openhab3... ;-)
Da ich kein Programmierer sondern eher copyandpaster und auch nur beschränkt allgorithmisch denkfähig bin, bin ich natürlich immer auf der Suche nach Code-Schnipseln, die ich weitgehend verstehe und für meine Bedürfnisse abwandeln kann.
Bei mir geht es um eine Regel, die ab einer bestimmten "Dunkelheit" aber nur zwischen 14:30 Uhr und 23:40 Uhr in betimmten Zimmern das Licht einschaltet.
In Openhab2.5 war das einfach so zu lösen:

Code: Alles auswählen

if (now.isAfter(now.withTimeAtStartOfDay.plusHours(15).plusMinutes(0)) && now.isBefore(now.withTimeAtStartOfDay.plusHours(23).plusMinutes(40)))
Nachdem ich (fast) alle Beiträge zu dem Thema, sowohl hier als auch im englischsprachigen Forum gelesen hab, war ich sehr froh, hier die Zeile von udo1toni zu lesen:

Code: Alles auswählen

if(now.get(SECOND_OF_DAY) >= 15*60*60 + 0*60 + 0 && now.get(SECOND_OF_DAY) < 19*60*60 + 0*60 + 0) {   //zwischen 15 und 19 Uhr
Die hab ich dann so abgewandelt in meine Regel eingebaut:

Code: Alles auswählen

if ((WG_LA_automat_licht.state == ON && (now.get(SECOND_OF_DAY) >= 14*60*60 + 30*60 + 0 && now.get(SECOND_OF_DAY) < 23*60*60 + 30*60 + 0)) && ((GZ_BW_hell_devolo_node21.state < 45) || (AZ_BW_hell_node13_philio.state < 5)))
(ok - ich geb´s zu - ein ziemlicher Bedingungsschlauch.)
Wenn die Regel dann ausgeführt wird, sagt mir das Log allerdings:

Code: Alles auswählen

2021-11-03 19:25:06.918 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'licht_wenn_zu_dunkel_24-2' failed: The name 'SECOND_OF_DAY' cannot be resolved to an item or type; line 12, column 51, length 13 in licht_wenn_zu_dunkel_24
Ich wäre echt dankbar, wenn jemand ne Idee hätte, wie ich das korrekt hinbekomme und diese Baustelle zumachen könnte...;-)
Grüße
Klaus

Re: Joda vs. Java time

Verfasst: 5. Nov 2021 21:37
von peter-pan
klausO hat geschrieben: 5. Nov 2021 17:22 Ich wäre echt dankbar, wenn jemand ne Idee hätte,
....ein kleines Schnipselchen von mir:

Code: Alles auswählen

var vSecond = now.toLocalTime().toSecondOfDay()
logInfo("test","Sekunde: Es sind: {}", vSecond)
Log:

Code: Alles auswählen

2021-11-05 21:25:33.779 [INFO ] [org.openhab.core.model.script.test  ] - Sekunde: Es sind: 77133
Vielleicht geht es ja damit, indem du "now.get(SECOND_OF_DAY)" durch " now.toLocalTime().toSecondOfDay()" auswechselst.

Re: Joda vs. Java time

Verfasst: 5. Nov 2021 23:58
von int5749
peter-pan hat geschrieben: 5. Nov 2021 21:37 Vielleicht geht es ja damit, indem du "now.get(SECOND_OF_DAY)" durch " now.toLocalTime().toSecondOfDay()" auswechselst.
Welchen Zweck haben die Klammern??
Bei mir reicht ein

Code: Alles auswählen

now.toLocalTime.toSecondOfDay >= 30600
Viele Grüße

Re: Joda vs. Java time

Verfasst: 6. Nov 2021 01:21
von peter-pan
int5749 hat geschrieben: 5. Nov 2021 23:58 Bei mir reicht ein
Das ist gut zu wissen.
Ich hab das mit den Klammern mal irgendwo in einem Java-Tutorial gefunden. Das ist wohl Standard-Syntax. Schaden kann's ja aber wohl nicht ;)

Re: Joda vs. Java time

Verfasst: 6. Nov 2021 15:01
von klausO
Hallo peter-pan und int5749,
vielen Dank für eure schnellen und guten Tipps/Schnipsel!!!
Ich hab es jetzt mit einer Kombination von euren und udos Schnipsel gelöst:

Code: Alles auswählen

(now.toLocalTime.toSecondOfDay >= 14*60*60 + 30*60 + 0 && now.toLocalTime.toSecondOfDay < 23*60*60 + 30*60 + 0))
In der Form sind die 14:30 Uhr und die 23:30 Uhr noch wenigstens halbwegs sichtbar und das Log schimpft nicht mehr... ;-)
btw. durch die oben beschriebene Abhängigkeit von der "Dunkelheit", ist der Zeitpunkt, wann das Licht eingeschaltet wird immer schön zufällig.
Was auch z.B. während eines Urlaubs Anwesenheit simuliert.
Leider ist der Ausschaltzeitpunkt immer 23:30 Uhr - wenn hier jemand eine Idee hätte, wie man das mit einer Art Zufallsgenerator plus-minus 30 Minuten einstellen könnte, wäre die Regel perfekt.
Jetzt freue ich mich aber erstmal über den komplett vollzogenen Umzug von 2.5 nach 3.2 - alles läuft wie vorher nur, wie ich finde, etwas stabiler!

Nochmals Danke für eure prompte Unterstützung!
Klaus

Re: Joda vs. Java time

Verfasst: 6. Nov 2021 21:28
von peter-pan
... und noch 2 Schnipsel:

Schnipsel 1:

Code: Alles auswählen

   val java.util.Random rand = new java.util.Random()
   var int vRandomNumber = rand.nextInt(60)
   logInfo("randomTest","Neuer Wert randomnumber: {} randomTime.", vRandomNumber )  
Schnipsel 2:

Code: Alles auswählen

   val vMin = -30
   val vMax = 30
   var vRandom = (Math::random * (vMax - vMin)).intValue + vMin
   logInfo("randomTest","Neuer Wert vRandom: {} randomTime.", vRandom)  
Mit beiden Schnipseln kannst du dir Zufallswerte generieren und die Variablen in deiner Rule einbauen. Ich weiss jetzt aber nicht, ob das mathematisch so alles korrekt ist ;) . Aber es scheint zu funktionieren. :lol: :lol:

Edit: Also anstelle von 30 * 60 + 0 kommt dann 30 * 60 + vRandom
oder anstelle von 30 * 60 + 0 kommt vRandomNumber * 60 + 0