cron Ausdruck und Visual Studio Code

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

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.

hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

Es gibt 44 Probleme in der Rule, soll ich mal einen Screenshot senden?

hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

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.

hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

zweite Hälfte:
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

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

Re: cron Ausdruck und Visual Studio Code

Beitrag von udo1toni »

Oha...
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

hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

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:

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.
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.

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

Re: cron Ausdruck und Visual Studio Code

Beitrag von udo1toni »

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)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

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

Re: cron Ausdruck und Visual Studio Code

Beitrag von udo1toni »

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.

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
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.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

hardl
Beiträge: 347
Registriert: 10. Mai 2018 09:46
Answers: 1

Re: cron Ausdruck und Visual Studio Code

Beitrag von hardl »

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:

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"] 
Deinen neuen Code werde ich testen, dann brauche ich das oben vielleicht nicht mehr, aber verstehen wollte ich es gerne.

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

Re: cron Ausdruck und Visual Studio Code

Beitrag von udo1toni »

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:

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
}
Die zugehörigen Items:

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!
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:

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"
            }
        }
    }
}
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:

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
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:

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
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...
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

Antworten