Auto update home location using HassOS, rPI, gpsd, and USB GPS

Project

The location of the home assistant install is on a mobile platform (examples: boat, RV, vehicle), and the desired outcome was to dynamically update the Home on the Map as the physical location changed.

Hardware Setup

  • WiFi/Network Router
  • raspberry PI (rpi_haos) with network cable to router
  • raspberry Pi (rpiclient) with network cable to router
  • USB GPS compatible with gpsd connected to rpiclient

Software Setup

rpiclient has Raspberry PI OS configured with gpsd and python3 with these packages installed:

It is running a python3 script on a 5 minute cron job that sends the data to the HA MQTT instance. There are some hard coded portions in this script, like the locations that the gpsdclient is installed, the IP of HA, and the MQTT details. The device name that was used is rpiclient, which does not matter, as long as it is consistent everywhere.

rpiclient > /home/pi/gps2mqtt.py - collect the GPS 3D fix, and send to MQTT

#!/usr/bin/python3

import subprocess
import json
import paho.mqtt.publish as publish

def main():
    """
    This script can take a bit of time for the several 3D fixes to be acquired and then sent, in my use less than 30 seconds is typical, but depending on how many fixes the GPS can get may cause this to be slower
    """
    json_result = None
    mode3_counter = 0
    app = subprocess.Popen(['/home/pi/.local/bin/gpsdclient', '--json'], stdout = subprocess.PIPE, text = True)
    for line in app.stdout:
        json_line = json.loads(line)
        # look for TPV messages with 3D fix, but only after more than 5 3D fixes
        if 'class' in json_line and json_line['class'] == 'TPV' \
            and 'mode' in json_line and json_line['mode'] == 3:
            mode3_counter = mode3_counter + 1
            if mode3_counter > 5:
                json_result = json_line
                break
    app.terminate()
    print('lat {} lon {} time {} alt {} speed {}'.format(json_result['lat'], json_result['lon'], json_result['time'], json_result['alt'], json_result['speed'])) 
    host_ip = "MY.HOME.ASSISTANT.IP"
    creds = {"username":"FIXME", "password":"FIXME"}
    # the JSON format is to satisfy the mqtt_json expectations
    # {"longitude": 0.0, "latitude": 0.0, "gps_accuracy": 0}
    publish.single("location/rpiclient", '{"longitude": ' + str(json_result['lon']) + ', "latitude": ' + str(json_result['lat']) + ', "gps_accuracy": 10}', hostname=host_ip, auth=creds)

if __name__ == "__main__":
    main()

rpiclient > crontab - edit using crontab -e, executes the script every 5 minutes

*/5 * * * * /home/pi/gps2mqtt.py 2>&1 | logger -t mygps

NOTE: by redirecting the output to logger the output can be seen in the journalctl output, this was very useful when the script was causing errors in cron but not when run manually. The default for cron is for the output to go to the default mail provider, which is not setup by default in a Raspberry Pi OS.

rpi_haos has Home Assistant OS configured with MQTT. The IP and MQTT username/password is required to use in the above script.

rpi_haos > configuraton.yaml - use mqtt_json to create a device with a location from the MQTT data

device_tracker:
  - platform: mqtt_json
    devices:
      rpiclient: location/rpiclient

NOTE: The known_devices.yaml entry was created automatically after the device_tracker started working, Manually modified the entry so that it had an appropriate icon and other information for future use.

rpi_haos > known_devices.yaml

rpiclient:
  name: RPi Client
  mac: ABC123ABC123
  icon: mdi:raspberry-pi
  picture:
  track: true

Finally the automation which runs every 15 minutes, and updates the home location. The entry was created in the automation UI, but used the YAML mode for the service portion.

rpi_haos > automations.yaml

- id: '1645004564981'
  alias: Set Home Location
  description: Update home location from mqtt data
  trigger:
  - platform: time_pattern
    minutes: /15
  condition: []
  action:
  - service: homeassistant.set_location
    data_template:
      latitude: '{{ state_attr(''device_tracker.rpiclient'', ''latitude'') }}'
      longitude: '{{ state_attr(''device_tracker.rpiclient'', ''longitude'') }}'
  mode: single

Conclusion

There was a lot of learning involved in this setup, the hope is this documentation helps others who are trying to do a similar setup. The python code is usable but quite terrible, no error handling, hard coded variables. The data_template chunk was found from a different post, so it is still a bit magical. This setup could be modified to use any GPS data sent over MQTT.

8 Likes

Thanks for pointing me in the right direction @jbnimble . I have a Caravan i travel fulltime in Australia in and have just done the same thing.

In case it also helps anyone going down the same path i also just wrote a python script to get the data from a secondary RPI with USB GPS and send it to Home assistant via MQTT. It’s probably a similar outcome to jbnimble’s code but with some tweaks :slight_smile:

  1. Success / fail logs are stored in home/pi/crontasks.log, as well as optional gpsd raw data.
  2. Sends config / states and attributes topics to keep HA happy
  3. How to use the code is written in the docstring (section at top of the file)

anyway, as i say probably similar outcome but just another option :slight_smile:

The github file is below

GPSD Data To Home Assistant

1 Like

Thanks both @jbnimble and @lazza . Would you mind please telling me what USB GPS device(s) you are using? A recommendation is always helpful to me, I’m about to embark on a similar project. Thank you!

I bought a vk-172 gmouse usb reciever from ebay. Works really well. :slight_smile:

Hello LazzaAU, I’m trying using your gpsdata.py script and I would you like to know if you could give an hand to solve an issue I facing.

Actually I have an gps ublox m8n connected in to raspberry pi through ttl-usb adapter and I able to see output data from cat /dev/ttyUSB0 and gpsd -N -D3 /dev/ttyUSB0 commands as well, and also when I run gpsdata.py I can see Connected to MQTT Broker!.. as well.

Issue I facing is after that I cannot see my mqtt broker being populated with gps data.

Also tried enable log file and nothing inside there.

Could you please help me out?

Thank you
Alex

Thank you!

I used this page as my guide to finding a compatible USB GPS device Compatible GPSes

Great topic, thanks @jbnimble and @lazza!

I’m a complete HA virgin, although I followed along for a few years, I installed my very first HA server today. Integrated (kind of) my Afterburner for my diesel heater and now I am looking for a solution for updating my location as my van moves along.

My use case is a bit different (I’d like to think it’s easier but I might be totally wrong :smiley: ) as I use a Teltonika industrial router which is GPS enabled so do not need a Pi and a USB GPS. I can see that there is a Traccar integration and my router can feed information to Traccar and I am sure (hope) it can do the same to HA as well.
Would I still need to have a script or can I just use a simple(ish) automatization?

Any guidance would be greatly appreciated!

You might look at the Traccar Integration. I don’t have any experience with that kind of setup, but it might be a good starting point.

Just for info, I made this addon after trying similar ways as you :+1: might be helpful?

Update, I moved to a docker based install of HA on Linux, same USB GPS receiver, did the following to get the Home location updates. The main differences is this all lives on the same machine, I don’t need MQ, and I don’t need the custom python script that pushes the GPS data to MQ.

  • install gpsd
    sudo apt install gpsd
    
  • Plugin USB GPS
  • Install gpsd integration
    sensor:
      - platform: gpsd
        name: usb_gps
    
  • Reload HA
  • Verify usb_gps sensor/entity exists and is updating with a GPS Fix in UX
  • Add automation (used UX to add, but modified data_template part manually)
    - id: '1695596008382'
      alias: GPSD Location Update
      description: Updates home location from USB GPS
      trigger:
      - platform: state
        entity_id:
        - sensor.usb_gps
      condition: []
      action:
      - service: homeassistant.set_location
        data_template:
          latitude: '{{ state_attr(''sensor.usb_gps'', ''latitude'') }}'
          longitude: '{{ state_attr(''sensor.usb_gps'', ''longitude'') }}'
      mode: single
    
  • Verify Home location is updating

The default entity name for the gpsd integration is gps so if you don’t add the custom name then the name of the entity will be gps instead of usb_gps

The trigger might be too often, it is updating every time the GPS gives a reading. I may want it to only update from a 3D fix and less often.