Presence simulation - trying to get an older script to run

Dear all,

I’m trying to get the following script to run on openhabian 2.4 / Raspberry 3b


import org.openhab.core.library.types.*
import org.joda.time.*
import java.util.TimeZone

val java.util.Random rand = new java.util.Random

rule "Praesenzsimulation durchführen"
    when
        Time cron "0 0/15 17-23 * * ?" // or		// Rule soll alle fünfzehn Minuten aktiv werden im Zeitraum von 17:00 - 23:45 Uhr
        // Time cron "0 0/15 0-1 * * ?" // Rule soll alle fünfzehn Minuten aktiv werden im Zeitraum von 17:00 - 01:30 Uhr
    then
 
 var Integer schaltintervall = 15 // Sollte mit Cron-Aufruf-Intervall übereinstimmen
 var Integer schaltueberlappung = 4 // Zufällige Überlappung, damit nicht alle Lampen gleichzeitig ausgehen (plus zus. 1 Minute)
 var DateTime datumSonnenaufgang = new DateTime((astro_sun_a_weyhe_rise_start.state as DateTimeType).calendar.timeInMillis)
 var DateTime datumSonnenuntergang = new DateTime((astro_sun_a_weyhe_set_end.state as DateTimeType).calendar.timeInMillis)
 val boolean istDunkel = datumSonnenaufgang.afterNow || datumSonnenuntergang.beforeNow

val Boolean log = true

if (log) logInfo('rules','Präsenzsimulation: Rule gestartet. istDunkel=' + istDunkel.toString() + ' praesenzSimulation=' + praesenzSimulation.state.toString())
 
 if (praesenzSimulation.state == OFF) {
    if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation ausgesetzt - nicht aktiv!')
 } 
    else if ((praesenzSimulation.state == ON) && (!istDunkel)) {
        if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation ausgesetzt - aktiv, aber es ist noch nicht dunkel!')
    } 
    else { // es ist dunkel und die Simulation wurde aktiviert
		if (log) logInfo('rules','Präsenzsimulation: Präsenzsimulation wird ausgeführt')
		// Je Präsenzsimulations-Lichtergruppe soll mindestens ein Licht geschalten sein. (pSimH und pSimV)
		var pSimHAnzahl = (pSimH.members.size)
		var pSimVAnzahl = (pSimV.members.size)
		
		// je Gruppe eine zufallsgenerierte Lampen schalten für Zufallszeit zwischen schaltintervall + schaltüberlappung in Minuten
		var Integer schaltzeitH = rand.nextInt(schaltueberlappung) + schaltintervall + 1
		var Integer schaltzeitV = rand.nextInt(schaltueberlappung) + schaltintervall + 1
		var Integer counterH = 0
		var lampeHON = true
		var lampeH = 0
		var boolean exitFlag = false
		
			// LampeH suchen, welche noch nicht AN ist, maximal 30 Versuche, sonst endlosschleife, wenn alle Lampen an wären
			while (lampeHON && !exitFlag) {
				lampeH = rand.nextInt(pSimHAnzahl)
				lampeHON = (pSimH.members.get(lampeH).state == ON) || (pSimH.members.get(lampeH).state > 0)
				if (log) logInfo('rules', 'Präsenzsimulation: Versuch Nr. ' + counterH + ' Ermittelte LampeH (Index) ' + lampeH + '/' + pSimH.members.get(lampeH).name + ' Status lampeHON ' + lampeHON.toString())
				counterH = counterH + 1
				if (counterH > 30) exitFlag = true
			}
			
			if (!exitFlag) {
				// LampeH schalten und Timer für Ausschalten setzen
				if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeH an: ' + pSimH.members.get(lampeH).name + ' für ' + schaltzeitH + ' Minuten')
				pSimH.members.get(lampeH).sendCommand(ON)
				createTimer(now.plusSeconds(schaltzeitH * 60)) [|
				pSimH.members.get(lampeH).sendCommand(OFF)
				if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeH aus: ' + pSimH.members.get(lampeH).name + ' nach ' + schaltzeitH + ' Minuten')
				]
			} 
			else {
				logInfo('rules','Präsenzsimulation: Keine freie LampeH gefunden!')
			}
		
		// LampeV suchen, welche noch nicht AN ist, maximal 30 Versuche, sonst ergibt sich evtl eine Endlosschleife, wenn alle Lampen an wären
		var Integer counterV = 0
		var lampeVON = true
		var lampeV = 0
		exitFlag = false
		
		while (lampeVON && !exitFlag) {
			lampeV = rand.nextInt(pSimVAnzahl)
			lampeVON = (pSimV.members.get(lampeV).state == ON) || (pSimV.members.get(lampeV).state > 0)
			if (log) logInfo('rules', 'Präsenzsimulation: Versuch Nr. ' + counterV + ' Ermittelte LampeV (Index) ' + lampeV + '/' + pSimV.members.get(lampeV).name + ' Status lampeVON ' + lampeVON.toString())
			counterV = counterV + 1
			if (counterV > 30) exitFlag = true
		}
		
		if (!exitFlag) {
			// LampeV schalten und Timer für Ausschalten setzen
			if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeV an: ' + pSimV.members.get(lampeV).name + ' für ' + schaltzeitV + ' Minuten')
			pSimV.members.get(lampeV).sendCommand(ON)
			createTimer(now.plusSeconds(schaltzeitV * 60)) [|
			pSimV.members.get(lampeV).sendCommand(OFF)
			if (log) logInfo('rules', 'Präsenzsimulation: Schalte LampeV aus: ' + pSimV.members.get(lampeV).name + ' nach ' + schaltzeitV + ' Minuten')
			] 
		} 
		else {
			if (log) logInfo('rules','Präsenzsimulation: Keine freie LampeV gefunden!')
		}
	}
end

It seems that the cron doesn’t work properly.

Are you sure the code did ever work as intended? :wink:

For OH2, as a first point, imports with * are not allowed anymore. Luckily both core and joda are per default imported, so no harm.

As you define the var log in the rule, it’s not global, so this var doesn’t exist inside a timer. Same goes for every other var, like lampeH

When using openHAB2, the easy way for log switching would be to use those

  • logDebug("mylogger","log text")
  • logInfo("mylogger","log text")
  • logWarn("mylogger","log text")
  • logError("mylogger","log text")

and do log level switching through karaf:

bash> openhab-cli console

openhab2> log:set DEBUG org.eclipse.smarthome.model.script.mylogger
openhab2> logout

This will instantly set the debug level for your log lines, so all messages are logged. If setting the level to INFO, no Debug lines will be logged, if setting the level to WARN, no Debug nor Info lines will be logged,if setting to ERROR, only Errors will be logged, if setting to OFF, nothing will be logged at all. Setting to DEFAULT will inherit the log level for all rules (i.e. org.eclipse.smarthome.model.script.

Instead of trying up to 30 times to find a light which is OFF, I would prefer to filter the group:

lampeH = rand.nextInt(pSimH.members.filter[ m | m.state == OFF || m.state == 0 ].size)
pSimH.members.filter[ m | m.state == OFF || m.state == 0 ].get(lampeH).sendCommand(ON)

BUT this will not solve the problem, that inside the timer the var lampeH isn’t present, so there is no chance to find the correct member to be switched off. You could solve this by using global vars to store the item names of the lights which were switched on by random:

var String myLight = null // head of rules file

...

myLight = pSimH.members.filter[ m | m.state == OFF || m.state == 0 ].get(lampeH).name

createTimer(now.plusSeconds(someTime), [ |
    sendCommand(myLight,OFF)
])

But be aware that this is only one string, so the light has to be switched off before the rule gets fired the next time (i.e. timer maxtime is less than 15 minutes)

As you don’t have any pointer to the timer, there is no way to cancel the timer in case of switching of presence simulation.

Many thanks for your explanations!

I’ll use this this rule since it suits my needs.

But there is no Timer used, (and no lambda other than for filter), so no problem with var