[SOLVED] OH3 - Rule creation - Group state item

Dear community,

I got a quick question on creating group state items. I have created two items that use the network binding to derive the presence of each smartphone. Here is the item config:

Switch Anwesenheit_Phone1 "Phone 1 Anwesenheit" <icon> (gAnwesenheit) {channel="network:pingdevice:192_168_XXX_001:online"}
Switch Anwesenheit_Phone2 "Phone 2 Anwesenheit" <icon> (gAnwesenheit) {channel="network:pingdevice:192_168_XXX_002:online"}

I created now a group switch which is supposed to set to ON once both item switches are turned to ON (= both phones are present). Here is the group item config:

Group:Switch:AND(ON,OFF) gAnwesenheit "Gruppe Anwesenheit" <icon>

Now so far everything works great. The switch of the group icon turns to ON once both items are in state ON. However, the group item turns OFF once one of the items turns OFF.

From a syntax perspective, everything works as it should. However, I would like to have the switch also only turn OFF, if both phones are not present.

How can I achieve that?

I think you’re saying you want the Group state to depend on previous state? i.e. if all members ON, Group ON, if all members OFF, Group OFF, if some members of each, Group to stay the way it was (whatever that was).

There’s no aggregation function for that, you’d have to construct a rule.
You might use a dummy Item and put your members in two Groups, one AND(ON,OFF) and one AND(OFF,ON). When either of those groups changes to the AND state, change your dummy Item.

Hey @rossko57, thanks a lot! It actually opened my mind to realize, that I will only need one virtual switch, which changes based on presence. Hence I now created a switch which turns ON if either of the persons is present and turns OFF only if both persons are not present. Here is what I have in the config of the switch:

Group:Switch:OR(ON,OFF) gAnwesenheit "Gruppe Anwesenheit" <presence>

I have now however a question which relates to this switch. The status of this switch is based on the presence of two items. Now say one phone is gone shopping or whatever, I have the behavior, that my phone turns OFF for around 30 seconds (duration of the ping item to re-check) form time to time, which makes the virtual switch turn OFF as well for a few seconds. Hence it triggers a rule to lock the door even I am still at home.

Now my question is, if I can loop a check in a rule for - say - 40 seconds to check again. So if it detects that the switch is OFF, check again in 40 seconds and if still TRUE, run the action. Is that possible? Here is what my rule currently looks like:

rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item gAnwesenheit changed from ON to OFF //No one is at home
        then
            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt") //Log a note in the logfile
            if (Schloss_Eingang_Status.state !=4) { //If lock is not in state 4 (= locked)
                Schloss_Eingang_Status.sendCommand(4) //Lock the door
            }
            sendNotification("Mailaddress", "WohnungstĂĽr abgeschlossen") //Ping me a notification
        end

Don’t do that, waste of resource.
This is an event-driven system.
Use a timer.

Typically, you’d start a 40-second Timer when everything went OFF, and set the timer up to do whatever it is you want.
When something comes ON, cancel the Timer if it is running.

It’s just the usual door-timer application

Thanks again for the quick feedback. Honestly I am a bit lost with those timers and especially with setting some var in the beginning. Never done that. :sweat_smile:

I just wonder if this would work:

        rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item gAnwesenheit changed from ON to OFF
        then
            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt")
            Thread::sleep(45000)
            if ((Schloss_Eingang_Status.state !=4) && (gAbwesenheit.Status == OFF) {
                Schloss_Eingang_Status.sendCommand(4)
            }
            sendNotification("Mailaddress", "WohnungstĂĽr abgeschlossen")
        end

Else, if you think this is the wrong way of doing it, how can I “learn” my way into those val/var and timer things? It will be relevant - I guess - anyhow if I want to use it in a proper way.

Well, I couldn’t hold it and tried to read my way into it. Here is what I came up with:

var Timer Abwesenheits_timer = null

        rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item gAnwesenheit changed from ON to OFF
        then
            Abwesenheits_timer?.cancel //Das ? ĂĽberspringt diese Zeile, wenn der Abwesenheits-Timer = 0 ist
            switch gAnwesenheit.state {
                case "OFF": {
                    Abwesenheits_timer = createTimer(now.plusSeconds(45),
                        [if (Schloss_Eingang_Status.state !=4) {
                            Schloss_Eingang_Status.sendCommand(4)
                            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt")
                            }
                        ]
                    )
                }
                case "ON": {
                    Abwesenheits_timer.cancel()
                }
            }
        end

Maybe you want to have a look at it and have a comment.

The important thing is, does it work?

You might like to think about…

        when
            Item gAnwesenheit changed from ON to OFF

can this case ever be true??

                case "ON": {

You probably want just a unqualified changed trigger.

Or maybe no ON case at all - what is it doing, haven’t you already done just that?

So you are saying that I should probably just qualify the when on a simple change like:

when
     Item gAnwesenheit changed

And then I will use the then expression to check what kind of change happens (case). Being:

case “OFF“: { //validate if OFF which means no one is present, then apply a timer and run again

case “ON“: { //validate if ON which means someone is present and if TRUE cancel timer

Correct?

Here is what I have right now:

var Timer Abwesenheits_timer = null

rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item Gruppe_Anwesenheit changed
        then
            Abwesenheits_timer?.cancel //Das ? ĂĽberspringt diese Zeile, wenn der Abwesenheits-Timer = 0 ist
            switch Gruppe_Anwesenheit.state {
                case "OFF": {
                    Abwesenheits_timer = createTimer(now.plusSeconds(50),
                        [if (Schloss_Eingang_Status.state !=4) {
                            Schloss_Eingang_Status.sendCommand(4)
                            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt")
                            }
                        ]
                    )
                }
                case "ON": {
                    Abwesenheits_timer.cancel()
                    logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - nicht ausgefĂĽhrt")
                }
            }
        end

You just did that before even entering the switch-case selection. It won’t be any deader by killing it twice.

Hi @rossko57, thanks for the feedback - again! I was enjoying a long weekend, hence did not tackle the topic.

I have two questions :slight_smile:

  1. I do not quite get, where I did that timer cancellation before already. Can you maybe elaborate on the steps, which my rule is doing now and where it already cancels the timer?

  2. Given the rule above, which is in place for now, basically nothing happens anymore. Even if both, my girlfriend and myself leave home, nothing happens. No trigger fires and the door stays closed but not locked. Any idea why? Anything else I should provide?

Here is again, what the rule looks like at the moment:

rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item Gruppe_Anwesenheit changed
        then
            Abwesenheits_timer?.cancel //Das ? ĂĽberspringt diese Zeile, wenn der Abwesenheits-Timer = 0 ist
            switch Gruppe_Anwesenheit.state {
                case "OFF": {
                    Abwesenheits_timer = createTimer(now.plusSeconds(50),
                        [if (Schloss_Eingang_Status.state !=4) {
                            Schloss_Eingang_Status.sendCommand(4)
                            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt")
                            sendNotification("Mailaddress", "WohnungstĂĽr abgeschlossen - niemand zuhause")
                            }
                        ]
                    )
                }
                case "ON": {
                    Abwesenheits_timer.cancel()
                    logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - nicht ausgefĂĽhrt")
                    sendNotification("Mailaddress", "WohnungstĂĽr abschlieĂźen abgebrochen - jemand zuhause")
                }
            }
        end

Don’t feel bad about following it with your finger.

when
your rule is triggered
then
the first thing it does, every time it runs, is -
Abwesenheits_timer?.cancel
and the next thing it does after that is the switch-case selecting
switch Gruppe_Anwesenheit.state {

As I said, once the Timer is cancelled, it’s cancelled.

The funny-looking ? placed in there serves a purpose - it says “only do this if not-null”. That’s useful here because if you try to cancel a Timer that does not exist, it (quite rightly) fails.
When this rule starts there may or may not be a pre-existing timer.
Abwesenheits_timer?.cancel
boils down to “if it exists, cancel it”.

Further down in your rule …

It’s already dead, stop hitting it.
Note also that this version will throw an error when the Timer does not yet exist.

Maybe the part that is not clear is that when you use createTimer() it makes a Timer outside of, and independent of the rule. The rule doesn’t stop and wait, it carries on and usually exits long before the Timer executes.
That means of course that you can run the rule again before the Timer has executed, that’s how you’re going to cancel it.


None. Why don’t you find out? All the tools are at your disposal, use them.

Apply some logic - if the rule does nothing, is it even loaded? I don’t know, find out.
After you edit your xxx.rules file, you should get a message in your openhab.log to tell you its been loaded or refreshed. Can you find that? If it doesn’t like your syntax and can’t make a working rule out of it at that point, it will tell you.

If you’re happy that your rule is loaded, but the rule does nothing, is it even running? I don’t know, find out.

rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item Gruppe_Anwesenheit changed
        then
            logInfo("test", "My rule has triggered")
            Abwesenheits_timer?.cancel //Das ? ĂĽberspringt diese Zeile, wenn der Abwesenheits-Timer = 0 ist
            ... etc ...

If you don’t get the log message, you know that you need to look into the triggering condition. Have you misspelled the Item name? Does the Item actually change when you thought it should? I don’t know, find out - look in your events.log which will tell you about every Item change and how the system thinks the Item is named.

From there, you’ll know whether to look further into your rule or instead at what your Items are doing, etc.

EDIT - late observation

You’ve missed out a non-obvious character from the example you found. createTimer() syntax requires a | pipe character inside the [ ] square brackets.
Some technical explanation about lambdas functions, but just take it that like every example you’ve seen, you need a structure [ | your code ]

                    Abwesenheits_timer = createTimer(now.plusSeconds(50), [ |
                        if (Schloss_Eingang_Status.state !=4) {

personally I put it on the createTimer() line so the code’s easier to read.

I skip then “thank you for now” :slight_smile:

I understood the first part about killing something that’s already dead. Whenever the state changes, it checks for a running timer and cancel one, if it is there. And only after, it checks for the state to see if there needs a new trigger to be started. Clear!

To cover your evaluation steps, let me confirm:

  1. Rules-File is loaded, yes CONFIRMED
  2. Rule is also running - I placed the logInfo right after the “then” and received the feedback in the log. CONFIRMED
  3. Nothing more happens. I see the related switch (Gruppe_Anwesenheit) changes from ON to OFF but besides the log that the rule was triggered, nothing else happens.

I do not know where to pick up again. I would have expected, that now, since it switched to OFF and no further changes happen, this part should have been executed - with a timer delay of 50 seconds:

Abwesenheits_timer = createTimer(now.plusSeconds(50),
                        [if (Schloss_Eingang_Status.state !=4) {
                            Schloss_Eingang_Status.sendCommand(4)
                            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - ausgefĂĽhrt")
                            sendNotification("Mailaddress", "WohnungstĂĽr abgeschlossen - niemand zuhause")
                            }
                        ]
                    )

I also checked that the if-rule condition (Schloss_Eingang_state !=4) is TRUE, which it is, as the state is 3.

What else can I check?

Please fix this part.

Again,if you had no other clues you would need to find out. For example you can place
logInfo("test","Creating timer")
and
logInfo("test","Executing timer code")
in appropriate places. This narrowing down by following progress isn’t rocket science.

Done, thank you! Missed it!

Thanks again. I made up my mind inserting a lot of logs and it hit the same stage. Nothing happened. But then I realized that maybe something with the switch cases is wrong. It was the quotation symbols that prevent anything further from happening. Since it is a switch it required
ON
instaed of
"ON"

Now it works like a charm. In case someone returns here later here is the completed code for the rule:

rule "WohnungstĂĽr abschlieĂźen bei Abwesenheit"
        when
            Item Gruppe_Anwesenheit changed
        then
            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - wurde angestoĂźen")
            Abwesenheits_timer?.cancel //Das ? ĂĽberspringt diese Zeile, wenn der Abwesenheits-Timer = 0 ist
            switch Gruppe_Anwesenheit.state {
                case OFF: {
                    Abwesenheits_timer = createTimer(now.plusSeconds(50), [ |
                    if (Schloss_Eingang_Status.state !=4) {
                            Schloss_Eingang_Status.sendCommand(4)
                            logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - Timer beendet, TĂĽr abgeschlossen")
                            sendNotification("xxx@yyy.com", "WohnungstĂĽr abgeschlossen - niemand zuhause")
                            }
                    ]
                    )
                    logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - Timer gestartet")
                }
                case ON: {
                    logInfo("Rule Log", "Regel - WohnungstĂĽr abschlieĂźen bei Abwesenheit - Anwesenheit festgestellt - Timer beendet")
                }
            }
        end
1 Like

@rossko57 Thanks a lot for your intense support. Quick, easy to follow, relaxed, … Awesome!