Präsenzsimulation im Urlaub

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Präsenzsimulation im Urlaub

Beitrag von fred07 »

Hallo zusammen,

ich würde gerne eine alte OH2 Rule für OH 4 anpassen, da ich gerade mein System neu aufsetze.

Ziel ist, dass, wenn ich auf Urlaub bin, sich in einem gewissen Zeitraum sich Lichter im Haus automatisch aus und einschalten. Dieser Zeitraum sollte nicht gleich sein und die Lichter zufällig schalten, auch die Brenndauer soll zufällig sein.

Ich finde nur keinen RandomLightsTimer wie in der alten Rule (ich tu mir generell etwas hart mit dem Umstieg.

Hat wer eine Idee? Hier zur Info die alte Rule:

Code: Alles auswählen

val String filename ="dev.rules"
var Timer RandomLightsTimer=null
val java.util.concurrent.ThreadLocalRandom random = (new java.util.concurrent.ThreadLocalRandom)  

// ab hier kommen die Rules

rule"Präsenzsimulation"
when
    Time cron "0 */15 5-23 * * ?"
then
    if(Urlaub.state == ON) {
        logInfo(filename,"Urlaub ON")
        val sunrise =(Nautic_Dawn.state as DateTimeType).calendar.timeInMillis
        logInfo(filename,"Sunrise {}",sunrise)
        val sunset = (Nautic_Dusk.state as DateTimeType).calendar.timeInMillis
        logInfo(filename,"Sunset {}",sunset)
        if (now.isBefore(sunrise) || now.isAfter(sunset)) {
            logInfo(filename,"Nach Sonnenuntergang und vor Sonnenaufgang")
            var int randomTime = random.nextInt(240)
            logInfo(filename,"Neuer Wert Zufallstimer: {} Sekunden.",randomTime)
            RandomLightsTimer = createTimer(now.plusSeconds(randomTime)) [ |
                var randomItem = gUr.members.get(random.nextInt(gUr.members.size))
                var randomLightCurrentState = randomItem.state
                // ersetzen der Dimmer-Werte von "0" auf "OFF"
                if (randomLightCurrentState == 0) randomLightCurrentState = OFF
                // alle Dimmer-Werte von 1-100 werden als ON gewertet
                var randomLightNewState = if (randomLightCurrentState != OFF) OFF else ON
                logInfo(filename, "Schalte Licht {} von {} nach {}",randomItem.name,randomLightCurrentState,randomLightNewState)
                randomItem.sendCommand(randomLightNewState)
            ]
        } else logInfo(filename,"Tagsüber")
    } else logInfo(filename,"Urlaub OFF")
end


rule "Licht Urlaub aus Nacht"

when 
Time cron "0 5 23 1/1 * ? *"
then 
if (Urlaub.state==ON) {
gUr.members.forEach(Light|if (Light.state!=OFF) Light.sendCommand(OFF))

}
end

rule "Licht Urlaub aus Tag"

when 
Time cron "0 3 8 1/1 * ? *"
then 
if (Urlaub.state==ON) {
gUr.members.forEach(Light|if (Light.state!=OFF) Light.sendCommand(OFF))

}
end
Vielen Dank im Voraus, LG

nw378
Beiträge: 296
Registriert: 22. Sep 2018 10:38
Answers: 5

Re: Präsenzsimulation im Urlaub

Beitrag von nw378 »

Hi,

warum so umständlich? Statt zufälligem An- und Ausgeknipse wirkt es doch viel "bewohnter", wenn alles so passiert, wie zu Zeiten, wenn wirklich jemand da ist.

Einfach alle entsprechenden Verbraucher / Rollo-stellungen etc. persistieren und mit folgender Regel im in den Zustand genau wie vor einer Woche einstellen.

Code: Alles auswählen

var int presence_days = 7
var String persistence = "influxdb"

rule "Anwesenheits-Simulation"
when
    Time cron "0 0/1 * 1/1 * ? *"
then
    if (Presence_sim.state == ON) {
	gSim.members.forEach(simItem,i |
		if(simItem.historicState(now.minusDays(presence_days), persistence).state != simItem.state) {
			logInfo("Anwesenheitssim ",simItem.name + " got " + simItem.historicState(now.minusDays(presence_days), persistence).state)
				simItem.sendCommand(simItem.historicState(now.minusDays(presence_days), persistence).state.toString)
				})}
end  
Dazu werden alle gewünschten Items der Gruppe gSim zugeordnet, bspw.:

Code: Alles auswählen

Group   gSim
Switch EG_Unterschrank      "Hängeschrank Küche"     <selflight>       (EG, EG_Kochen, gLights, gSim, gWest)                 { channel="knx:device:bridge:aktor1:Licht_EG_Unterschrank" }
openHAB 4.3.3 @ RPi 4 / SSD - InfluxDB2 und Grafana @ Synology Docker - KNX

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Re: Präsenzsimulation im Urlaub

Beitrag von fred07 »

Tja, das geht leider nicht, weil die Lichter, die man leicht von der Straße sieht, nicht so oft verwendet werden. Die sind oft tage- und wochenlang aus, das bringt leider nicht so viel.

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

Re: Präsenzsimulation im Urlaub

Beitrag von udo1toni »

Auch wenn Du das nicht hören willst: Die Rule macht nicht, was Du denkst :) und das hat sie noch nie.

Code: Alles auswählen

rule"Präsenzsimulation"
when
    Time cron "0 */15 5-23 * * ?" //täglich alle 15 Minuten, von 05:00 Uhr bis 23:59(!) Uhr.
Da die Triggerminute nicht klar definiert ist, kann es sein, dass openHAB irgendwann innerhalb einer Viertelstunde die Datei neu einliest und ab diesem Zeitpunkt in dieser Minute triggert. Die letzte Ausführung der Rule am Tag ist aber frühestens (!) um 23:45 Uhr.

Code: Alles auswählen

rule "Licht Urlaub aus Nacht"
when 
Time cron "0 5 23 1/1 * ? *" // täglich um 23:05:00 Uhr
Ups, das Licht wird also zuverlässig um 23:05 Uhr ausgeschaltet, danach kann es aber wieder eingeschaltet werden (und bleibt im Zweifel auch bis zum nächsten Morgen an)

Ohne sie getestet zu haben, hier eine andere Version:

Code: Alles auswählen

// Globale Variablen vor der ersten Rule definieren!
var Timer tRandomLights = null                                                                     // Handle für Urlaubslicht
val java.util.Random random = new java.util.Random()                                               // Zufall
var Integer iRandom = null

// ab hier kommen die Rules

rule "Präsenz Simulation"
when
    Item Urlaub changed or                                                                         // Schalter umgelegt
    Time is midnight or                                                                            // oder neuer Tag
    Time cron "0 45 23 * * ?" or                                                                   // täglich um 23:45 Uhr
    Channel 'astro:sun:local:nauticDawn#event' triggered END                                       // nautischer Sonnenaufgang
then
    tRandomLights?.cancel                                                                          // bestehenden Timer abbrechen
    if(Urlaub.state != ON)                                                                         // Falls kein Urlaub
        return;                                                                                    // Ende
    gUr.members.filter[i | i.getStateAs(OnOffType) != OFF].forEach[Light | Light.sendCommand(OFF)] // alle nicht ausgeschalteten Lichter ausschalten

    val dtDawn = (Nautic_Dawn.state as DateTimeType).getZonedDateTime                              // Sonnenaufgang (nautisch)
    val dtDusk = (Nautic_Dusk.state as DateTimeType).getZonedDateTime                              // Sonnenuntergang (nautisch)
    var nextStart = now                                                                            // jetzt
    if(nextStart.getHour < 5) nextStart = nextStart.with(LocalTime.MIDNIGHT).plusHours(5)          // falls vor 5 Uhr, setze 5 Uhr
    if(nextStart.isAfter(dtDawn) && nextStart.isBefore(dtDusk)) nextStart = dtDusk                 // falls nach dtDawn und vor dtDusk, setze dtDusk
    if(nextStart.get(ChronoField.MINUTE_OF_DAY) > 1424)                                            // falls nach 23:45
        return;                                                                                    // heute kein Timer mehr
    iRandom = random.nextInt(240)                                                                  // generiere Zufallswert
    tRandomLights = createTimer(nextStart.plusSeconds(iRandom),[|                                  // Timer für nächsten Timerstart
        var randomItem = gUr.members.get(random.nextInt(gUr.members.size))                         // suche ein Item aus
        randomItem.sendCommand(if(randomItem.getStateAs(OnOffType) != OFF) OFF else ON)            // und schalte es um
        iRandom = random.nextInt(240)                                                              // generiere Zufallswert
        tRandomLights.reschedule(now.plusMinutes(5).plusSeconds(iRandom))                          // Plane Timer erneut nach mindestens 5 Minuten
    ])
end
Hauptunterschied: Die Steuernde Rule wird dreimal täglich plus ein weiteres Mal für jedes Wechseln des Urlaubsmodus getriggert.
Die Rule beendet zunächst eventuell laufende Timer.
Dann prüft sie, ob der Urlaubsmodus aktiv ist. Ist dies nicht der Fall, hat sie nichts mehr zu erledigen und wird beendet.
Falls es weiter geht, schaltet sie alle Lichter auf OFF, die gerade nicht den OFF Zustand haben. (über eine gefilterte Liste, statt die Liste komplett zu durchlaufen). Dabei berücksichtigt sie auch Dimmer Items korrekt, indem sie den Status explizit als OnOffType abfragt ;)
Danach bestimmt sie die gewünschten Randzeiten (hier allerdings als dtDawn und dtDusk bezeichnet, ist ja was anderes als Sonnenauf-/-untergang)
Nun wird zunächst die Startzeit des Timers auf die aktuelle Zeit gesetzt.
Anschließend prüft die Rule die Grenzwerte, falls also gerade vor fünf Uhr, wird fünf Uhr gesetzt, falls nach Nautic Dawn, aber vor Nautic Dusk, wird dtDusk gesetzt.
Falls es nach 23:45 Uhr ist (erfragt über die Minute des Tages, statt Stunde und Minute zu prüfen) bricht die Rule ab, denn zu diesem Zeitpunkt wird für den aktuellen Tag kein Timer mehr gebraucht. Um 0 Uhr triggert die Rule dann erneut...
Falls die Rule noch läuft, wird ein Zufallswert in iRandom abgelegt.
Der Timer wird erzeugt. Dies ist der erste Timer entweder a) nach Aktivieren des Urlaubsmodus oder b) nach (um) Mitternacht des aktuellen Tages.
Der "Reset" um Mitternacht stellt sicher, dass auf jeden Fall immer wieder Timer erzeugt werden, selbst wenn mal aus irgendeinem Grund zwischendrin kein Timer mehr laufen sollte.
Im Timer wird wie gehabt zufällig ein Item aus der Gruppe der Zufallsitems ausgewählt und der Zustand getoggelt (wie weiter oben mit getStateAs(OnOffType).
Es wird ein neuer Zufallswert erzeugt und der Timer plant sich selbst erneut ein, nach frühestens 5 Minuten und höchstens 9 Minuten (wegen Zufallswert 240 Sekunden). :)
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Re: Präsenzsimulation im Urlaub

Beitrag von fred07 »

Vielen Dank für die Hilfe, ich werde das am Wochenende testen.
Ich hoffe, ich kriege das hin, ich tue mir etwas schwer mit OH3/4.

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Re: Präsenzsimulation im Urlaub

Beitrag von fred07 »

Ich muss den alten Beitrag nochmals hervorholen, leider war ich nie auf Urlaub und konnte es nicht testen.

Ich habe folgende Rule, wie von udo1toni vorgeschlagen:

Code: Alles auswählen

// Globale Variablen vor der ersten Rule definieren!
var Timer tRandomLights = null                                                                     // Handle für Urlaubslicht
val java.util.Random random = new java.util.Random()                                               // Zufall
var Integer iRandom = null

// ab hier kommen die Rules

rule "Präsenz Simulation"
when
    Item Urlaub changed or                                                                         // Schalter umgelegt
    Time is midnight or                                                                            // oder neuer Tag
    Time cron "0 45 23 * * ?" or                                                                   // täglich um 23:45 Uhr
    Channel 'astro:sun:local:nauticDawn#event' triggered END                                       // nautischer Sonnenaufgang
then
    tRandomLights?.cancel                                                                          // bestehenden Timer abbrechen
    if(Urlaub.state != ON)                                                                         // Falls kein Urlaub
        return;                                                                                    // Ende
    Urlaubslichter.members.filter[i | i.getStateAs(OnOffType) != OFF].forEach[Light | Light.sendCommand(OFF)] // alle nicht ausgeschalteten Lichter ausschalten

    val dtDawn = (nauticDawn.state as DateTimeType).getZonedDateTime                              // Sonnenaufgang (nautisch)
    val dtDusk = (nauticDusk.state as DateTimeType).getZonedDateTime                              // Sonnenuntergang (nautisch)
    var nextStart = now                                                                            // jetzt
    if(nextStart.getHour < 5) nextStart = nextStart.with(LocalTime.MIDNIGHT).plusHours(5)          // falls vor 5 Uhr, setze 5 Uhr
    if(nextStart.isAfter(dtDawn) && nextStart.isBefore(dtDusk)) nextStart = dtDusk                 // falls nach dtDawn und vor dtDusk, setze dtDusk
    if(nextStart.get(ChronoField.MINUTE_OF_DAY) > 1424)                                            // falls nach 23:45
        return;                                                                                    // heute kein Timer mehr
    iRandom = random.nextInt(240)                                                                  // generiere Zufallswert
    tRandomLights = createTimer(nextStart.plusSeconds(iRandom),[|                                  // Timer für nächsten Timerstart
        var randomItem = Urlaubslichter.members.get(random.nextInt(Urlaubslichter.members.size))                         // suche ein Item aus
        randomItem.sendCommand(if(randomItem.getStateAs(OnOffType) != OFF) OFF else ON)            // und schalte es um
        iRandom = random.nextInt(240)                                                              // generiere Zufallswert
        tRandomLights.reschedule(now.plusMinutes(5).plusSeconds(iRandom))                          // Plane Timer erneut nach mindestens 5 Minuten
    ])
end
Leider bekomme ich, sobald ich den Schalter Urlaub umlege, folgenden Fehler:

Code: Alles auswählen

2024-06-07 17:48:23.117 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'praesenz-1' failed: The name 'nauticDawn' cannot be resolved to an item or type; line 20, column 19, length 10 in praesenz
Ich komme leider nicht dahinter, was hier nicht passt.

Der Channel sieht so aus:

astro:sun:local:nauticDusk#event

Ein Item ist nicht verlinkt.

Vielen Dank!

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

Re: Präsenzsimulation im Urlaub

Beitrag von udo1toni »

Du musst die beiden Informationen nauticDawn und nauticDusk asls DateTime Item bereitstellen (analog der Rule mit den entsprechenden Namen). Das hat nichts mit den rangeEvent-Channel zu tun, die Rule braucht hier die Zeiten als DateTime Status.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Re: Präsenzsimulation im Urlaub

Beitrag von fred07 »

Vielen Dank, ich bin mir nur nicht sicher, ob ich es richtig verstanden habe (vermutlich nicht):

Im Programm sieht es so aus:
Unbenannt.jpg
Code des Items:

Code: Alles auswählen

label: Nautische Abenddämmerung Event
type: DateTime
category: ""
groupNames: []
tags:
  - Point
Das zweite Item sieht gleich aus, nur eben Morgendämmerung.

Die Rule sieht jetzt so aus:

Code: Alles auswählen

// Globale Variablen vor der ersten Rule definieren!
var Timer tRandomLights = null                                                                     // Handle für Urlaubslicht
val java.util.Random random = new java.util.Random()                                               // Zufall
var Integer iRandom = null

// ab hier kommen die Rules

rule "Präsenz Simulation"
when
    Item Urlaub changed or                                                                         // Schalter umgelegt
    Time is midnight or                                                                            // oder neuer Tag
    Time cron "0 45 23 * * ?" or                                                                   // täglich um 23:45 Uhr
    Channel 'astro:sun:local:nauticDawn#event' triggered END                                       // nautischer Sonnenaufgang
then
    tRandomLights?.cancel                                                                          // bestehenden Timer abbrechen
    if(Urlaub.state != ON)                                                                         // Falls kein Urlaub
        return;                                                                                    // Ende
    Urlaubslichter.members.filter[i | i.getStateAs(OnOffType) != OFF].forEach[Light | Light.sendCommand(OFF)] // alle nicht ausgeschalteten Lichter ausschalten

    val dtDawn = (NautischeMorgendaemmerungEvent.state as DateTimeType).getZonedDateTime                              // Sonnenaufgang (nautisch)
    val dtDusk = (NautischeAbenddaemmerungEvent.state as DateTimeType).getZonedDateTime                              // Sonnenuntergang (nautisch)
    var nextStart = now                                                                            // jetzt
    if(nextStart.getHour < 5) nextStart = nextStart.with(LocalTime.MIDNIGHT).plusHours(5)          // falls vor 5 Uhr, setze 5 Uhr
    if(nextStart.isAfter(dtDawn) && nextStart.isBefore(dtDusk)) nextStart = dtDusk                 // falls nach dtDawn und vor dtDusk, setze dtDusk
    if(nextStart.get(ChronoField.MINUTE_OF_DAY) > 1424)                                            // falls nach 23:45
        return;                                                                                    // heute kein Timer mehr
    iRandom = random.nextInt(240)                                                                  // generiere Zufallswert
    tRandomLights = createTimer(nextStart.plusSeconds(iRandom),[|                                  // Timer für nächsten Timerstart
        var randomItem = Urlaubslichter.members.get(random.nextInt(Urlaubslichter.members.size))                         // suche ein Item aus
        randomItem.sendCommand(if(randomItem.getStateAs(OnOffType) != OFF) OFF else ON)            // und schalte es um
        iRandom = random.nextInt(240)                                                              // generiere Zufallswert
        tRandomLights.reschedule(now.plusMinutes(5).plusSeconds(iRandom))                          // Plane Timer erneut nach mindestens 5 Minuten
    ])
end
Und jetzt kommt dieser Fehler:

Code: Alles auswählen

2024-06-07 19:53:18.824 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'praesenz-1' failed: Could not cast NULL to org.openhab.core.library.types.DateTimeType; line 20, column 19, length 52 in praesenz
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

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

Re: Präsenzsimulation im Urlaub

Beitrag von udo1toni »

Ja, Du hast es nicht richtig verstanden.
Du kannst Trigger Channel nicht mit einem Item verlinken. Das, Du da gemacht hast, ist, ein Item anzulegen, welches den Zeitstempel des letzten Triggers notiert. Darum geht es hier aber nicht. Vergiss bitte den Trigger Channel. Schau stattdessen nach den DateTime Channeln Es gibt für jedes Ereignis (also z.B. nautic dusk -> nautische Abenddämmerung) einen Channel für start, einen Channel für end, einen Channel für duration und einen Trigger Channel. Dich interessiert der start Channel. Dieser ist vom Typ DateTime, entsprechend verlinkst Du diesen Channel mit dem passenden Item. Das gleiche machst Du mit nauticDawn.
Wobei es vermutlich sinnvoller sein wird, civilDusk und civilDawn zu verwenden, nauticDusk ist schon sehr dunkel...
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

fred07
Beiträge: 40
Registriert: 26. Aug 2022 16:40
Answers: 0

Re: Präsenzsimulation im Urlaub

Beitrag von fred07 »

Ok, danke, ich war verwirrt, weil es die jeweiligen Items ja schon gab.

Habe es angepasst:

Code: Alles auswählen

// Globale Variablen vor der ersten Rule definieren!
var Timer tRandomLights = null                                                                     // Handle für Urlaubslicht
val java.util.Random random = new java.util.Random()                                               // Zufall
var Integer iRandom = null

// ab hier kommen die Rules

rule "Präsenz Simulation"
when
    Item Urlaub changed or                                                                         // Schalter umgelegt
    Time is midnight or                                                                            // oder neuer Tag
    Time cron "0 45 23 * * ?" or                                                                   // täglich um 23:45 Uhr
    Channel 'astro:sun:local:nauticDawn#event' triggered END                                       // nautischer Sonnenaufgang
then
    tRandomLights?.cancel                                                                          // bestehenden Timer abbrechen
    if(Urlaub.state != ON)                                                                         // Falls kein Urlaub
        return;                                                                                    // Ende
    Urlaubslichter.members.filter[i | i.getStateAs(OnOffType) != OFF].forEach[Light | Light.sendCommand(OFF)] // alle nicht ausgeschalteten Lichter ausschalten

    val dtDawn = (LokaleSonnendaten_NauticDawn_Start.state as DateTimeType).getZonedDateTime                              // Sonnenaufgang (nautisch)
    val dtDusk = (LokaleSonnendaten_NauticDusk_Start.state as DateTimeType).getZonedDateTime                              // Sonnenuntergang (nautisch)
    var nextStart = now                                                                            // jetzt
    if(nextStart.getHour < 5) nextStart = nextStart.with(LocalTime.MIDNIGHT).plusHours(5)          // falls vor 5 Uhr, setze 5 Uhr
    if(nextStart.isAfter(dtDawn) && nextStart.isBefore(dtDusk)) nextStart = dtDusk                 // falls nach dtDawn und vor dtDusk, setze dtDusk
    if(nextStart.get(ChronoField.MINUTE_OF_DAY) > 1424)                                            // falls nach 23:45
        return;                                                                                    // heute kein Timer mehr
    iRandom = random.nextInt(240)                                                                  // generiere Zufallswert
    tRandomLights = createTimer(nextStart.plusSeconds(iRandom),[|                                  // Timer für nächsten Timerstart
        var randomItem = Urlaubslichter.members.get(random.nextInt(Urlaubslichter.members.size))                         // suche ein Item aus
        randomItem.sendCommand(if(randomItem.getStateAs(OnOffType) != OFF) OFF else ON)            // und schalte es um
        iRandom = random.nextInt(240)                                                              // generiere Zufallswert
        tRandomLights.reschedule(now.plusMinutes(5).plusSeconds(iRandom))                          // Plane Timer erneut nach mindestens 5 Minuten
    ])
end
Diesmal nimmt es die Hürde mit dem Item, stoppt aber dann bei ChronoField mit folgendem Fehler:

Code: Alles auswählen

2024-06-08 08:14:25.965 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'praesenz-1' failed: The name 'ChronoField' cannot be resolved to an item or type; line 25, column 22, length 11 in praesenz
Nautic/Civil werd ich berücksichtigen, aber wichtig, dass es mal so richtig läuft.

Antworten