cron Ausdruck und Visual Studio Code
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
Hallo udo1toni,
zunächst nochmal vielen Dank, unglaublich wie Du dich für "fremde" User engagierst!
Ich brauche etwas länger, bis ich das ganz kapiert habe (bin auch schon 70), aber ich werde das testen.
Die Items werde ich zunächst an meine sitemap anpassen, da ich das ja für drei Zimmer brauche und auch die Temperaturwähler integrieren muss.
zunächst nochmal vielen Dank, unglaublich wie Du dich für "fremde" User engagierst!
Ich brauche etwas länger, bis ich das ganz kapiert habe (bin auch schon 70), aber ich werde das testen.
Die Items werde ich zunächst an meine sitemap anpassen, da ich das ja für drei Zimmer brauche und auch die Temperaturwähler integrieren muss.
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
Es gibt 44 Probleme in der Rule, soll ich mal einen Screenshot senden?
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
Zeile 15
Code: Alles auswählen
hour = if(SchaltUhr.filter[u|u.name.contains(n.name + "_H")].head.state instanceof Number) SchaltUhr.filter[u|u.name.contains(n.name + "_H")].head.state else null // falls gültige Stunde, übernehmen
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
zweite Hälfte:
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
- udo1toni
- Beiträge: 13864
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: cron Ausdruck und Visual Studio Code
Oha...
Das muss ich mir dann doch mal genauer anschauen...
Das muss ich mir dann doch mal genauer anschauen...
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
Aktuell läuft ja noch meine "alte" Rule.
Nachdem ich jetzt abfrage, ob der timer in der Vergangenheit liegt, funktioniert es sehr gut.
Es gibt aber Fehlermeldungen:
Ist es möglich, dass der Inhalt der items UhrX_M_XXX_WZ als .state NULL erkannt werden, wenn 0 Minuten eingetragen ist? Trotzdem wird das Lambda abgearbeitet.
Nachdem ich jetzt abfrage, ob der timer in der Vergangenheit liegt, funktioniert es sehr gut.
Es gibt aber Fehlermeldungen:
Code: Alles auswählen
2018-10-31 15:00:00.569 [ERROR] [org.quartz.core.ErrorLogger ] - Job (DEFAULT.2018-10-31T15:00:00.000+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
org.eclipse.xtext.xbase.impl.XIfExpressionImpl@18adaf9
<null>.time2_Ein_WZ = <XNullLiteralImplCustom>
} ] threw an exception.
- udo1toni
- Beiträge: 13864
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: cron Ausdruck und Visual Studio Code
Nein, null != NULL != 0.
null -> Inhalt einer Variablen, solange diese noch keine Zuweisung erhalten hat (oder alternativ wenn explizit null zugewiesen wurde)
NULL -> Status eines Items, solange es noch keinen gültigen Status hat (oder alternativ wenn per postUpdate(NULL) zugewiesen wurde)
0 -> eine gültige Zahl vom Typ Number (oder auch DecimalType)
null -> Inhalt einer Variablen, solange diese noch keine Zuweisung erhalten hat (oder alternativ wenn explizit null zugewiesen wurde)
NULL -> Status eines Items, solange es noch keinen gültigen Status hat (oder alternativ wenn per postUpdate(NULL) zugewiesen wurde)
0 -> eine gültige Zahl vom Typ Number (oder auch DecimalType)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet
- udo1toni
- Beiträge: 13864
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: cron Ausdruck und Visual Studio Code
So. Ich hab jetzt mal meinen Code getestet
Ich hatte noch eine Kleinigkeit vergessen, nämlich einen einfachen lock einzubauen, da die 2. Rule immer nur mit einer Instanz laufen darf, potenziell aber mehrfach getriggert wird.
Ich habe den Code für die bessere Lesbarkeit noch etwas umformuliert, außerdem schreibt die Rule für die Umrechnung von Stunden und Minuten auf Tagesminuten nur dann ein Item um, wenn es auch eine Änderung dieses Item betreffend gab. Vorher wurden einfach immer alle Schaltzeiten neu berechnet.
Ich hab noch ein bisschen was geändert und vor allem ein paar Fehler beseitigt, das passiert mir leider regelmäßig, dass ich irgendwelche Schlüsselworte weg lasse...
Da Du das Ganze für mehrere Heizkreise nutzen willst, müsste man die Rules noch ein wenig erweitern - es reicht nicht, nur ein paar zusätzliche Items dazu zu basteln.
Ich hatte noch eine Kleinigkeit vergessen, nämlich einen einfachen lock einzubauen, da die 2. Rule immer nur mit einer Instanz laufen darf, potenziell aber mehrfach getriggert wird.
Ich habe den Code für die bessere Lesbarkeit noch etwas umformuliert, außerdem schreibt die Rule für die Umrechnung von Stunden und Minuten auf Tagesminuten nur dann ein Item um, wenn es auch eine Änderung dieses Item betreffend gab. Vorher wurden einfach immer alle Schaltzeiten neu berechnet.
Code: Alles auswählen
var NumberItem myItem // Name des aktuellen Timers
var Timer nextTimer = null // der Timer
var Boolean lock = false
rule "Zeiten berechnen"
when
Time cron " 0 0 0 * * ?" or // Mitternacht
System started or // Neustart oder Rules neu geladen
Member of SchaltUhr changed // oder Schaltzeit geändert
then
logInfo("timer","Zeiten berechnen")
SchaltMinute.members.forEach[n|
var Number hour
var Number minute
var Number minutes
if(SchaltUhr.members.filter[u|u.name.contains(n.name + "_H")].head.state instanceof Number)
hour = SchaltUhr.members.filter[u|u.name.contains(n.name + "_H")].head.state as Number
else
hour = null
if(SchaltUhr.members.filter[u|u.name.contains(n.name + "_M")].head.state instanceof Number)
minute = SchaltUhr.members.filter[u|u.name.contains(n.name + "_M")].head.state as Number
else
minute = null
if(hour !== null && minute !== null)
minutes = hour * 60 + minute
else
minutes = null
logInfo("timer","Schaltzeit {} auf {}:{} Uhr ({}) gesetzt.",n.name,hour,minute,hour * 60 + minute)
if(minutes !== null)
if(n.state instanceof Number) {
if((n.state as Number) != minutes)
postUpdate(n.name.toString, minutes.toString)
}
else
postUpdate(n.name.toString, minutes.toString)
else if(n.state instanceof Number)
postUpdate(n.name.toString, "NULL") // Falls gültige Zeit, eintragen
]
logInfo("timer","Zeiten berechnen fertig")
end
rule "Timer anlegen"
when
Member of SchaltMinute received update or // Schaltzeit getriggert
Item planTimer received command // Nachtriggern (nächsten Timer anlegen)
then
if(lock) return;
lock= true
logInfo("timer","Timer anlegen")
if(nextTimer !== null) nextTimer.cancel // alten Timer löschen, falls noch aktiv
myItem = SchaltMinute.members.filter[n|n.state instanceof Number].filter[m|(m.state as Number) > now.getMinuteOfDay].sortBy[(state as Number).intValue].head as NumberItem
//sortBy[state].head // nur der nächste -> Namen merken
logInfo("timer","Timer anlegen für {} ({})",myItem.name,myItem.state)
nextTimer = createTimer(now.withTimeAtStartOfDay.plusMinutes((myItem.state as Number).intValue),[ // nur der nächste
logInfo("timer","Timer ausgelöst: {}",myItem.name)
if(Schalter_manu_BZ.state != OFF) { // falls manueller Betrieb
logInfo("timer","Aber manueller Betrieb, Abbruch!")
planTimer.sendCommand(OFF) // nächsten Timer planen
return; // und Schluss
}
val Number soll = if(myItem.name.contains("Ein")) 11 else 1 // Sollzustand nach Timer
logInfo("timer","geforderter Schaltvorgang: {}",soll)
if ((myItem.name.contains("1")) || // Soll der Timer berücksichtigt werden?
(myItem.name.contains("2") && SW_2_BZ.state == ON) ||
(myItem.name.contains("6") && SW_Sa_BZ.state == ON && now.getDayOfWeek == 6) ||
(myItem.name.contains("7") && SW_So_BZ.state == ON) && now.getDayOfWeek == 7) {
logInfo("timer","Schaltvorgang aktiv")
if((Thermostat_BZ.state instanceof Number)) {
if((Thermostat_BZ.state as Number) != soll)
Thermostat_BZ.sendCommand(soll)
else
logInfo("timer","aber Zustand bereits erreicht")
} else {
logInfo("timer","initialisiere Thermostat_BZ")
Thermostat_BZ.sendCommand(soll)
}
} else {
logInfo("timer","Schaltvorgang inaktiv")
}
planTimer.sendCommand(OFF)
])
logInfo("timer","Timer anlegen fertig")
lock = false
end
Da Du das Ganze für mehrere Heizkreise nutzen willst, müsste man die Rules noch ein wenig erweitern - es reicht nicht, nur ein paar zusätzliche Items dazu zu basteln.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet
-
- Beiträge: 347
- Registriert: 10. Mai 2018 09:46
Re: cron Ausdruck und Visual Studio Code
Zur NULL Problematik.
Ich bekomme immer den oben genannten Fehler beim Abarbeiten des Lambdas, aber nur die "AUS" timer.
Im Log ist das Item tatsächlich leer, obwohl es in der Sitemap als 0 erscheint und damit ja einen gültigen Status haben sollte.
Es gibt auch keine Zuweisungen.
Aus der sitemap sollte es ja auch nicht kommen:
Deinen neuen Code werde ich testen, dann brauche ich das oben vielleicht nicht mehr, aber verstehen wollte ich es gerne.
Ich bekomme immer den oben genannten Fehler beim Abarbeiten des Lambdas, aber nur die "AUS" timer.
Im Log ist das Item tatsächlich leer, obwohl es in der Sitemap als 0 erscheint und damit ja einen gültigen Status haben sollte.
Es gibt auch keine Zuweisungen.
Aus der sitemap sollte es ja auch nicht kommen:
Code: Alles auswählen
Setpoint item=Uhr1_M_Aus_WZ label="1.Ausschaltzeit Minuten [: %s Min]" icon="time" minValue=0 maxValue=59 step=15 labelcolor=[>=0="red"]
- udo1toni
- Beiträge: 13864
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: cron Ausdruck und Visual Studio Code
Keine Ahnung, hier sehe ich keine solchen Fehler.
Ich habe bereits die nächste Version fertig, weil es mir keine Ruhe gelassen hat
Weil aber die Rules eh schon komplex genug sind, habe ich mir gedacht, man kann sich das Leben auch unnötig kompliziert machen... Also etwas entschlacken und bestimmte Dinge als gegeben voraussetzen!
Wir benötigen eine Persistence, die alle Items auf eine gültige Zahl bringt, falls das System mal neu gestartet wird, am besten eignet sich hierzu mapDB, da sie praktisch keinen Platz braucht (wird über Paper UI Config->Addons->Persistence->mapDB installiert)
mapDB braucht keine spezielle Konfiguration, aber natürlich müssen wir dem Service mitteilen, welche Items er persistieren soll.
Die zugehörige Konfigurationsdatei für die Persistence, persistence/mapdb.persist:
Die zugehörigen Items:
Ich habe die Items so sortiert, dass man nur den gesamten Block "2. Heizkreis" kopieren muss, um einen weiteren Heizkreis anzulegen. Zu ändern ist dann nur die Vorsilbe bei allen Items, und natürlich die Gruppenzugehörigkeit der Zeitspeicher.
Da ich in den Rules keine Vorkehrungen mehr treffe, ungültige Werte abzufangen, ist nun der Zeitpunkt gekommen, jedem Stellitem einen gültigen Wert zuzuweisen. Meine Sitemap hierfür:
Kann man natürlich auch anders gestalten. für jeden Heizkreis muss der entsprechende Block mit rein, damit alle Stellitems (und die zugehörigen Schalter) von Beginn an einen gültigen Status haben. Der Thermostat ist nur für die Simulation mit drin.
Nun wird die erste Rule in Betrieb genommen, um alle Zeitspeicher zu füllen:
Nun reicht es, einmalig ein Stellitem zu ändern, um die Rule zu triggern. Im Anschluss müssen alle Zeitspeicher einen gültigen Wert haben (wird geloggt).
Jetzt könne wir die 2. Rule in Betrieb nehmen:
Ich habe mich bemüht, den Code so zu gestalten, dass klar ist, wo was anzupassen ist. Einfach den Block EG_Timer kopieren und hinten einfügen, als Block vor dem Rahmencode.
Wenn die Ruledatei neu geladen wird, triggert die 2. Rule automatisch und prüft auf heute auszuführende Timer. Dabei bekommt jeder Heizkreis seinen eigenen Timer.
Da ich nicht mehr auf NULL oder null prüfe, braucht ein Timer keine 30 Zeilen Code, mit sämtlichen Schaltern usw. Das bedeutet aber auch, dass jederzeit alle (!) Items einen gültigen Status haben müssen, sonst kracht die Rule. Wenn die Rule abschmiert, bleibt der lock hängen, das heißt, die Rule wird erst dann wieder ausgeführt, wenn die Ruledatei mindestens neu gespeichert wurde (dabei wird lock auf false gesetzt).
Mit entsprechendem Aufwand könnte die 2. Rule auch noch so konzipiert werden, dass sie für alle Heizkreise funktioniert, allerdings ist das um einiges aufwändiger, und ich denke, 40 Zeilen Code als Rumpf (inklusive 1. Rule) + 29 Zeilen Code pro Heizkreis sind ok für die gebotene Funktionalität schließlich hatte der ursprüngliche Code ca. 200 Zeilen Code pro Heizkreis...
Ich habe bereits die nächste Version fertig, weil es mir keine Ruhe gelassen hat
Weil aber die Rules eh schon komplex genug sind, habe ich mir gedacht, man kann sich das Leben auch unnötig kompliziert machen... Also etwas entschlacken und bestimmte Dinge als gegeben voraussetzen!
Wir benötigen eine Persistence, die alle Items auf eine gültige Zahl bringt, falls das System mal neu gestartet wird, am besten eignet sich hierzu mapDB, da sie praktisch keinen Platz braucht (wird über Paper UI Config->Addons->Persistence->mapDB installiert)
mapDB braucht keine spezielle Konfiguration, aber natürlich müssen wir dem Service mitteilen, welche Items er persistieren soll.
Die zugehörige Konfigurationsdatei für die Persistence, persistence/mapdb.persist:
Code: Alles auswählen
Strategies {
default=everyChange,restoreOnStartup
}
Items {
SchaltUhr* : strategy = everyChange,restoreOnStartup // Alle Member von SchaltUhr
BZ_SchaltMinute* : strategy = everyChange,restoreOnStartup // Alle Member von BZ_SchaltMinute
EG_SchaltMinute* : strategy = everyChange,restoreOnStartup // Alle Member von EG_SchaltMinute
Thermo* : strategy = everyChange,restoreOnStartup // Alle Member von Thermo
}
Code: Alles auswählen
// Hilfsitems
Group SchaltUhr // Alle Stell-Items
Group SchaltMinute // Alle Schaltzeiten
Group Thermo // Die restlichen zu persitierenden Items (Schalter usw.)
Switch planTimer // ein Trigger
// 1. Heizkreis
Group BZ_SchaltMinute (SchaltMinute) // Schaltzeiten BZ
// Stellitems
Number BZ_Uhr1_Ein_H "Uhr1 Ein Stunde [%d]" (SchaltUhr)
Number BZ_Uhr1_Ein_M "Uhr1 Ein Minute [%d]" (SchaltUhr)
Number BZ_Uhr1_Aus_H "Uhr1 Aus Stunde [%d]" (SchaltUhr)
Number BZ_Uhr1_Aus_M "Uhr1 Aus Minute [%d]" (SchaltUhr)
Number BZ_Uhr2_Ein_H "Uhr2 Ein Stunde [%d]" (SchaltUhr)
Number BZ_Uhr2_Ein_M "Uhr2 Ein Minute [%d]" (SchaltUhr)
Number BZ_Uhr2_Aus_H "Uhr2 Aus Stunde [%d]" (SchaltUhr)
Number BZ_Uhr2_Aus_M "Uhr2 Aus Minute [%d]" (SchaltUhr)
Number BZ_Uhr6_Aus_H "Uhr6 Aus Stunde [%d]" (SchaltUhr)
Number BZ_Uhr6_Aus_M "Uhr6 Aus Minute [%d]" (SchaltUhr)
Number BZ_Uhr7_Aus_H "Uhr7 Aus Stunde [%d]" (SchaltUhr)
Number BZ_Uhr7_Aus_M "Uhr7 Aus Minute [%d]" (SchaltUhr)
// Zeitspeicher
Number BZ_Uhr1_Ein (BZ_SchaltMinute)
Number BZ_Uhr1_Aus (BZ_SchaltMinute)
Number BZ_Uhr2_Ein (BZ_SchaltMinute)
Number BZ_Uhr2_Aus (BZ_SchaltMinute)
Number BZ_Uhr6_Aus (BZ_SchaltMinute)
Number BZ_Uhr7_Aus (BZ_SchaltMinute)
// restliche Items (Schalter plus Thermostat)
Switch BZ_Auto "BZ Automatik" (Thermo)
Switch BZ_SW_2 "2. Zeit" (Thermo)
Switch BZ_SW_Sa "Samstag Zeit" (Thermo)
Switch BZ_SW_So "Sonntag Zeit" (Thermo)
Number BZ_Thermostat "Nummer [%d]" (Thermo) // muss eventuell nicht persistiert werden? Aber für die Simulation!
// 2. Heizkreis
Group EG_SchaltMinute (SchaltMinute) // Schaltzeiten EG
// Stellitems
Number EG_Uhr1_Ein_H "Uhr1 Ein Stunde [%d]" (SchaltUhr)
Number EG_Uhr1_Ein_M "Uhr1 Ein Minute [%d]" (SchaltUhr)
Number EG_Uhr1_Aus_H "Uhr1 Aus Stunde [%d]" (SchaltUhr)
Number EG_Uhr1_Aus_M "Uhr1 Aus Minute [%d]" (SchaltUhr)
Number EG_Uhr2_Ein_H "Uhr2 Ein Stunde [%d]" (SchaltUhr)
Number EG_Uhr2_Ein_M "Uhr2 Ein Minute [%d]" (SchaltUhr)
Number EG_Uhr2_Aus_H "Uhr2 Aus Stunde [%d]" (SchaltUhr)
Number EG_Uhr2_Aus_M "Uhr2 Aus Minute [%d]" (SchaltUhr)
Number EG_Uhr6_Aus_H "Uhr6 Aus Stunde [%d]" (SchaltUhr)
Number EG_Uhr6_Aus_M "Uhr6 Aus Minute [%d]" (SchaltUhr)
Number EG_Uhr7_Aus_H "Uhr7 Aus Stunde [%d]" (SchaltUhr)
Number EG_Uhr7_Aus_M "Uhr7 Aus Minute [%d]" (SchaltUhr)
// Zeitspeicher
Number EG_Uhr1_Ein (EG_SchaltMinute)
Number EG_Uhr2_Ein (EG_SchaltMinute)
Number EG_Uhr1_Aus (EG_SchaltMinute)
Number EG_Uhr2_Aus (EG_SchaltMinute)
Number EG_Uhr6_Aus (EG_SchaltMinute)
Number EG_Uhr7_Aus (EG_SchaltMinute)
// restliche Items (Schalter plus Thermostat)
Switch EG_Auto "EG Automatik" (Thermo)
Switch EG_SW_2 "2. Zeit" (Thermo)
Switch EG_SW_Sa "Samstag Zeit" (Thermo)
Switch EG_SW_So "Sonntag Zeit" (Thermo)
Number EG_Thermostat "Nummer [%d]" (Thermo) // muss eventuell nicht persistiert werden? Aber für die Simulation!
Da ich in den Rules keine Vorkehrungen mehr treffe, ungültige Werte abzufangen, ist nun der Zeitpunkt gekommen, jedem Stellitem einen gültigen Wert zuzuweisen. Meine Sitemap hierfür:
Code: Alles auswählen
sitemap test label="Test Sitemap" {
Frame label="Schaltuhren" {
Text label="BZ" {
Frame label="BZ Uhr" icon="icon" {
Setpoint item=BZ_Uhr1_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr1_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=BZ_Uhr1_Ein_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr1_Ein_M minValue=0 maxValue=59 step=1
Setpoint item=BZ_Uhr2_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr2_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=BZ_Uhr2_Ein_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr2_Ein_M minValue=0 maxValue=59 step=1
Setpoint item=BZ_Uhr6_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr6_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=BZ_Uhr7_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=BZ_Uhr7_Aus_M minValue=0 maxValue=59 step=1
}
Frame label="BZ Aktivieren" {
Default item=BZ_Auto label="BZ Automatik"
Default item=BZ_SW_2 label="2. Zeit"
Default item=BZ_SW_Sa label="Samstag Zeit"
Default item=BZ_SW_So label="Sonntag Zeit"
Setpoint item=BZ_Thermostat label="Nummer"
}
}
Text label="EG" {
Frame label="EG Uhr" icon="icon" {
Setpoint item=EG_Uhr1_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr1_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=EG_Uhr1_Ein_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr1_Ein_M minValue=0 maxValue=59 step=1
Setpoint item=EG_Uhr2_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr2_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=EG_Uhr2_Ein_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr2_Ein_M minValue=0 maxValue=59 step=1
Setpoint item=EG_Uhr6_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr6_Aus_M minValue=0 maxValue=59 step=1
Setpoint item=EG_Uhr7_Aus_H minValue=0 maxValue=23 step=1
Setpoint item=EG_Uhr7_Aus_M minValue=0 maxValue=59 step=1
}
Frame label="EG Aktivieren" {
Default item=EG_Auto label="EG Automatik"
Default item=EG_SW_2 label="2. Zeit"
Default item=EG_SW_Sa label="Samstag Zeit"
Default item=EG_SW_So label="Sonntag Zeit"
Setpoint item=EG_Thermostat label="Nummer"
}
}
}
}
Nun wird die erste Rule in Betrieb genommen, um alle Zeitspeicher zu füllen:
Code: Alles auswählen
var Boolean lock = false // lock für 2. Rule
// 1. Heizkreis, für 2.Rule
var NumberItem BZ_Item // Item des aktuellen BZ_Timers
var Timer BZ_Timer = null // der BZ_Timer
//2. Heizkreis, für 2.Rule
var NumberItem EG_Item // Item des aktuellen EG_Timers
var Timer EG_Timer = null // der EG_Timer
rule "Zeiten berechnen"
when
Member of SchaltUhr changed // Schaltzeit geändert
then
logInfo("timer","Zeiten berechnen")
SchaltMinute.allMembers.forEach[n| // Alle Schaltzeiten aller Heizkreise
var Number hour
var Number minute
var Number minutes
hour = SchaltUhr.members.filter[u|u.name.contains(n.name + "_H")].head.state as Number // Stunde des korrespondierenden StellItems
minute = SchaltUhr.members.filter[u|u.name.contains(n.name + "_M")].head.state as Number // Stunde des korrespondierenden StellItems
minutes = hour * 60 + minute // Sollzeit in Minuten
logInfo("timer","Schaltzeit {} auf {}:{} Uhr ({}) gesetzt.",n.name,hour,minute,hour * 60 + minute)
postUpdate(n.name.toString, minutes.toString) // im passenden Item speichern
]
logInfo("timer","Zeiten berechnen fertig")
end
Jetzt könne wir die 2. Rule in Betrieb nehmen:
Code: Alles auswählen
rule "Timer anlegen"
when
Time cron " 0 0 0 * * ?" or // Mitternacht
System started or // Neustart oder Rules neu geladen
Member of BZ_SchaltMinute changed or // Schaltzeit geändert
Member of EG_SchaltMinute changed or // Schaltzeit geändert
Item planTimer received command // Timertrigger (nächsten Timer anlegen)
then
if(lock) return; // Rule bereits aktiv?
lock= true // Rule sperren
logInfo("timer","Timer anlegen")
// ab hier BZ_Timer
if(BZ_Timer !== null) BZ_Timer.cancel // alten BZ_Timer löschen, falls aktiv
BZ_Item = BZ_SchaltMinute.members.filter[m|(m.state as Number) > now.getMinuteOfDay].sortBy[
(state as Number).intValue].head as NumberItem // Nächsten BZ_Timer ermitteln
if(BZ_Item !== null) { // noch mindestens ein Timer heute
logInfo("timer","Timer anlegen für {} ({})",BZ_Item.name,BZ_Item.state)
BZ_Timer = createTimer(now.withTimeAtStartOfDay.plusMinutes((BZ_Item.state as Number).intValue),[ // Timer anlegen
logInfo("timer","Timer ausgelöst: {}",BZ_Item.name)
if(BZ_Auto.state != ON) { // falls manueller Betrieb
logInfo("timer","Aber manueller Betrieb, Abbruch!")
planTimer.sendCommand(OFF) // nächsten BZ_Timer planen
return; // und Schluss
}
val Number soll = if(BZ_Item.name.contains("Ein")) 11 else 1 // Sollzustand nach BZ_Timer
logInfo("timer","geforderter Schaltvorgang: {}",soll)
if ((BZ_Item.name.contains("1")) || // Soll der Timer berücksichtigt werden?
(BZ_Item.name.contains("2") && BZ_SW_2.state == ON) ||
(BZ_Item.name.contains("6") && BZ_SW_Sa.state == ON && now.getDayOfWeek == 6) ||
(BZ_Item.name.contains("7") && BZ_SW_So.state == ON) && now.getDayOfWeek == 7) {
logInfo("timer","Schaltvorgang aktiv")
if((BZ_Thermostat.state as Number) != soll) // Aktueller Status abweichend?
BZ_Thermostat.sendCommand(soll)
else
logInfo("timer","aber Zustand bereits erreicht")
} else {
logInfo("timer","Schaltvorgang inaktiv")
}
planTimer.sendCommand(OFF) // nächsten BZ_Timer planen
])
}
// ab hier EG_Timer
if(EG_Timer !== null) EG_Timer.cancel
EG_Item = EG_SchaltMinute.members.filter[m|(m.state as Number) > now.getMinuteOfDay].sortBy[
(state as Number).intValue].head as NumberItem
if(EG_Item !== null) {
logInfo("timer","Timer anlegen für {} ({})",EG_Item.name,EG_Item.state)
EG_Timer = createTimer(now.withTimeAtStartOfDay.plusMinutes((EG_Item.state as Number).intValue),[
logInfo("timer","Timer ausgelöst: {}",EG_Item.name)
if(EG_Auto.state != ON) {
logInfo("timer","Aber manueller Betrieb, Abbruch!")
planTimer.sendCommand(OFF)
return;
}
val Number soll = if(EG_Item.name.contains("Ein")) 11 else 1
logInfo("timer","geforderter Schaltvorgang: {}",soll)
if ((EG_Item.name.contains("1")) ||
(EG_Item.name.contains("2") && EG_SW_2.state == ON) ||
(EG_Item.name.contains("6") && EG_SW_Sa.state == ON && now.getDayOfWeek == 6) ||
(EG_Item.name.contains("7") && EG_SW_So.state == ON && now.getDayOfWeek == 7)) {
logInfo("timer","Schaltvorgang aktiv")
if((EG_Thermostat.state as Number) != soll)
EG_Thermostat.sendCommand(soll)
else
logInfo("timer","aber Zustand bereits erreicht")
} else {
logInfo("timer","Schaltvorgang inaktiv")
}
planTimer.sendCommand(OFF)
])
}
// ab hier Rahmencode
logInfo("timer","Timer anlegen fertig")
lock = false
end
Wenn die Ruledatei neu geladen wird, triggert die 2. Rule automatisch und prüft auf heute auszuführende Timer. Dabei bekommt jeder Heizkreis seinen eigenen Timer.
Da ich nicht mehr auf NULL oder null prüfe, braucht ein Timer keine 30 Zeilen Code, mit sämtlichen Schaltern usw. Das bedeutet aber auch, dass jederzeit alle (!) Items einen gültigen Status haben müssen, sonst kracht die Rule. Wenn die Rule abschmiert, bleibt der lock hängen, das heißt, die Rule wird erst dann wieder ausgeführt, wenn die Ruledatei mindestens neu gespeichert wurde (dabei wird lock auf false gesetzt).
Mit entsprechendem Aufwand könnte die 2. Rule auch noch so konzipiert werden, dass sie für alle Heizkreise funktioniert, allerdings ist das um einiges aufwändiger, und ich denke, 40 Zeilen Code als Rumpf (inklusive 1. Rule) + 29 Zeilen Code pro Heizkreis sind ok für die gebotene Funktionalität schließlich hatte der ursprüngliche Code ca. 200 Zeilen Code pro Heizkreis...
Zuletzt geändert von udo1toni am 4. Nov 2018 05:50, insgesamt 1-mal geändert.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet