shuo hat geschrieben: ↑17. Jan 2022 09:15
Kann Dir leider nicht ganz folgen.
udo1toni hat geschrieben: ↑15. Jan 2022 21:32
Schreibe eine Rule, welche die Auswertung vornimmt. Die wird über Item xy changed getriggert.
Das ist ja bereits der Fall.
Nein. Du hast eine Rule, welche einen Timer startet. Im Timer triggerst Du eine Abfrage und untersuchst unmittelbar die Items, in welchen irgendwann mal die Ergebnisse landen. Die aktuell vorliegenden Daten sind aber noch vom vorigen Durchlauf (also gewöhnlich 5 Minuten alt) Dass die Daten aktuell sind, ist ausgeschlossen, da die 2. Rule einige Millisekunden braucht, bis sie ausgeführt wird. Dann muss das Python Script laufen, welches ebenfalls eininge Millisekunden braucht (plus Antwortzeit des Renault Service).
Zu dem Zeitpunkt ist der Timer schon abgearbeitet.
shuo hat geschrieben: ↑17. Jan 2022 09:15
udo1toni hat geschrieben: ↑15. Jan 2022 21:32
Die Rule kann als Steuerwert auch ein Item setzen, welches dann die Zykluszeit des Timers darstellt.
Kannst mir das näher erklären?
Na, Du musst die Rules genau anders herum aufbauen:
Code: Alles auswählen
rule "update timespan and eventually stop charging"
when
Item RenaultZEServices_Zoe_ChargeLevel changed or
Item RenaultZEServices_Zoe_Charging_Target changed
then
var nChargeLevel = 0 // Default Wert, falls kein gültiger Wert
if(RenaultZEServices_Zoe_ChargeLevel.state instanceof Number) // falls gültiger Wert
nChargeLevel = (RenaultZEServices_Zoe_ChargeLevel.state as Number).floatValue // übernimm diesen Wert
var nChargeTarget = 0 // Default Wert, falls kein gültiger Wert
if(RenaultZEServices_Zoe_Charging_Target.state instanceof Number) // falls gültiger Wert
nChargeTarget = (RenaultZEServices_Zoe_Charging_Target.state as Number).floatValue // übernimm diesen Wert
if(nChargeLevel >= nChargeTarget) { // Ziel überschritten?
tMyTimer?.cancel // laufenden Timer abbrechen (falls vorhanden)
tMyTimer = null // Zeiger löschen
myTime.postUpdate(5) // Default Zyklus setzen
logInfo("RemainingCharingTarget", "Zoe set charging target achieved. Switching off at : {} %", nChargeTarget)
sendBroadcastNotification("Ladeziel erreicht. Schalte Ladevorgang ab bei: " + nChargeTarget.toString + "%" )
} else if(nChargeLevel > nChargeTarget - 4 && myTime.state != 2) { // weniger als 4 % bis zum Ladeziel
myTime.postUpdate(2) // Zyklus anpassen
tMyTimer.reschedule(now.plusMinutes(2)) // und Timer neu planen (optional)
}
end
rule "schedule charging timer"
when
Item KebaState changed or
Item KebaPower changed
then
if(KebaPower.state > 0 && KebaState.state == 3 && tMyTimer === null) // falls Timer nicht existiert und Zoe lädt
tMyTimer = createTimer(now.plusSeconds(1), [| // Timer anlegen und gleich starten
val tSched = if(myTime.state instanceof Number) (myTime.state as Number) else 0 // Zykluszeit in Minuten
val results_status = executeCommandLine(Duration.ofSeconds(30), "sudo", "-u", "openhabian", "/usr/local/bin/pyze", "status", "--km")
logInfo("charge", "results_Status Plugged {}", results_status)
val results_vehicle = executeCommandLine(Duration.ofSeconds(30),"sudo", "-u", "openhabian", "/usr/local/bin/pyze", "vehicles")
logInfo("charge", "results_Vehicle Plugged [}", results_vehicle)
if(tSched > 0) {
logInfo("charge","Timer wird in {} Minuten erneut ausgeführt!",tSched)
tMyTimer.reschedule(now.plusMinutes(tSched))
} else {
logInfo("charge","Timer wird nicht mehr ausgeführt!")
tMyTimer = null
}
])
end
Innerhalb des Timers werden nur die aktuellen Werte angefordert.
Ändern sich die Werte, so löst die 2. Rule aus, welche die Zykluszeit berechnet und den Ladeschluss erkennt.
Das Switch Item RenaultZEServices_Zoe_Refresh kann entfallen, dafür braucht es ein Number Item myTime, in welchem die Zykluszeit in Minuten hinterlegt wird.
Es können beliebig weitere Zeitspannen eingebaut werden, wenn der Ladestand z.B. um mehr als 20%, 40%, 60% abweicht, entsprechend z.B. alle 10 Minuten oder gar nur alle 20 Minuten.
Man könnte noch ein DateTime Item anlegen, welches innerhalb des Timers jeweils den aktuellen Zeitstempel erhält. Dann kann man eine Anzeige gestalten in der Art "Letzte Aktualisierung um xx:xx Uhr, nächste Aktualisierung nach yy Minuten". Oder man definiert ein String Item, in welchem man die Info in Klartext durch den Timer eintragen lässt ("Nächste Aktualisierung um xx:xx Uhr")
Code: Alles auswählen
val StringBuilder strNext = new StringBuilder
strNext.append("Nächste Aktualisierung um ")
strNext.append(String::format("%02d",now.plusMinutes(tSched).getHour))
strNext.append(":")
strNext.append(String::format("%02d",now.plusMinutes(tSched).getMinute))
strNext.append(" Uhr.")
myMessge.postUpdate(strNext.toString)
Das nur als Beispiel, man kann die Uhrzeit auch anders über String::format() erhalten, ich war jetzt aber zu faul, die korrekte Formatierung rauszusuchen.
Ich habe in den beiden Rules unterschiedliche Verfahren angewandt, den Status eines Number Items in einer Variablen bzw. Konstanten zu speichern, um ihn lokal zu nutzen. Bei Verfahren sind gleichwertig, solange man den Wert nicht innerhalb der Rule nochmals ändern will. Dann müsste man natürlich auf jeden Fall eine Variable statt einer Konstanten (var statt val) verwenden. Es ging mir hier eher darum, beide Möglichkeiten aufzuzeigen.
Der ternäre Operator
a = if(b) c else d ist halt sehr schlank, aber leider nicht ganz so eingängig wie
a = d; if(b) a = c. Der ternäre Operator ist zwingend, wenn man eine Konstante alternativ mit unterschiedlichen Werten belegen will.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet