Reverse Geocode Sensor ("Places") using OpenStreetMap - custom component

Hi All,

I have built a component that uses OpenStreetMaps to provide reverse geocode lookups and gather some other data that I am finding pretty useful.

Apart from the usual reverse lookup ‘place’ information, the sensor will also calculate the users “distance from home” and their current “direction of travel” as either “towards home”, “away from home” or “stationary”. Each user being tracked can have their “home” set to be a different zone (which is necessary for a distributed family). The sensor also generates a URL for Apple Maps or Google Maps that can be attached to a notification and provide a clickable link to see where the user currently is. The component also doesn’t only rely on polling for it’s updates. It subscribes to DeviceTracker state change events and it generates it’s own update event with trigger data containing attributes essential to constructing a good notification.

It was my first python project and unfortunately it does not conform to the guidelines required to submit it for inclusion in the product - but it works well (for me) as a custom component, so I thought I’d share it here in case others also can find a use for it. I spent about 2.5 months developing, testing, tweaking and extending it - then I’ve spent the last 30+ days testing it without any tweaks, so I guess it’s as ready to share as it will ever be.

To use it, you provide a name and a device tracker entity. There are a bunch of other optional things you can provide to enable different behaviors. The documentation is built into the .py script and in the readme on GitHub.

Here’s the link for anyone that wants to try it out. Feedback is welcome.
[https://github.com/tenly2000/HomeAssistant-Places]

The readme on Github also has examples of how to create automations that sends notifications for a single user and for all users.

I know that there are a couple of other similar sensors out there - but they didn’t do exactly what I needed when I needed it - so I decided to go the route of creating my own. Hope someone out there finds this one useful.

Anyhow - enjoy….

17 Likes

Very nice. Giving this a go.

Thanks! Please let me know if you have any trouble with it…

I tried ths and so far it works great. Would it be possible to make it so that the order of the values for the options is respected when constructing the sensor’s value?

At the moment this is my configuration:

- platform: places
  name: Geocode Location
  devicetracker_id: device_tracker.owntracks
  options: street, street_number, postal_code, city, country
  display_zone: show
  map_provider: apple
  map_zoom: 19
  home_zone: zone.home
  api_key: !secret openstreetmap_api_key

But the street number is written first and only then the street name. In Belgium the street name is put in front of the street number when constructing an address.

Hi Geoffrey,

Thanks for the feedback. Let me have a look and see what I can do.

Hi Geoffrey,

I’ve modified the component to do what you’ve asked. I just want to test it for a day before I release it - to make sure it acts the way it should. I’ll probably upload it to the Github repository tomorrow afternoon if everything works as it should.

I also want to point out that - if the component detects that the user is inside a defined ZONE (ie: zone.home, zone.work, zone.cottage, etc) - it doesn’t display any of the detailed information specified in the options list. It just displays the Zone name and the " (since hh:mm)" timestamp.

The detailed “place” information is only displayed when a device is outside of defined Zone (ie: devicetracker’s zone = ‘not_home’)

This makes sense to me since you obviously already know everything about the Zone or you wouldn’t have defined it and set it up to be a Zone. I just wanted to make sure this was clear to everyone who tries out the component. If they’re inside zone.home when they first enable the component - they may think it’s not working because it just displays the Zone name. They can tell it’s working if it has the " (since hh:mm)" suffix after the zone name.

Hi Geoffrey,

I’ve added the changes you requested. Let me know if you have any other problems or suggestions!

Here are the comments for the new version:

20180509 - Updated to support new option value of "do_not_reorder" to disable the automatic ordered display of any specified options
         - If "do_not_reorder" appears anywhere in the list of comma delimited options, the state display will be built 
           using the order of options as they are specified in the options config value.
           ie:  options: street, street_number, do_not_reorder, postal_code, city, country 
           will result in a state comprised of: 
                <street>, <street_number>, <postal_code>, <city>, <country> 
           without the "do_not_reorder" option, it would be:
                <street_number>, <street>, <postal_code>, <city>, <country>
         - The following attributes can be specified in any order for building the display string manually:
            - do_not_reorder
            - place_type, place_name, place_category, place_neighbourhood, street_number, street, city,
            - postal_town, state, region, county, country, postal_code, formatted_address
            Notes:  All options must be specified in lower case.  
                    State and Region return the same data (so only use one of them).
         - Also added 'options' to the attribute list that gets populated by this sensor (to make it easier to see why a specific state is being generated)

[https://github.com/tenly2000/HomeAssistant-Places](Github link)

Thanks,
Jim

Hi Jim

Thank you very much for the changes. When I try the new version, I receive the following error message:

Update for sensor.geocode_location_osm fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 204, in async_update_ha_state
    yield from self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 327, in async_device_update
    yield from self.hass.async_add_job(self.update)
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/local/lib/python3.6/site-packages/homeassistant/util/__init__.py", line 319, in wrapper
    result = method(*args, **kwargs)
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 431, in update
    self.do_update("Scan Interval")
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 665, in do_update
    display_options.remove("do_not_reorder")
AttributeError: 'str' object has no attribute 'remove'

I used the example from your documentation for the options

options: street, street_number, do_not_reorder, postal_code, city, country 

Do you have any idea what’s causing this?

HI Geoffrey,

Yep. That’s what happens when I treat a string as if it were a list. :slight_smile:
I’m not sure why I didn’t see it when I tested it yesterday.

Anyhow…sorry. I’ve fixed it now (I think) and uploaded a new version (v1.2) to Github.

Please re-download it, try again and then let me know if it’s working or not?
https://github.com/tenly2000/HomeAssistant-Places

Hi Jim

Your update solved the issue. I am however now facing another error:

2018-05-28 23:56:09 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.geocode_location fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 204, in async_update_ha_state
    yield from self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 327, in async_device_update
    yield from self.hass.async_add_job(self.update)
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/local/lib/python3.6/site-packages/homeassistant/util/__init__.py", line 319, in wrapper
    result = method(*args, **kwargs)
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 432, in update
    self.do_update("Scan Interval")
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 676, in do_update
    user_display.append(targetoption)
NameError: name 'targetoption' is not defined

When I have a look at line 676 I see the following:

if option in locals():
   user_display.append(targetoption)

Whereas line 674 uses target_option. When I change line 676 this does however throw another error.

Do you have any idea what might cause this?

Not sure what’s been wrong with Forum notifications - but i just saw your message today. Are you still having a problem with this?

Hi Jim

Now I receive in 0.72 the following message:

Update for sensor.geocode_location fails
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 197, in async_update_ha_state
    yield from self.async_device_update()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 320, in async_device_update
    yield from self.hass.async_add_job(self.update)
  File "/usr/local/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/local/lib/python3.6/site-packages/homeassistant/util/__init__.py", line 319, in wrapper
    result = method(*args, **kwargs)
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 432, in update
    self.do_update("Scan Interval")
  File "/home/homeassistant/.homeassistant/custom_components/sensor/places.py", line 462, in do_update
    distance_m = distance(float(new_latitude), float(new_longitude), float(home_latitude), float(home_longitude))
ValueError: could not convert string to float: 'None'

Do you have idea what might cause this?

I’m having the same issue. HASSIO 0.71

For some reason, I’m not getting form notifications when @geoffrey posts - but I got yours today @ravensteijn .

I’ve been working on developing an iOS app and haven’t been doing much with HomeAssistant for the last two months so I haven’t been very active on the forums. I am still running on 0.65.3 (upgrading now) and I don’t see the error in my log files. Did this error begin occurring in conjunction with a version upgrade?

I looked at the code in question and I would ask you to check the values you have for the following:

        new_latitude    = str(self._hass.states.get(self._devicetracker_id).attributes.get('latitude'))
        new_longitude   = str(self._hass.states.get(self._devicetracker_id).attributes.get('longitude'))
        home_latitude   = str(self._home_latitude)
        home_longitude  = str(self._home_longitude)

Specifically, my guess is that your devicetracker (for some reason) doesn’t have a latitude and/or longitude. I’ll look at my log files again after my version upgrade completes to see if I have the error you guys are seeing. If I do, then it might be something version specific that I’ll need to track down.

Until then - please let me know if you did in fact have values for your devicetracker entity latitude and longitude (did the attribute names change?) and/or if there might have been an error with the places component prior to this one.

I added it again to the configurator.yaml file and it’s working now, after a few minutes waiting.

i’m feeling a bit ashamed because i changed 1 thing. I made an account at www.openstreetmap.org
I think it’s mandatory to get this API call working? Am i correct?

Thanxs!

Not unless they’ve changed their policy since April…
Their API allowed completely anonymous access, but they requested that you include your email address in the APIKey field so that they would be able to contact you if they needed to - or to explain that they’ve had to throttle your requests because they were above some “non-published threshold” level.

I am tracking 3 family members, and for each one, I configured it to use the family members email address as the apikey value. I have never had requests rejected nor received any emails from openstreetmap.org

To be honest, now that google requires an account and form of payment for their service, I’m not sure why more people aren’t using my component simply to avoid having to register with Google. Google does provide a $200 credit each month which is enough to cover most people’s usage - but it still seems like a hassle to have to sign up for the apikey.

Glad it’s working for you now. Let me know if you experience any other problems.

2 Likes

Very nice component and a perfect alternative for the google_geocode component.

I noticed that the component uses UTC within the timestamp. Is it possible to change this to the local timezone?

I’m trying to integrate this into HA however the log indicates that cannot find the sensor. Here is what I have done:

Created a folder custom_components/sensors in the folder in which my configuration.yaml resides.

Place a copy of places.py within the custom_components/sensors folder

Edited configuration.yaml to include the following:

sensor_places_alan:
  name: alan
  devicetrackerid: device_tracker.iphone_
  options: zone, street, city
  display_zone: show
  map_provider: google
  map_zoom: 18
  home_zone: zone.alan_home
  api_key: !secret email_alan

What am I doing wrong? I’m on 0.77.0.

Thanks

Hi Texangeek,

I have all my sensors in a sensors.yaml file which I include from configuration.yaml with the following line:
sensor: !include sensors.yaml

Inside my sensors.yaml file, I have the following configuration for the “places” component:

  - platform: places
    name: jim
    devicetracker_id: device_tracker.jim_iphone8plus
    options: zone,place
    display_zone: show
    map_provider: google
    map_zoom: 19
    home_zone: zone.jim_home
    api_key: !secret email_jim

There is nothing in your configuration that says to use the “places” component. It looks like you’ve tried to name it twice - once as “sensor_places_alan” and again as just “alan”.

If you’re configuring your sensors within your configuration.yaml, try something like this:

Sample Configuration.yaml configurations:

sensor places_jim:
  - platform: places
    name: jim
    devicetracker_id: device_tracker.jim_iphone8
    options: zone,place
    display_zone: show
    map_provider: google
    map_zoom: 19
    home_zone: zone.jim_home
    api_key: !secret email_jim

sensor places_sharon:
  - platform: places
    name: sharon
    devicetracker_id: device_tracker.sharon_iphone7
    options: zone, place
    map_provider: apple
    map_zoom: 18
    home_zone: zone.sharon_home
    api_key: !secret email_sharon

Hi Nelbs,

Sorry for the delayed reply - I didn’t see your post until just today.

Which timestamp are you referring to? I’m in EST and all of the times I see appear to be local time. Is it possible that you didn’t correctly set the timezone on the computer you are using to run HomeAssistant?