Arriving home - detecting phones which are arriving to Home

Dear Everyone!

I just wondered if anyone did something like this before. So what I have already:

  • WiFi detection (perfect for fast-detecting Home)
  • iCloud/OwnTracks for mobile phone location / info
  • A switch which is toggled by these two things, based on I’m Home or not.

What I would need is detect if someone is arriving home (so the position is changing and getting closer) and is in the given radius. So everything is given, I just need to write some rules, however I have some questions:

  • Is it possible to persist Location type? I have read somewhere that you can’t persist these explicitly, you need to persist the latitude and longitude. How? Write a rule which persist the data?
  • Can I access easily previous persisted data? Like previous data, n-2 data, etc…?

Hi Kristof
I‘m doing something like you described.
We‘ve two iPhones that are used with WiFi, iBeacons and the iCloud location.
As i‘m on mobile right now i can‘t give you examples.

  1. The iCloud Location is pulled every 5 minutes, when one of the phones sees my Beacon or when the phone didn‘t see the Beacon for 5 minutes.
  2. The Location is stored in an mysql database persisted with the jdbc-mysql service.
    As far as i know this works without a problem
    but you can‘t really query that data as the location is far to precise to make an exact check. Maybe with String formatting like removeEnd.
  3. When someone returns home i‘m checking if he is now at home (geofence or WiFi), wasn‘t 30 minutes ago and hasn‘t been welcomed in the last 30 minutes. If all that is true i‘m sending an Welcome TextToSpeech to my Alexa right next to the door.
  4. The Location is translated to an address with the OpenStreetMap Nominatim.
  5. You can ask Alexa where a person is and Alexa will answer with the current address, at home or some hardcoded locations like work.

I would suggest to use an Dummy item that is changed to ON when your phone is in an defined distance to your homeLocation.
I‘ll put my rule for this purpose here when i‘m back home.

Kind regards
Michael

So here´s the rule i´m using to reverse lookup the coordinates to an address and changing the presence.

Items:

Location iPMichael_Location "Ort" <map> (iPhones, gPersist) {channel="icloud:device:openHAB:iPMichael:location"}
String iPMichael_Address "Ort [%s]" <map> (iPhones, gPersist)
Switch iPMichael_Home "Michael Home [%s]" (iPhones, gPersist)

Rule:

// Reverse address lookup with OpenStreetmap
// https://wiki.openstreetmap.org/wiki/Nominatim#Reverse_Geocoding
// Function called to lookup address from a Location type item
// It assumes that the item
val Functions$Function1<GenericItem, String> lookupAddress= [ iPMichael_Location |

    // get lat lon from item
    val PointType location = iPMichael_Location.state as PointType
    val String lat = location.latitude.toString
    val String lon = location.longitude.toString

    // build request
    val String endpoint = 'https://nominatim.openstreetmap.org/reverse'
    val String lang     = 'de-DE'
    val String query    = 'lat=' + lat + '&lon=' + lon + '&accept-language=' + lang + '&format=json&addressdetails=1'
    val String request  = endpoint + '?' + query

    // get result
    val String result  = sendHttpGetRequest(request)
    // logInfo("lookupAddress", result)
    var String road    = transform("JSONPATH", "$.address.road", result)
    var String house   = transform("JSONPATH", "$.address.house_number", result)
    var String city    = transform("JSONPATH", "$.address.city", result)
    var String town    = transform("JSONPATH", "$.address.town", result)
    var String country = transform("JSONPATH", "$.address.country", result)
    var String hide    = 'Deutschland' // in result language; hide your home country

    // Empty the Strings if they´re not part of the result and the full result was put into the Strings
    if(road.startsWith("{"))
    {
        road = ''
    }
    if(house.startsWith("{"))
    {
        house = ''
    }
    if(town.startsWith("{"))
    {
        town = ''
    }
    if(city.startsWith("{"))
    {
        city = ''
    }
    if(country.startsWith("{"))
    {
        country = ''
    }

    // Check if city is empty and fill in town
    if(city == '')
    {
        city = town
    }

    // Check if city is still empty and fill Unknown
    if(city == '')
    {
        city = 'Unknown'
    }

    var String address  = road + ' ' + house + ', ' + city

    // Check if the address starts with comma and only use the city
    if(address.startsWith(",") || address.startsWith(" ,"))
    {
        address = '' + city
    }

    if (country != hide) {
        address = address + ', ' + country
    }

    return address
]

rule "Michael Zuhause"

when

    Item iPMichael_Location changed

then

    // Definition von Zuhause und aktueller Standort
    val PointType homeLocation = new PointType(new DecimalType(<Latitude>), new DecimalType(<Longitude>))
    val PointType phoneLocation = iPMichael_Location.state as PointType
    val int distance = phoneLocation.distanceFrom(homeLocation).intValue()

    // Remove _Location from the triggering item and add _Address
    val String updateItem = triggeringItem.name.toString.split("_Location").get(0) + '_Address'

    // Fill in the address into the item
    postUpdate(updateItem, lookupAddress.apply(triggeringItem))

    if(distance < 150)
    {
        iPMichael_Home.postUpdate(ON)
        logDebug("MichaelHome", "Michael is at home")
    }
    else
    {
        iPMichael_Home.postUpdate(OFF)
        logDebug("MichaelHome", "Michael is gone")
    }

end

I´ve to correct this…
You´re right, the Location isn´t stored with persistence!
It´s because the Location consists of multiple strings (Latitude, Longitude, Altitude).
The only way is to write a rule to store them in parts and another rule to restore them if needed.

Just a quick rule to get the parts of an Location value:

rule "Michael Location split"

when

    Item iPMichael_Location changed

then

    val String Latitude = triggeringItem.state.toString.split(",").get(0)
    val String Longitude = triggeringItem.state.toString.split(",").get(1)
    val String Altitude = triggeringItem.state.toString.split(",").get(2)

    Michael_Latitude.postUpdate(Latitude)
    Michael_Longitude.postUpdate(Longitude)
    Michael_Altitude.postUpdate(Altitude)

    Thread::sleep(100)
    Michael_Latitude.persist
    Michael_Longitude.persist
    Michael_Altitude.persist

end

kind regards
Michael

See:

or


and

My items:

Group:Switch:OR(ON, OFF)      Presence          (Persist)
Group                         gPresence         (Persist)
Group:Switch:AND(OFF, ON)     gPresence_Vincent (gPresence)
Group:Switch:AND(OFF, ON)     gPresence_Sadaf   (gPresence)
Group:Switch:AND(OFF, ON)     gPresence_Ayesha  (gPresence)

Switch Presence_Vincent                "Vincent [MAP(presence.map):%s]" <parents> (Presence)
Switch Presence_Sadaf                  "Sadaf [MAP(presence.map):%s]"   <parents> (Presence)
Switch Presence_Ayesha                 "Ayesha [MAP(presence.map):%s]"            (Presence)
Switch Presence_Vincent_PhoneWifiHome                                             (gPresence_Vincent) { channel="network:pingdevice:192_168_0_25:online" }
Switch Presence_Sadaf_PhoneWifiHome	                                              (gPresence_Sadaf)   { channel="network:pingdevice:192_168_0_6:online" }
Switch Presence_Ayesha_PhoneWifiHome                                              (gPresence_Ayesha)  { channel="network:pingdevice:192_168_0_7:online" }

Switch Presence_Vincent_Timer                                                                         { expire="7m,command=OFF"}
Switch Presence_Sadaf_Timer                                                                           { expire="7m,command=OFF"}
Switch Presence_Ayesha_Timer                                                                          { expire="7m,command=OFF"}


//linking switch item to regionTrigger channel. assuming the Home distance channel is defined in the binding config (see above)
Switch Presence_Vincent_Owntracks                                                 (gPresence_Vincent) { channel="gpstracker:tracker:vr:regionTrigger" [profile="gpstracker:trigger-geofence", regionName="Home"] } 
Switch Presence_Sadaf_Owntracks                                                   (gPresence_Sadaf)   { channel="gpstracker:tracker:sh:regionTrigger" [profile="gpstracker:trigger-geofence", regionName="Home"] } 
Switch Presence_Ayesha_Owntracks                                                  (gPresence_Ayesha)  { channel="gpstracker:tracker:ar:regionTrigger" [profile="gpstracker:trigger-geofence", regionName="Home"] } 

My rules:

// *******************************************
// *             PRESENCE RULES              *
// *******************************************

rule "gPresence Item updated"
when
    Member of gPresence changed
then
    val state = triggeringItem.state.toString // to force item update
    val name = triggeringItem.name.split("_").get(1)
    var timerString = "Presence_" + name + "_Timer"
    val SwitchItem presenceItem = Presence.members.filter[ i | i.name.contains(name) ].head as SwitchItem
    if (state == "ON") {
        postUpdate(timerString, "OFF") // cancel the timer if necessary
        if (presenceItem.state != ON) {
            sendCommand(presenceItem.name, "ON")
        }
    }

    // Not at home
    if (state == "OFF" && presenceItem.state != OFF) {
        sendCommand(timerString, "ON") // start the timer
    }
end

rule "Presence Timer expired"
when
    Item Presence_Vincent_Timer received command OFF or
    Item Presence_Sadaf_Timer received command OFF
then
    var String itemName = triggeringItem.name.split("_").get(0) + "_" + triggeringItem.name.split("_").get(1)
    postUpdate(itemName, "OFF")
    //Presence_Vincent.postUpdate(OFF)
end
1 Like

Thanks to you and @Bredmich!

However if I’m right (I had a quick look at both solutions) they are just reports that the particular phone is home or not, am I right? (I’m using something similar to @vzorglub solution). But I want something to detect if someone is getting closer and closer to home. I just asked this, because I could easily code this, but usually I find much better and simpler solutions here.

@Bredmich openstreetmap api calls are totally free? Now I’m using Google API to provide postal address from GPS location, so it would be good if I don’t need to use another API to get back the data which I already have…

Yes it´s completly free, that´s why i´m using OSM instead of Google. Google doesn´t offer this service for free anymore, as far as i know…

The only way i could think of is a rule and Dummy item with multi cases (1 to 9).

  1. The rule triggers with location changes and checks the distance to your homeLocation
  2. Multiple clauses check the current distance in zones (e.g. 100m/200m/300m/…/900m) and write a case to the dummy item (1 = 100m/9 = 900m)
  3. A second rule triggers at every change of the Dummy item and checks the previousState to determine if you´re coming closer or get farther away

Thanks!

They do. It is limited to 300$/month - meaning if you are under 300$, it’s free. This is really enough for me, I’m using around 100$ with locating 4 phones regurarly.

Thanks for the tips! So it seems there is no regular way to do this (I thought who use openHab to control heating - cooling, they would need this feature). I will need this to control climate and heating.

Overly complicated
rule triggers with location change
calculate distance (Or with GPSTraker binding you have that already in a channel)
If distance more or less than previous distance then you are nearing home or getting farther

How to compare the current distance with the last distance without persistence?
You would still need any type of dummy item to store the last distance.

Good to know. I didn´t found any information about that and Google wants you to add an payment option directly.

Yes of course.
What I meant is that you don’t need this zone and multiple clauses. A simple comparison is sufficient

This is what I thought of first! But I also had to add some ‘guards’ in the rule I think.
For example, consider somehow the location accuracy - sometimes the accuracy increases, and the location is pointed to the center of the location accuracy radius - meaning that this can be far closer to home before the previous one - so just comparing the previous and the current is not enough I think.

Also I had to check if this movement is a ‘bigger’ movement. If you walk around somewhere, you might get closer to home (just a few meters), but you are not going home. Setting a minimum distance is not enough I think (before the rule gets triggered), because I do lots of things not so far from my home - groceries, etc…

I don´t think it´s possible to get an rule that could cover all of this.
It sounds more like a job for an AI to predict if you´re really going home or just getting groceries.

Yes, that’s will be my another ‘big’ project later, to add same AI to my smart home.

However if this is too complicated to do, I should rephrase my original question:

  • How other users manage heating/cooling ‘smartly’? I live in a flat, so I don’t have a ‘programmable’ thermostat either, I will buy radiator valve thermostats (Z-Wave). Thus I don’t really know which is a right and doable way.
    Just make some rules based on current presence, weather conditions outside and time? I have seen many-many users who use some smart thermostat, but I really don’t know how they ‘program’ it.
  • Same for AC, I will buy a smart controller for my AC.

I´m using a tado bridge and thermostat for my smart heating.
The app already has a geofence and start to heat when getting closer to home.
But without an AI that checks if you´re really going home or just getting groceries.
The tado bridge can be controlled by openHAB and Alexa.

And there´s another tado thermostat for AC :slight_smile:
Just have a look at the products.

I used to use a geofence, but found it highly inaccurate due to location drift. I now use my WiFi connection to detect presence.

If everyone is away, the thermostat drops to Eco Mode. When someone arrives home, the thermostat resumes its normal schedule.

I also have imported some simple scheduling that checks against a set of “school days”. If everyone has left the house by school time, the thermostat operates in school mode (drops to Eco temps but preheats or precools the house in anticipation of my kids’ arrival home from school).

This has worked well for me especially since my HVAC is a new unit and even if the temp is a couple of degrees off of desired temp when there is an arrival, it takes little time to get the house to the desired temp.

Thanks! After figuring out that you can’t really detect precisly by phone, this looks the best idea!

So you mean, that you use some calendars which has some info which days are ‘school’ days?
What does ECO means on your thermostat? Does it kick in after a bigger temperature drop or it somehow manages the boiler to operate on a lower power?

Thanks!

Yes, exactly about the school days.

I use a Nest thermostat and the ECO on my thermostat is a min/max temperature setting. Basically I am telling the thermostat that in this mode no one is home and I don’t want the thermostat to run constantly to achieve my actual desired settings all day.

Nest uses Eco MIN and Eco MAX. Eco MIN basically equates to do not use the heat while everyone is away UNLESS it drops below this temperature. Eco MAX equates to the exact opposite. Do not use the A/C while everyone is away UNLESS it rises above this temperature.

Thanks for your respone!
Nest Thermostats are really cool! However I can’t achieve this in my flat :frowning: I need TRVs (Thermostat Radiator Valves). However this behaviour looks really good, even if I will implement it in openHab.

Thanks for you as well!

Yes I had a look at tado devices before, but it seemed a little bit pricier than other products and I didn’t find any review that will ensure it will work great with openhab and the devices itself are great products.

I’m just going old-school with a simple ping!
I’ve set my dhcp to static-define and up address to the devices macaddress (so they always get the same one), then send pings to them!
I can track if their home by if they can ping or not.
Works effectively
(Not at home right now for any scripts/setups but is not difficult to do!)

Just an idea for you.