Timers and code questions

I have a few lines of code I have borrowed here and there to make things work but I want to understand how they work so I can write better and more correct scripts/rules. Can someone breakdown example 1 a little better as to what exactly it is doing please? I mean I know what the end result is I just don’t understand how it works.

  1. if(gMobiles.members.filter(s | s.changedSince(now.minusMinutes(5))).size == 0) {Do something}

I also have a motion sensor in my bedroom and used a waitTimer to turn the light off after I leave the room, I had to add extra cancel and null statements in the beginning of the then statement or I found that each time I walked in it would start a new 5 minute timer and turn off the light even if I was walking around. Is there a better way to do this?

--------BedroomMotion.rules---------
import org.openhab.model.script.actions.Timer
var Timer bwaitTimer=null

    rule "Bedroom motion sensor"
    when
        Item Upstairs_Bedroom_Motion changed from CLOSED to OPEN
    then
    if (MyBedroomLamp_Toggle.state==ON) {
    bwaitTimer.cancel
    bwaitTimer=null
    }
    if (MotionSensors.state==ON) {
    sendCommand(MyBedroomLamp_Toggle, ON)
    sendCommand(MyBedroomLamp_Brightness, 100)
            bwaitTimer = createTimer(now.plusMinutes(5)) [|
        if (MotionSensors.state==ON) {
        sendCommand(MyBedroomLamp_Toggle, OFF)
        }
            ]
        } else {
            if(bwaitTimer!=null) {
                bwaitTimer.cancel
                bwaitTimer=null
            }
        }
    
    end

Last question how is the waitTimer different from the s.changedSince(now.minusMinutes(5))).size ==0 from the first statement.

Thanks for any help, it is much appreciated.

1 Like

First of all, could you please correct the formatting of your posting above? It is exceptionally difficult to read plain text formatted like code in the posting.

Remove the indentation from the lines and it will wrap them appropriately.

The if statement is a way to optionally execute some code only if a conditional evaluates to true. In this case, the conditional is the “gMobiles…”.

if(<conditional>) {
    // do something
}

The conditional in this case is executing a filter on the members of a group and seeing if any have changed in the last five minutes.

  • gMobiles : this is a group defined somewhere in a .items file. A common naming convention is to name groups starting with a lowercase ‘g’ to distinguish them from Items which are usually named with starting caps and camel case or dashes (e.g. MyItem or My_Item). Presumable one or more Items are members of the gMobiles group and these items represent information about a Mobile phone (I’m guessing a switch or contact which is ON or OPEN when the phone is present).

  • gMobiles.members : The .members part calls a method on the group that returns a List (i.e. a list containing containing Item objects) all of the members of gMobiles. A list is like an array with some extra capabilities built in. There is a sister method called allMembers which will get all the members of subgroups and give you one big list verses members which will give you the subgroup Item only.

  • gMobiles.members.filter : One of those extra capabilities built into the List is the ability to filter the list down to a smaller list containing only those items that match a certain criteria. That is what the filter does.

  • gMobiles.members.filter(s | s.changedSince(now.minusMinutes(5))) : This adds in the filter critera. A lot of magic is going on here. The filter method expects a function, technically referred to as a lambda, to be passed to it as its argument, a function that returns a boolean. Whenever you see a ‘|’ or a “[ | ]” (with stuff in between the brackets) you are looking at a lambda. The stuff to the left of the ‘|’ are the function’s arguments and the stuff to the right is the code that makes up the function. In a filter’s case you usually have one argument and one statement that returns a boolean. The function is called with each item that is in the List returned by members and those for which the function returns true are included in the List that is returned. In this case, each item is passed to the lambda as “s” and the function returns true only for those items that have changed within the past five minutes. The now is a reference to a joda DateTime object that gets populated with what ever the time happens to be when it is referenced so now.minusMinutes(5) will give you a DateTime for five minutes ago.

  • gMobiles.members.filter(s | s.changedSince(now.minusMinutes(5))).size == 0) : Up to this point we have grabbed all the members of the gMobiles group and filtered them down to a List containing only those that have changed in the last five minutes. Now we check to see if the list has anything in it. If the list is empty then you know that no members of the gMobiles group has changed in the past five minutes in which case the code between the “{ }” is executed.

I would just keep rescheduling the timer every time motion is detected. That way the timer will execute only after five minutes after the last time motion was detected.

I can’t really tell what is going on with Upstairs_Bedroom_Motion verses MotionSensors but I’m guessing that even if you are not in the room but walking around elsewhere you want to keep the light on? Is MotionSensors a group of all your sensors?

Anyway, to simplify things for the example which you can expand later to deal with whatever is going on here I’ll just go with the one Upstairs_Bedroom_Motion sensor. To add in the MotionSensors add a check to the body of the timer lambda (see above) to check if it is on and reschedule the bwaitTimer instead of setting it to null if it is ON and set bwaitTimer to null if it is OFF.

val Timer bwaitTimer = null
rule "Bedroom motion sensor"
when
    // This assumes it goes back to CLOSED at some point
    Item Upstaitrs_Bedroom_Motion changed from CLOSED to OPEN
then

    // Timer already set
    if(bwaitTimer == null) {
        MyBedroomLamp_Toggle.sendCommand(ON)
        MyBedroomLamp_Brightness.sendCommand(100)

        bWaitTimer = createTimer(now.plusMinutes(5), [|
            MyBedroomLamp_Toggle.sendCommand(OFF)

            // null out the timer so we know it isn't running anymore
            bwaitTimer = null
        ]
    }

    else {
        bwaitTimer.reschedule(now.plusMinutes(5))
    }
end

As described above, that whole line is getting all the items that are a member of a group that have changed within the last five minutes and returning true if there are none (see above).

Setting a timer is a way to schedule a chunk of code (a lambda) to execute in the future. So in your code above, the stuff between the “[|” and “]” will execute in five minutes.

3 Likes

Thanks for the reply, I fixed the formatting and I am still digging through all the information.

To further elaborate I have a switch called MotionSensors and when it is turned on I would like my lights to be controlled via the motion sensor rules, if I just want to control it manually I turn that off. I still haven’t implemented your nifty Manual vs script code yet.

Thanks for all your advice, this will clean up my bedroom sensor code and help me write better stuff in the future. I had seen the word lambda thrown around here and there but did not understand how it worked exactly. Most of the other stuff I generally understood but seeing it broken down really helped.

Another quick question, What does the s stand for in the lambda?
(s | s.changedSince…

It is just a variable name. It can be anything but similar to the way people tend to use “i” for the index in a for loop, a lot of people use “s” to stand for “switch” in a filter statement. If I had a bunch of contacts that were members of gMobile I would probably use “c” as in (c | c.changedSince… instead of “s”.

Thanks again good sir. I really do appreciate the help.