Run a forEach once per object per hour

Hi,

I have a rule that notifies me if a window is open for too long (> 30 minutes) and it’s cold outside (< 15 Celsiuis). It looks like this and - in general - works:

rule "window open for too long"
when
	Time cron "0 * * * * ?" //once a minute
then
	if(Weather_Temperature.state < 15) {
		var contact = gWindows?.members.filter(s | s.state != CLOSED)
		if(!(contact.isEmpty)) { //are there any windows open?
			contact.forEach[c |
				if(!(c.changedSince(now.minusMinutes(30), "rrd4j"))) {
					logInfo("RULE", "--> Fenster offen und außen kalt: " + c.label+ " ist seit über 30 Minuten offen")
					sendBroadcastNotification(c.label + " ist seit über 30 Minuten offen")
				}
			]
		}
	}
end

Now as you can imagine, it would fire every minute again and again as long as the window is open. So I want to make it notifying me only once per window for a specific time, like once per hour.

Ideas:

  1. create a “alreadyNotified” variable. Set it to True after it fired and then set it to false with another rule after one hour. Problem: It would stop for all windows.

2 . Create a similar variable, but one for each window and react on it.
Problem: Much work and not nice as the forEach was make to be able to “scale” easily…

Is there a way to create a kind of dynamic variable for each, so I wouldn’t need to specify it? Or probably another way I didn’t think of?

Thanks in advance
Viktor

For your idea number 2, you could use a hashmap, with the item name as key. If you are unfamiliar with these, there are a number of examples on the forum, e.g. https://github.com/openhab/openhab/wiki/Taking-Rules-to-New-Heights

Hi,
thanks for your reply. I’ll try to get throuh it and understand it. Seems to be not that easy :smiley:

Cheers,
Viktor

Is there an easy to understand guide for hashmaps somewhere? I can’t find anything or I don’t understand it :slight_smile:

A hashmap is basically a way to achieve your collection of ‘dynamic variables’. You first define the collection - e.g:

val java.util.Map<String, boolean> mWindowAlerts = newHashMap

This will create a collection of ‘variables’, each identified by a unique String as key, with an object of type boolean.

Once the collection is defined, you can check for (using .get method), add (using the .put method) or remove (with the .remove method) ‘variables’ as you need. For example, in your forEach loop, you could check if the key for the current contact is already there in the collection by:

if (mWindowAlerts.getKey(c.name) != null) {...

If you then wanted to add a new item to the collection to store that an alert has already been sent, you could do:

mWindowAlerts.put(c.name, true)

(Note that we are using the name of the item - c.name - as the key or name of the ‘dynamic variable’.)

I hope that gives you a quick intro. There are plenty of tutorials online, e.g. https://www.tutorialspoint.com/java/java_hashmap_class.htm

Cool, thanks a lot!
I think I got it and will try it now :slight_smile:

cheers

I wrote a couple of generic design patterns for this:


The tl;dr is create a hashMap keyed on the Item name of Timers. The timer gets scheduled when the door opens. If it closes before the time goes off the timer is canceled. If the timer goes off it sends an alert then recreates itself to go off in another hour.

An alternative way is to create a rule that triggers with a cron trigger periodically (every five minutes maybe). Then, using persistence loop through the Items and send an alert for those that are OPEN and have a lastupdate of over an hour ago. Then take a timestamp put into a hashMap. For any Items open for over an hour check the timestamp in the map and when that gets over an hour old send the message again and save a new timestamp.

In all of these cases, you use the Item.name as the key so you can still use your forEach loops and do not have to create a separate variable for each Item