Rule optimization: Window OPEN reminder

I use duiffie´s rule and it work well. Is just extended it a little bit for my needs, but I´m still not familiar with javascript. Here is my code:

import java.util.Map

val Map<String, Timer> OpenWindowTimers = newHashMap

val Functions$Function2<ContactItem, Map<String, Timer>, Boolean> checkOpenWindow = [
	windowItem,
	timerMap |

	var String myTimerKey = windowItem.name.toString
	var String season_name = transform("MAP", "astro.map", Jahreszeit.state.toString)
	var winopentime = 0
	switch Jahreszeit {
		case Jahreszeit.state == "SPRING"	:	winopentime = 15
		case Jahreszeit.state == "SUMMER"	:	winopentime = 30
		case Jahreszeit.state == "AUTUMN"	:	winopentime = 10
		case Jahreszeit.state == "WINTER"	:	winopentime = 5
		default								:	winopentime = 5  //just in case if no season is set
	}
	
	logInfo("Fenster-Check", "Jahreszeit ist " + season_name )
	if (windowItem.state == CLOSED) {
		if (timerMap.get(myTimerKey) !== null) timerMap.get(myTimerKey).cancel()
	} else if (windowItem.state == OPEN) {
	timerMap.put(myTimerKey, createTimer(now.plusMinutes(winopentime)) [|
		timerMap.put(myTimerKey, null)

		val String shortName = transform("MAP", "windowShortName.map", windowItem.name.toString)
		val String longName = transform("MAP", "windowLongName.map", windowItem.name.toString)

		logInfo("Fenster-Check", shortName + " ist seit "+ winopentime +" Min. offen")
		sendBroadcastNotification(longName + " ist seit "+ winopentime +" Min. offen")
		echodotWZ_reminder.sendCommand(longName + " ist seit "+ winopentime +" Minuten offen")
	])
	}
	true
	]



rule "Fenster-Check"
when
	Item gWindoors received update
then
	logInfo("Fenster-Check", "Fenster/Tür hat sich verändert")
	Thread::sleep(500) // this gives the persistence service time to store the last update
	val lastUpdatedWindowItem = gWindoors.members.filter[s|s.lastUpdate("mapdb") !== null].sortBy[lastUpdate("mapdb")].last as ContactItem
	checkOpenWindow.apply(lastUpdatedWindowItem, OpenWindowTimers)
end

What i´ve done is simple, I just use the astro binding to check which season it is and depending on that, set a specific amount of minutes for the timer.

What I still want is a further extension to recheck if a window is still open after the first timer expired. I know there´s a command to reschedule a timer: https://docs.openhab.org/addons/actions.html#timers

Could someone assist to make this possible?

I monitore my windows status and send a message to alexa when a window is opened for longer than 15 minutes.
My rule looks like this:

rule "Window open while heat is on"

when
    Item gEGContacts changed to OPEN or
    Item gOGContacts changed to OPEN or
    Item gDBContacts changed to OPEN
then

if(EG_Vi_hkpump.state == ON){
    if(stopMotionTimer === null && gEGContacts.state == OPEN && gOGContacts.state == OPEN && gDBContacts.state == OPEN){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Erd, Ober und Dachgeschoss.')
        stopMotionTimer = null
        ]
    }
    else if(stopMotionTimer === null && gEGContacts.state == CLOSED && gOGContacts.state == OPEN && gDBContacts.state == OPEN){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Ober und Dachgeschoss.')
        stopMotionTimer = null
        ]
    }
        else if(stopMotionTimer === null && gEGContacts.state == OPEN && gOGContacts.state == CLOSED && gDBContacts.state == OPEN){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Erd und Dachgeschoss.')
        stopMotionTimer = null
        ]
    }
        else if(stopMotionTimer === null && gEGContacts.state == OPEN && gOGContacts.state == OPEN && gDBContacts.state == CLOSED){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Erd und Obergeschoss.')
        stopMotionTimer = null
        ]
    }
        else if(stopMotionTimer === null && gEGContacts.state == OPEN && gOGContacts.state == CLOSED && gDBContacts.state == CLOSED){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Erdgeschoss.')
        stopMotionTimer = null
        ]
    }
        else if(stopMotionTimer === null && gEGContacts.state == CLOSED && gOGContacts.state == OPEN && gDBContacts.state == CLOSED){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Obergeschoss.')
        stopMotionTimer = null
        ]
    }
        else if(stopMotionTimer === null && gEGContacts.state == CLOSED && gOGContacts.state == CLOSED && gDBContacts.state == OPEN){
        stopMotionTimer = createTimer(now.plusMinutes(15)) [|
        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im Dachgeschoss.')
        stopMotionTimer = null
        ]
    }
}

But the problem is, that when a window is closed while the timer is running, alexa still brings the message that a window is open after 15 minutes. So I need a second check, if a window is still open after 15 minutes and I don’t know how to do it.
An other problem is, that I would like to put alexas volume to a certain value before the window message is played. After the message I would like to restore the previous volume.
And Ideas?
By the way - I am a noob learning :wink:

This would have been better posted as a separate new posting.

OK, I see a whole lot of duplicated code here. Let’s see if we can make is simpler and then we can address the problem (which is that you need to cancel the Timer when the window closes.

First, put all your Contacts into a Group, I’ll call it Windows. Define is as Group:Windows:OR(OPEN, CLOSED). The state of Windows will be OPEN if any one of its members is OPEN, CLOSED otherwise.

Next change the Rule to trigger with Member of Windows. But, you probably want an alert if the heat turns on and a window is already OPEN so let’s trigger on the heat pump as well.

If the pump isn’t on, just exit the Rule. If there is already a Timer set, exit the Rule.

var Timer stopMotionTimer = null

rule "Window open while heat is on"
when
    Member of Windows changed to OPEN or
    Item EG_Vi_hkpump changed to ON
then
    if(EG_Vi_hkpump.state != ON) return;
    if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(15), [ |
        // Generate a list of all the open windows
        var openWins = Windows.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg = msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im " + openWins + ".")
        stopMotionTimer = null
    ])

end

Theory of operation: When a Window opens or the heat turns ON, set a timer if one doesn’t already exist. When the Timer goes off, construct a list of all the open Windows and TTS the list.

Now what if the heat turns off or all the windows close?

rule "Windows closed"
when
    Item Windows changed to CLOSED or // happens when all windows are closed
    Item  EG_Vi_hkpump changed to OFF
then
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

Theory of operation: If all the windows close or the heat turns off, cancel the timer if it is running.

1 Like

Hello,
thank you so much for your help.
I already have a group for all windows. It is

Group:Contact:OR(OPEN,CLOSED) gHContacts "Fensterkontakte Haus [(%d)]" <window>

An example for a window item:

Contact EG_wo_fe_re1  "Wohnen Rechts Auf [MAP(de.map):%s]" <window> (gEGContactsOffen, gEGContacts) { channel="knx:device:bridge:Tasterschnittstellen:EG_wo_fe_re1" } 
Contact EG_wo_fe_re2  "Wohnen Rechts Kippe [MAP(de.map):%s]" <window> (gEGContactsKippe, gEGContacts) { channel="knx:device:bridge:Tasterschnittstellen:EG_wo_fe_re2" } 
String EG_wo_fe_re "Wohnen Rechts [MAP(de.map):%s]" <contact> (gHFenster, gEG, gEGFenster, gWO, gHContacts)

And the logfile shows an error:

019-02-25 20:45:51.532 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rules.rules', using it anyway:
Assignment to final parameter
2019-02-25 20:45:51.731 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'rules.rules'
2019-02-25 20:45:51.742 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception: 
java.lang.NullPointerException: null
	at org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleContextHelper.getContext(RuleContextHelper.java:68) ~[?:?]
	at org.eclipse.smarthome.model.rule.runtime.internal.engine.RuleEngineImpl.lambda$2(RuleEngineImpl.java:339) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
	at java.lang.Thread.run(Thread.java:748) [?:?]
2019-02-25 20:46:35.456 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rules.rules', using it anyway:
Assignment to final parameter
2019-02-25 20:46:35.594 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'rules.rules'
2019-02-25 20:48:20.193 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2019-02-25T20:48:20.056+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  var openWins
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <XFeatureCallImplCustom>.sendCommand(<XBinaryOperationImplCustom>)
  <null>.stopMotionTimer = <XNullLiteralImplCustom>
} ] threw an unhandled Exception: 
java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param msg
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1225) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy155.apply(Unknown Source) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IteratorExtensions.reduce(IteratorExtensions.java:647) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IterableExtensions.reduce(IterableExtensions.java:545) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:827) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:264) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy150.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
2019-02-25 20:48:20.308 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2019-02-25T20:48:20.056+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  var openWins
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <XFeatureCallImplCustom>.sendCommand(<XBinaryOperationImplCustom>)
  <null>.stopMotionTimer = <XNullLiteralImplCustom>
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param msg
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1225) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy155.apply(Unknown Source) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IteratorExtensions.reduce(IteratorExtensions.java:647) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IterableExtensions.reduce(IterableExtensions.java:545) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:827) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:264) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy150.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more
2019-02-25 20:50:32.060 [INFO ] [.smarthome.model.script.system.rules] - Uptime updated to 1 Std. / 52 Min.

Your rule adjusted to my group:

var Timer stopMotionTimer = null

rule "Window open while heat is on"
when
    Member of gHContacts changed to OPEN or
    Item EG_Vi_hkpump changed to ON
then
    if(EG_Vi_hkpump.state != ON) return;
    if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1), [ |
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg = msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im " + openWins + ".")
        stopMotionTimer = null
    ])

end

There is an exception being thrown from inside the Timer.

I just typed in the code above so there is likely a typo.

Add some logging so you can figure out which line is throwing the error.

I found some typos like

stopMotionTimer = createTimer(now.plusMinutes(1)

Shoulld be

stopMotionTimer = createTimer(now.plusMinutes(1))

right?
Still some errors in the openhab.log

2019-02-25 22:21:53.230 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rules.rules', using it anyway:
Assignment to final parameter

The rule looks like this now:

rule "Window open while heat is on"
when
    Member of gHContacts changed to OPEN or
    Item EG_Vi_hkpump changed to ON
then
    if(EG_Vi_hkpump.state != ON) return;
    if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1)) [|
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg = msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im " + openWins + ".")
        stopMotionTimer = null
    ]

end

rule "Windows closed"
when
    Item gHContacts changed to CLOSED or // happens when all windows are closed
    Item  EG_Vi_hkpump changed to OFF
then
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

When a window is open, this happens:

2019-02-25 22:26:44.382 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'rules.rules'
2019-02-25 22:28:17.953 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2019-02-25T22:28:17.901+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  var openWins
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <XFeatureCallImplCustom>.sendCommand(<XBinaryOperationImplCustom>)
  <null>.stopMotionTimer = <XNullLiteralImplCustom>
} ] threw an unhandled Exception: 
java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param msg
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1225) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy155.apply(Unknown Source) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IteratorExtensions.reduce(IteratorExtensions.java:647) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IterableExtensions.reduce(IterableExtensions.java:545) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:827) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:264) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy150.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
2019-02-25 22:28:18.044 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2019-02-25T22:28:17.901+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  var openWins
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <null>.openWins = <XMemberFeatureCallImplCustom>
  <XFeatureCallImplCustom>.sendCommand(<XBinaryOperationImplCustom>)
  <null>.stopMotionTimer = <XNullLiteralImplCustom>
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param msg
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1225) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1213) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:216) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy155.apply(Unknown Source) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IteratorExtensions.reduce(IteratorExtensions.java:647) ~[?:?]
	at org.eclipse.xtext.xbase.lib.IterableExtensions.reduce(IterableExtensions.java:545) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:827) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:264) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy150.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more

No, that isn’t a typo.

There is a feature of the language that I absolutely hate because it causes a lot of confusion.

createTimer takes two arguments, a DateTime and a lambda. But the language let’s you define the second argument outside the parens if and only if the last argument is a lambda.

For those who are not familiar with the language, it looks like you are somehow magically associating the lambda with the Timer by putting it after the call.

createTimer(now.plusMinutes(1), [ |
    // some code
])

is a more correct reflection of what is actually going on. So I always put the lambda inside the parens and encourage others who post to the forum to do the same.

How did you declare stopMotionTimer? Is it a val? It needs to be a var.

There is nothing in this code that would generate that error unless stopMotionTimer is a val instead of a var.

Did you create windows.map? You need to create it with a mapping between the Item names and the name you want to include in your spoken message. See Design Pattern: Human Readable Names in Messages.

Okay, lets start with the easy one.
The timer is var:

var Timer stopMotionTimer = null

So this is not the reason.
When I leave the code like this:

createTimer(now.plusMinutes(1), [ |
    // some code
])

I get this errors in the log:

2019-02-25 23:27:00.159 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'rules.rules' has errors, therefore ignoring it: [124,54]: mismatched input ',' expecting 'end'

Putting the rule to this:

createTimer(now.plusMinutes(1) [ |
    // some code
])

Gives me:

2019-02-25 23:31:23.767 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rules.rules', using it anyway:
Assignment to final parameter
2019-02-25 23:31:23.777 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'rules.rules'

You are right - I missed the windows map. I will do it and post if it helps.

I made a “windows.map” like this:

EG_wo_fe_li=Wohnen Links
EG_wo_fe_re=Wohnen Rechts
EG_er_fe_li=Erker Links
EG_er_fe_re=Erker Rechts
EG_es_fe_li=Essen Links
EG_es_fe_li3=Essen Links, linker Flügel
EG_es_fe_re=Essen Rechts
EG_es_fe_re3=Essen Rechts, linker Flügel
EG_ko_fe1=Kochen, rechter Flügel
EG_ko_fe2=Kochen, linker Flügel
EG_wc_fe=Gäste WC
OG_Mi_fe_li=Mika links
OG_Mi_fe_re=Mika rechts
OG_Jo_fe_li=Joel links
OG_Jo_fe_re=Joel rechts
OG_Sc_fe_li1=Schlafen links, rechter Flügel
OG_Sc_fe_re=Schlafen rechts
OG_Sc_fe_li2=Schlafen links
OG_Hw_fe=Hauswirtschaftsraum
OG_Ba_fe_li=Bad links
OG_Ba_fe_re=Bad rechts
DB_fe_bu=Büro
DB_ve=Büro Velux
DB_fe_te=Dachboden Technik

Left hand the items and right hand the names to be spoken.
Still get an error on the log:

2019-02-25 23:47:29.455 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rules.rules', using it anyway:
Assignment to final parameter
2019-02-25 23:47:29.704 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'rules.rules'
2019-02-25 23:48:59.190 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Window open NEW': An error occurred during the script execution: index=1, size=1

When not even the map works - I think it’s time for bed…

Darn it, I always get this wrong.

.reduce[ msg, winName | msg + ", " + winName ]

We don’t assign to msg directly in the body of the lambda. The result of the operation is automatically assigned to msg.

1 Like

Thats it! Thank you!
Regarding the pump I need to make an adjustment.
Actually it is like:

rule "Windows closed NEW"
when
    Item gHContacts changed to CLOSED or// happens when all windows are closed
    Item EG_Vi_hkpump changed to OFF
then
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

This would stop the timer every time, the pump stopps. Unfortunately this happens also for heating our drinking water.
So it needs to be like:

rule "Window open NEW"
when
    Member of gHContacts changed to OPEN
then
    if(EG_Vi_hkpump.state != OFF) return;
    if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1) [ |
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im " + openWins + ".")
        stopMotionTimer = null
    ])

end

rule "Windows closed NEW"
when
    Item gHContacts changed to CLOSED // happens when all windows are closed
then
    if(EG_Vi_hkpump.state != OFF) return;
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

I hope, this will skip the rule when the pump is OFF, right?

Do you think it is possible to adjust the Windows closed rule the way alexa tells me after a minute after the last window was closed, which windows are still open?
Like my wife closed 3 windows but forgot one in the attic :wink:
By the way - how can I ask which windows are open? I always get the answer Window xyz does not support this or something like this.

Yes.

I can’t help with Alexa stuff. I don’t use it.

But for your first use case you would need a new Rule that triggers when Member of gHContacts changed to CLOSED. Set a Timer there to send your alert using the same approach as “Window open NEW”

I’m using a switch to trigger a rule to build the sentence Alexa should say and have her speak it via Alexa control binding. I have a second function “long press” on a light switch near the main entrance so I can trigger it before leaving the house to check if all Windows are closed. I also can ask Alexa “did I close the windows”, but there’s no variation allowed. I can’t ask “are the windows closed”. For that I would need a second routine with exactly these words.

Hey guys, I am sorry but the rule does not work.
Alexa is not saying anything although there is no error in the log.
So Maybe there is some error in the definition of the items?
Lets look on one window - for example DB_ve

Group:Contact:OR(OPEN,CLOSED) gHContacts "Fensterkontakte Haus [(%d)]" <window>
Contact DB_ve  "Dachboden Velux [MAP(de.map):%s]" (gHFenster, gDB, gDBFenster, gBU, gHContacts, gDBContacts) { channel="knx:device:bridge:Tasterschnittstellen:DB_ve" } 

The “de.map” is like this:

CLOSED=zu
OPEN=offen
NULL=undefiniert

The definition of the pump is:

Switch EG_Vi_hkpump "Heizkreispumpe [%s]" <power> (gVitogate) { channel="knx:device:bridge:Vitogate:EG_Vi_hkpump" }

The rule is acually like this:

rule "Window open NEW"
when
    Member of gHContacts changed to OPEN
then
    if(EG_Vi_hkpump.state == OFF) return;
    if(stopMotionTimer === null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1) [ |
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im ' + openWins + '.')
        stopMotionTimer = null
    ])

end

rule "Windows closed NEW"
when
    Item gHContacts changed to CLOSED // happens when all windows are closed
then
    if(EG_Vi_hkpump.state == OFF) return;
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

I changed the rule a little bit from:

if(EG_Vi_hkpump.state != OFF) return;

to

if(EG_Vi_hkpump.state == OFF) return;

because I had the impression, that this is the reason why nothing comes. Furthermore my old rule which worked was defined like this:

if(EG_Vi_hkpump.state == ON){
    if(stopMotionTimer === null && gEGContacts.state == OPEN &&.....

But still no luck.
Any ideas? Maybe something with this %d and %s in my item definition where I still don’t know what the difference is?

this needs to be !==. With it being === that means stopMotionTimer never gets set to anything besides null and the Rule never runs.

This is deliberate. This line means that any time a window opens when the heat pump is not running there will be no alert.

What you had before was “only do something if EG_Vi_hkpump.state is ON”. This is “only do something if EG_Vi_hkpump.state is not OFF”. Hopefully you can see these mean the same thing.

Okay, understood.
I put it back to

rule "Window open NEW"
when
    Member of gHContacts changed to OPEN
then
    if(EG_Vi_hkpump.state == OFF) return;
    if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1) [ |
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg + ", " + winName ] // get list of windows
        openWins = openWins.replaceFirst(", ", "") // delete the first comma
        openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand('Die Heizung ist an. Bitte Fenster schließen im ' + openWins + '.')
        stopMotionTimer = null
    ])

end

rule "Windows closed NEW"
when
    Item gHContacts changed to CLOSED // happens when all windows are closed
then
    if(EG_Vi_hkpump.state == OFF) return;
    stopMotionTimer?.cancel // if stopMotionTimer isn't null, cancel it.
end

The Pump is on:
HK%20Pump

The window was opened at 8:52,27:

2019-02-26 20:52:27.953 [vent.ItemStateChangedEvent] - DB_ve changed from CLOSED to OPEN
2019-02-26 20:52:27.958 [GroupItemStateChangedEvent] - gDBContacts changed from CLOSED to OPEN through DB_ve

But after 1 Minute nothing…

2019-02-26 20:53:25.057 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 4.1 to 6.1
2019-02-26 20:53:25.879 [vent.ItemStateChangedEvent] - OG_voc_Sc changed from 1625.6000000000001 to 1765.1200000000001
2019-02-26 20:53:26.094 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 6.1 to 13.3
2019-02-26 20:53:27.148 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 13.3 to 2.9
2019-02-26 20:53:27.233 [vent.ItemStateChangedEvent] - EG_Strom_Dampfgar changed from 2.490000009536743 to 2.4700000286102295
2019-02-26 20:53:28.236 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 2.9 to 11.0
2019-02-26 20:53:29.287 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 11.0 to 7.1
2019-02-26 20:53:29.957 [vent.ItemStateChangedEvent] - EG_voc_wo changed from 2607.36 to 2961.92
2019-02-26 20:53:30.309 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 7.1 to 13.7
2019-02-26 20:53:30.612 [vent.ItemStateChangedEvent] - OG_voc_Mi changed from 853.12 to 1738.24
2019-02-26 20:53:31.073 [vent.ItemStateChangedEvent] - DB_voc_Bu changed from 1812.48 to 1945.6000000000001
2019-02-26 20:53:31.355 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 13.7 to 19.2
2019-02-26 20:53:31.366 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_memory_availablePercent changed from 42.6 to 42.5
2019-02-26 20:53:31.387 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_memory_usedPercent changed from 57.4 to 57.5
2019-02-26 20:53:32.038 [vent.ItemStateChangedEvent] - EG_voc_wo changed from 2961.92 to 3169.28
2019-02-26 20:53:32.424 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 19.2 to 11.4
2019-02-26 20:53:33.455 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 11.4 to 5.9
2019-02-26 20:53:33.969 [vent.ItemStateChangedEvent] - OG_voc_Jo changed from 3420.16 to 3164.16
2019-02-26 20:53:34.482 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 5.9 to 8.4
2019-02-26 20:53:35.438 [vent.ItemStateChangedEvent] - OG_voc_Ba changed from 2437.12 to 2035.2
2019-02-26 20:53:35.525 [vent.ItemStateChangedEvent] - systeminfo_computer_openHABianPi_cpu_load changed from 8.4 to 6.2

I have no idea what to do.

Edit:
But there is a new error in the openhab.log now:

2019-02-26 20:51:09.985 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'rules.rules'
2019-02-26 20:52:27.973 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Window open NEW': An error occurred during the script execution: index=1, size=1

Although I took out as much as I understood from this rule, still the same error in the log.

rule "Window open NEW"
when
    Member of gHContacts changed to OPEN
then
    //if(EG_Vi_hkpump.state == OFF) return;
    //if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1) [|
        // Generate a list of all the open windows
        var openWins = gHContacts.members.filter[ w | w.state == OPEN ].map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg + ", " + winName ] // get list of windows
        //openWins = openWins.replaceFirst(", ", "") // delete the first comma
        //openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im " + openWins + ".")
        stopMotionTimer = null
    ])

end

Add logging to the Rule and the Timer and log out all the relevant values and Item states. Compare those with the code so you can see what it is actually doing.

So you know that the error is coming from one of the three remaining lines. It’s talking about an index and the only line that deals with an index is the filter map reduce line.

So split that line into multiple lines and log out what each part returns so you can figure out if the error is coming from the filter, the map, or the reduce.

OK, I did this:

rule "Window open NEW"
when
    Member of gHContacts changed to OPEN
then
    //if(EG_Vi_hkpump.state == OFF) return;
    //if(stopMotionTimer !== null) return;

    // Set the timer
    stopMotionTimer = createTimer(now.plusMinutes(1) [|
        // Generate a list of all the open windows
        //var openWins = gHContacts.members.filter[ w | w.state == OPEN ]//.map[ transform("MAP", "windows.map", name) ].reduce[ msg, winName | msg + ", " + winName ] // get list of windows
        //openWins = openWins.replaceFirst(", ", "") // delete the first comma
        //openWins = openWins.replaceLast(", ", "und ") // replace the last comma with "und"

        Echo_TTS.sendCommand("Die Heizung ist an. Bitte Fenster schließen im "/* + openWins + "."/*/)
        stopMotionTimer = null
    ])

end

Not much left from the code but still the index error.
Maybe this Member of gHContacts changed to OPEN is wrong somehow?
Remember my definition of the group?

Group:Contact:OR(OPEN,CLOSED) gHContacts "Fensterkontakte Haus [(%d)]" <window>

And an example of a window:

Contact EG_wo_fe_li1  "Wohnen Links Auf [MAP(de.map):%s]" <window> (gEGContactsOffen, gEGContacts) { channel="knx:device:bridge:Tasterschnittstellen:EG_wo_fe_li1" } 
Contact EG_wo_fe_li2  "Wohnen Links Kippe [MAP(de.map):%s]" <window> (gEGContactsKippe, gEGContacts) { channel="knx:device:bridge:Tasterschnittstellen:EG_wo_fe_li2" } 
String EG_wo_fe_li "Wohnen Links [MAP(de.map):%s]" <contact> (gHFenster, gEG, gEGFenster, gWO, gHContacts)

Maybe there is a problem with the string?
There is a rule for each String:

rule Fenster_Wohnen_links
when
        Item EG_wo_fe_li2 changed or
         Item EG_wo_fe_li1 changed
then
        if (EG_wo_fe_li2.state == CLOSED && EG_wo_fe_li1.state == CLOSED) {
          EG_wo_fe_li.postUpdate(0)
        } else if (EG_wo_fe_li2.state == OPEN && EG_wo_fe_li1.state == CLOSED) {
           EG_wo_fe_li.postUpdate(1)
        } else {
           EG_wo_fe_li.postUpdate(2)
        }
end

The post update(0, 1 and 2) is translated in “de.map” into:

2=offen
1=gekippt
0=geschlossen

You’ve done everything but what I asked you to do. Reread my previous reply and add logging.

I would love to do what you asked for but I don’t know how. I do only know the openhab.log and events.log This is what I already posted.
Maybe you can give me a hint and I will try to do this.