Seite 1 von 1

Geht diese rule so?

Verfasst: 9. Nov 2018 09:31
von isostar1101
Hallo Zusammen, ich möchte mit dieser Rolle einfach das Flurlicht nach zwei Minuten ausschalten!


wie kann ich sagen das er das nur in der Zeit von Morgens 8 Uhr bis 16:30 Uhr durchgeführt werden soll und in der Zeit ab 16:30 Uhr bis 23:00 Uhr soll der Timer dann 10 Minuten laufen!

Danke für die Mühe! vG. Isostar



rule "Flurunten wird nach 2 Minuten ausgeschaltet"
when
Item GF_Flur_Licht received command ON
then
{
createTimer(now.plusSeconds(120)) [
sendCommand(GF_Flur_Licht, OFF)
]
}
end

Re: Geht diese rule so?

Verfasst: 9. Nov 2018 12:10
von udo1toni
Ja, die Rule wird so funktionieren. Allerdings gibt es da noch ein paar Dinge zu beachten. openHAB arbeitet komplett asynchron, das bedeutet auf Rules bezogen, die Rule kann beliebig oft hintereinander aufgerufen werden. Wenn Du nun spaßeshalber das Licht alle 10 Sekunden einschaltest (z.B. kommt der Einschaltimpuls von einem Bewegungsmelder), wird das Licht nach zwei Minuten ausgeschaltet, wenn Du es dann aber direkt wieder einschaltest, wird es spätestens zehn Sekunden später wieder ausgeschaltet, weil für jeden Einschaltimpuls ein unabhängiger Timer angelegt wurde.

Richtig geht es so:

Code: Alles auswählen

var Timer tLichtFlurGF = null                                                        // globale Variable für den Timer

rule "Flur unten wird nach 2 Minuten ausgeschaltet"
when
    Item GF_Flur_Licht received command ON
then
    if(tLichtFlurGF !== null) tLichtFlurGF.cancel
    tLichtFlurGF = createTimer(now.plusSeconds(120), [
        GF_Flur_Licht.sendCommand(OFF)
        tLichtFlurGF = null
    ])
end
Es gibt jetzt eine globale Variable, über die man Kontrolle über den Timer hat. Die Rule prüft, ob ein Timer aktiv ist (Variable ist nicht null), falls das der Fall ist, wird der Timer abgebrochen. Anschließend wird ein neuer Timer angelegt und der Variablen zugewiesen.

Deine Schreibweise für createTimer() (mit den eckigen Klammern nach dem createTimer() ) ist zwar korrekt, ich habe trotzdem eine andere Schreibweise gewählt (eckige Klammern innerhalb der Klammern als zweiter Parameter), weil dadurch klar ist, dass das Lambda (der Inhalt der eckigen Klammern) zu der Funktion gehört und nicht unabhängig davon ist.

Weiterhin habe ich die Methode GF_Flur_Licht.sendCommand(OFF) verwendet, anstatt der Action sendCommand(GF_Flur_Licht, OFF). Die Methode ist grundsätzlich die bessere Wahl, die Action erwartet als Argumente immer zwei Strings, während die Methode alles annehmen sollte, was an Typen sinnvoll ist. die Action macht also aus dem übergebenen Item einen String, der den Namen des Items enthält und aus dem übergebenen Status einen String, der den Status als Text enthält. Oft funktioniert das, unter bestimmten Umständen klappt diese Typwandlung aber nicht wie vorgesehen.

Wenn der Timer abläuft (also der Code im Lambda ausgeführt wird) wird zum Schluss die Variable reinitialisiert, so dass die Prüfung auf einen aktiven Timer wieder funktioniert.

Du möchtest das Ganze erweitert haben, so dass uhrzeitabhängig verschiedene Laufzeiten für den Timer verwendet werden, das ist einfach realisierbar. Allerdings hast Du nicht geschrieben, was von 23 Uhr bis 8 Uhr passieren soll. Ich setze da jetzt einfach mal eine dritte Schaltlänge ein:

Code: Alles auswählen

var Timer tLichtFlurGF = null                                                              // globale Variable für den Timer

rule "Flur unten wird nach x Minuten ausgeschaltet"
when
    Item GF_Flur_Licht received command ON
then
    if(tLichtFlurGF !== null) tLichtFlurGF.cancel                                          // falls ein Timer läuft, abbrechen
    var Integer iSekunden = 300                                                            // default Länge des Timers
    if(now.getMinuteOfDay >= 8 * 60 && now.getMinuteOfDay < 16 * 60 +30 ) iSekunden = 120  // zwischen 8 und 16:30 
    if(now.getMinuteOfDay >= 16 * 60+30 && now.getMinuteOfDay < 23 * 60 ) iSekunden = 600  // zwischen 16:30 und 23
    tLichtFlurGF = createTimer(now.plusSeconds(iSekunden), [
        GF_Flur_Licht.sendCommand(OFF)                                                     // Licht aus!
        tLichtFlurGF = null                                                                // Timer reinitialisieren
    ])
end
Ich habe also lediglich eine weitere Variable definiert, diesmal innerhalb der Rule. Beim Initialisieren bekommt sie einen Default von 300 zugewiesen.
Sollte die aktuelle Zeit zwischen 8:00 Uhr und 16:29:59.999 Uhr liegen, bekommt sie den Wert 120 zugewiesen. Sollte die aktuelle Zeit zwischen 16:30 Uhr und 22:59:59.999 Uhr liegen, bekommt sie den Wert 600 zugewiesen.
Anschließend verwende ich die Variable anstatt eines fixen Wertes.

Es gibt übrigens auch die Eigenschaften .getHourOfDay und .getMinuteOfHour, aber im Allgemeinen ist bei minutengenauen Angaben das .getMinuteOfDay bequemer. Man kann natürlich auch direkt 990 statt 16*60 +30 schreiben, so wird aber sofort klar, was die Zahl bedeutet.

Re: Geht diese rule so?

Verfasst: 11. Nov 2018 11:53
von isostar1101
Super und vielen Dank udo1toni für diese tolle Antwort funktioniert!



ich habe noch eine Frage zum Astrobinding, ist es richtig, dass sich wenn ich es wie unten mache, wir uns in der Dämmerung befinden und wie könnte ich diese Regel vereinfachen? Sorry fange gerade erst an! Lieben Dank!


rule "Bewegungsmelder Flur hat Bewegung erkannt und es ist Dunkel"

when
Item FF_Flur_Bewegungsmelder received update
then
if(astro_sun_home_civilDusk_start)
if(FF_Flur_Bewegungsmelder.state == ON && FF_Flur_Steckdoseoben.state == OFF){

sendCommand(FF_Flur_Steckdoseoben, ON)
}
end

rule "Bewegungsmelder Flur wird nach 2 Minuten ausgeschaltet"
when
Item FF_Flur_Steckdoseoben received command ON
then
if(FF_Flur_Bewegungsmelder.state == ON){
createTimer(now.plusSeconds(120)) [
sendCommand(FF_Flur_Steckdoseoben, OFF)
]
}
end

Re: Geht diese rule so?

Verfasst: 11. Nov 2018 14:27
von udo1toni
Es gibt zwei unterschiedliche Funktionen des Astro Bindings.
Zum einen gibt es normale Channel, die z.B. die Uhrzeit enthalten, zu der am aktuellen Tag ein bestimmtes astronomisches Ereignis eintritt. Diese Channel musst Du einem passenden Item zuweisen, dann kannst Du den Status des Items innnerhalb einer Rule abfragen.
Zum anderen gibt es event Channel, die zum Eintreten des Ereignisses einen Trigger senden, mit dem dann eine Rule getriggert werden kann.

Wenn Du einen Blick auf das Astro Thing "sun" wirfst, kannst Du z.B. verschiedenste Sonnen Auf- und Untergänge finden, jeweils mit Start, Ende, Dauer und dem Range Event, welches dann zum Startzeitpunkt mit START triggert, zum Endzeitpunkt mit END.
Start und Ende ergeben sich dadurch, dass das Ereignis als Berührung des Himmelskörpers mit einer gedachten Linie definiert ist. Die Sonne ist aber kein Punkt, sondern ein Kreis (zweidimensional betrachtet), so dass es einen Zeitpunkt gibt, zu dem die Berührung beginnt, und einen Zeitpunkt, zu dem die Berührung endet.

Nehmen wir an, Du möchtest auf CivilDusk triggern, die Sonne sinkt unter -6° Höhe am Horizont. Eine Rule dazu sähe dann so aus:

Code: Alles auswählen

rule "civilDusk Trigger"
when
    Channel 'astro:sun:home:civilDusk#event' triggered START
then
    logInfo("astro","Bürgerliche Abenddämmerung erreicht!")
end
Die andere Variante ist, ob wir uns zum Zeitpunkt des Triggers innerhalb der Dunkelheit zwischen Abend- und Morgendämmerung befinden.
Dazu brauchen wir zwei Items:

Code: Alles auswählen

DateTime CivilDawnStart "Sonnenaufgang Bürgerl."   <sunrise> { channel="astro:sun:home:civilDawn#start" }
DateTime CivilDuskStart "Sonnenuntergang bürgerl." <sunset> { channel="astro:sun:home:civilDusk#start" }
und in der Rule müssen wir uns mit der Umrechnung der Zeiten herumschlagen.

Code: Alles auswählen

rule "trigger innerhalb bürgerlicher Dämmerung"
when
    Item blah received update
then
    val Number cDusk = (CivilDuskStart.state as DateTimeType).calendar.timeInMillis
    val Number cDawn = (CivilDawnStart.state as DateTimeType).calendar.timeInMillis
    if(now.millis < cDawn || now.millis > cDusk) {
// alternativ kann man auch eine Funktion von now() verwenden
//    if(now.isBefore(cDawn) || now.isAfter(cDusk)) {  
        logInfo("astro","Trigger innerhalb der bürgerlichen Dämmerung!")
    }
end
Elegant an diesem Ansatz ist, dass man im Astrobinding auch Offsets für jeden Channel definieren kann, man kann das Ereignis also statisch um eine beliebige Zeit nach vorne oder hinten verschieben, außerdem kann man Ober- und Untergrenze definieren.
Hässlich ist, dass es wirklich keinen Spaß macht, die nötigen Konvertierungen durchzuführen, vor allem, weil an dem Datenmodell in letzter Zeit herumgeschraubt wurde. Ich bin mir nicht mal sicher, ob der oben verwendete Ausdruck nicht schon deprecated ist.

Deshalb ist hier eventuell ein einfacherer Ansatz besser, denn das Astrobinding liefert auch die Position der Sonne in Richtung und Höhenwinkel. Wie oben erwähnt ist die Bürgerliche Dämmerung ab -6°. es reicht jetzt ein Item:

Code: Alles auswählen

Number:Angle Elevation "Sonnenhöhe [%.1f°]" <sun>{ channel="astro:sun:home:position#elevation" }
In der Rule wird das jetzt so berücksichtigt:

Code: Alles auswählen

rule "trigger innerhalb bürgerlicher Dämmerung"
when
    Item blah received update
then
    if((Elevation.state as as QuantityType<Number>) <-6) {
        logInfo("astro","Trigger innerhalb der bürgerlichen Dämmerung!")
    }
end
Alternativ kannst Du auch die Einheit mit in den Vergleich nehmen:

Code: Alles auswählen

rule "trigger innerhalb bürgerlicher Dämmerung"
when
    Item blah received update
then
    if(Elevation.state  <-6|°) {
        logInfo("astro","Trigger innerhalb der bürgerlichen Dämmerung!")
    }
end
Beides sollte funktionieren, der erste Ansatz ist praktischer, falls man mit dem Zahlenwert noch Berechnungen anstellen will, da ist es oft nur lästig, Einheiten mit rumzuschleppen.

Re: Geht diese rule so?

Verfasst: 11. Nov 2018 16:58
von isostar1101
Super Danke für die ausführliche Erklärung! vG. Isostar

Re: Geht diese rule so?

Verfasst: 12. Nov 2018 17:34
von BOP
Ich habe es noch ein klein wenig anders gelöst.
Ein Item definiert:

Code: Alles auswählen

Switch EventDaylight
Und dann zwei Regeln:

Code: Alles auswählen

rule "Tageslicht endet"
when
  Channel "astro:sun:home:daylight#event" triggered END
then
  logInfo("Tageslicht-Rule","Tageslicht endet")
  EventDaylight.sendCommand(OFF)
end

rule "Tageslicht beginnt"
when
  Channel "astro:sun:home:daylight#event" triggered START
then
  logInfo("Tageslicht-Rule","Tageslicht startet")
  EventDaylight.sendCommand(ON)
end
Ich kann in anderen Regeln dann einfach auf "EventDaylight.state == ON" oder "EventDaylight.state == OFF"' abfragen.