Fine tuning of wifi presence detection

Hi Rick,

Thanks for this answer.

So now I have understood that there is not a simple url command I can just enter in any of those location Apps like Locative or Geofency which then will report my location via myopenhab to my OH2 server.

Correct?

It cannot be done. A String is a Sting. A SwitchItem is a SwitchItem. They have almost nothing in common. Casting is not some magic operation that does a lot of extra work to convert one type of Object into another. It only lets you say, for example, ā€œtreat this object which is of types Object, State, DecimalType, and Number as if it were a Numberā€. The Object already has to be of those types.

You can. If you have the actual Group you can call:

gMyGroup.sendCommand(ON)

and all of the members of that Group will receive the ON command.

You cannot however do the following:

val groupName = "gMyGroup"
groupName.sendCommand(ON)

because groupName is just a String, not a GroupItem. Therefore there is no sendCommand method to call on it.

I donā€™t know these apps but if they only support HTTP GET commands then the answer is yes.

Understood, Thanks for take time teach me the basic knowledge,
one more question, you create timer base on item name timers.get(sw.name).reschedule(now.plusMinutes(timeoutMinutes))
I try to create timer and base on item name, please tell me how should I write on script?

At the top of the code you will see a val Map<String, Timer> timers = newHashMap. A Map is a way to store key/value pairs. I use the Item name as the key and the Timer as the value. So to put that line in English: Get the value (i.e. Timer) paired with the key (i.e. sw.name) and call reschedule.

The Timer is not created based on the Item name. It is just stored in a Map that way.

Of course, you need to store the Timer in the Map assocaited with the Item name. And that is exactly what you are doing on timers.put(iphoneProxy.name, createTimer(now.plusMinutes(3), [|.

So given you are already doing this I donā€™t understand your question.

Finally, I highly recommend looking into the Expire binding. It will greatly simplify this code.

Thanks rich, Iā€™ll take your advise,
my previous question is I try on counter way so as var Number counter 1 = Null
and use counter1 = counter1 + 1 as another approach way,
but I found I donā€™t know how to let rules to store individual counter when difference item in use the rule
so I see when timer you use

Map<String, Timer> timers = newHashMap.

, than I am thinking is there a similar way to store counter value,something like below

 Map<String, Number> counters= newHashMap

.
as I donā€™t know much about the casting way, so I make the question

Should be var Number counter1 = 0

Otherwise, you will get an error when you try to counter1 = counter1 + 1.

Was that your error?

Letā€™s step back a second because your code is WAY more complex than it needs to be. This is why I keep suggesting the Expire binding. If I understand the code correctly you want an alert when your iPhone is away for 3 minutes or more and another alert when the iPhone comes back. And you have more than one iPhone you are tracking.

So letā€™s drop all the timers, and all the counters and add apply my Generic Presence Detection example.

So if I expand that example to include a way to detect when individuals leave or arrive rather than just generic someone is home:

Switch Present "Someone is Present" <present> // master presence switch
Group:Switch:AND(OFF,ON) gPresent <present> // all presence sensors belong to this group
Switch Present_Timer { expire="3m,command=OFF" }

Group:Switch:AND(OFF,ON) gHamPresent <present>
Switch Ham_Present <present> // master Ham present switch
Switch Ham_Present_Timer { expire="3m,command=OFF" }

Group:Switch:AND(OFF,ON) gPerson2Present <present>
Switch Person2_Present <present> // master Person2 present switch
Switch Person2_Present_Timer { expire="3m,command=OFF" }

Switch Ham_iphone (gPresent, gHamPresent)
Switch Ham_otherSensor (gPresent, gHamPresent)
...
Switch Sensor1Person2 (gPresent, gPerson2Present)
Switch Sensor2Person2 (gPresent, gPerson2Present)
...

Rules:

import org.eclipse.xtext.xbase.lib.Functions

val Functions$Function3 <GroupItem, SwitchItem, SwitchItem, Boolean> processPresence = [grp, sw, timer |
    // came home
    if(grp.state == ON && sw.state != ON) {
        timer.postUpdate(OFF) // cancel the timer if necessary
        sw.sendCommand(ON)
        sendBroadcastNotification(sw.label + " returned home")
    }

    // not home
    else if(grp.state == OFF and sw.state != OFF){
        timer.sendCommand(ON) // start the timer
    }
    true
]

val Functions$Function1 <SwitchItem, Boolean> processTimer = [sw |
    sw.sendCommand(OFF)
    sendBroadcastNotification(sw.label + " has left home")  
    true
]

rule "System started"
when
    System started
then
    // assume no one is home on startup
    Present.sendCommand(OFF) 
    Ham_Present.sendCommand(OFF)
    Person2_Present.sendCommand(OFF)
end

rule "gPresent updated, at least one sensor changed state"
when
    Item gPresent received update
then
    processPresence.apply(gPresent, Present, Present_Timer )
end

rule "gHamPresent updated, at least one sensor changed state"
when
    Item gHamPresent received update
then
    processPresence.apply(gHamPresent, Ham_Present, Ham_Present_Timer )
end

rule "gPerson2Present updated, at least one sensor changed state"
when
    Item gPerson2Present received update
then
    processPresence.apply(gPerson2Present, Person2_Present, Person2_Present_Timer )
end

rule "Present_Timer expired"
when
    Item Present_Timer received command OFF
then
    processTimer.apply(Present)
end

rule "Ham_Present_Timer expired"
when
    Item Ham_Present_Timer received command OFF
then
    processTimer.apply(Ham_Present)
end

rule "Person2_Present_Timer expired"
when
    Item Person2_Present_Timer received command OFF
then
    processTimer.apply(Person2_Present)
end

Iā€™m certain there are ways to combine some of this code but this is already a lot simpler logic. By moving the Timer logic to the Timer Items and the Expire binding we no longer have to do any bookkeeping at all. All the issues with casting, keeping track of timers or counters disappears and you just have a few simple rules which Iā€™ve combined the logic for using lambdas. No longer are there global vars, nested if else statements, etc.

And this logic allows you to easily add more sensors for presence detection without ever needing to change your rules again. You just need to add the sensors to the right groups.

3 Likes

Dear rich
Huge thanks for taking time reply me and yes, all your guess is correct, and thanks for writing the code for me,
I just type the var number in memory didnā€™t do the copy form files, so thats a typo
I saw the code you write for me in a quick view, in logic part it looks much more simple than before, itā€™s great and see you use more rules to update the status,

I got a basic question that would probably improve my mind in writing script in future,
when writing rules to do logic, for example should I use 10 rules (10 trigger) with simple logic to do a thing or write a complex logic and 1 rules (1 to 2 trigger) to do a thing? which will be more benefit for OH?

I think this question should also help someone not familiar with coding.

Thank you again, best regards

Forget OH. What is more benefit to YOU? OH doesnā€™t care. What matters is what is easier for you to write and understand? What is easier for your to update in the long run?

Generally, it is best to apply Donā€™t Repeat Yourself (DRY) so if you have ten rules all with the same copy and paste code you should look for ways to either combine the rules or consolidate the logic (e.g. like I did above with the lambdas). This lets you, for example, have only one place to look and one place to change when you want to update the code later.

But of primary importance is writing your rules so you yourself understand them.

1 Like

:sweat_smile:
I just start learning to write script, for a fresh new learner/beginner, simple and duplicate is easy start as script is not hard to learn with simple command,
your way is more advanced, I try to learn from you and other posterā€™s code and try to understand how to write in advanced mode.

The reason I ask what is the right way to write script due to I am afraid too much rules will slow down the OH system. so at the starting, I write it with fewer trigger way as I donā€™t know which way will affect more, my OH only runs on RPi3.

Thank you for your time. Best Regards

Donā€™t worry about performance until performance actually becomes a problem. Especially while you are still learning. You are unlikely to have too many rules or rule triggers in a typical home automation configuration, even when running on a RPi3.

Focus on learning how to code the rules and writing in a way that you can understand. As you learn go back and revisit your rules and make improvements.

yes I am, that is exactly I try to write this script in group detection way,
I first start with 1 rule 1 phone,
than few days ago your example code of turn off motion sensor Inspire me, so I try to do it in above way, in fact I have tried difference approach before post here,
the code you wrote for me also inspire me a lot, I am trying to under it and want it to use it in other rules.

Thank you again and again, have a nice day. :slight_smile:

I am not using any of those applications but you can do this with Tasker (Android only app) and the HTTP(s) POST action. I authenticate using a username/password although I would prefer a certificate authentication but currently not supported ā€¦ Tasker scans the wifi networks and whenever I walk home and pass by all the neighbouring wifi networks it signals openhab that I am in the neighborhood. Pretty cool

Thanks for the hint.
Unfortunately I am using an iPhone.

I am not an iphone user myself but maybe [iCurl] (https://itunes.apple.com/us/app/icurl/id480568361?mt=8) ??

Hi Christos,
I do not see that iCurl can trigger a curl command based on location change.
Or did I overlook this feature?

The code is exactly what I am looking for. However, the designer gives me a lot of errors already starting on the first line:

val Functions$Function3 <GroupItem, SwitchItem, SwitchItem, Boolean> processPresence = [grp, sw, timer |

The logfile does not talk about it at all, no complaints about the rule. Single present switches receive updates, so the underlying item conf seems to be ok.
Since I am on OH 2 stable I assume you are running a newer version which would explain the errors, even the designer 0.9 still reports errors.
May I ask what version (snapshot?) your system is running on?

0.9 is known to be buggy and even in the recommended (for now) 0.8 version there are some glitches.

Thatā€™s good, so I would not worry about the designer errors. It is still a good tool to catch typos and using it for basic setups ā€¦

With lambdas, there can be one error that marks the whole thing as error. I suspect that is what is going on here. The biggest clue is it is marking the comment lines as errors as well. What is the error reported on the first line of the lambda when you hover your mouse over the red dot to the left? Sometimes the error can be caused by something before the lambda and not even inside the lambda itself.

Does the lambda work as expected?

Iā€™m running 2.0 release. As @sihui reports, you should use Designer 0.8 for now. 0.9 is basically broken. 0.8 does have some missing functionality, mainly it marks certain new constructs like channel triggers as well as non-default actions as unknown but otherwise 0.8 works pretty well. The problem with 0.9 is that nothing having to do with Items works. So all your Items will be marked as unknown in Rules, the Item browser is empty, etc.

What is the error reported on the first line of the lambda when you hover your mouse over the red dot to the left? Sometimes the error can be caused by something before the lambda and not even inside the lambda itself.

Error in the first line is:
Note: This element has no attached Javadoc and the Javadoc could not be found in the attached source.

Looks like a more generic binding / translation or any maybe necessary installation is missing?

That actually isnā€™t an error. Itā€™s a warning and can safely be ignored. It also has nothing to do with why the lambda is being marked as an error.

Is that the only thing that appears when you hover the mouse over line 3?