Can I make a custom position tracker from longditude and latitude MQTT messages from boat plotter?

I have tried the Hass plug-in for SignalK, but that hasn’t been updated for a few years, and Hass doesn’t automatically discover it, so I gave up using it that way. But the plugin works in a way because sends the NMEA messages as MQTT, so I can read them in Node-RED and parse them. This is what a position message looks like, from the common SignalK topic signalk/delta:

{"context":"vessels.urn:mrn:signalk:uuid:7afb9ed8-3045-4219-9c0e-90f4c98a8462","updates":[{"timestamp":"2022-05-29T14:31:03.000Z","$source":"Garmin.GP","values":[{"path":"navigation.position","value":{"longitude":7.51889,"latitude":58.0026}}]}]}

I have made a little script to parse it into an easier message with the topic madmax/madmaxposisjon (Mad Max being the name of my rather rugged looking 24 feet aluminum shellfish and ocean fishing boat), which has the payload:

58.0026 7.51889

So as the subject says, can I get a position tracker from that, by reading those messages into Hass with an automation?

yes.

see here:

and here is an example I use to track my vehicle parking location:

- alias: Set Tacoma Location
  trigger:
    - platform: state
      entity_id: sensor.my_mobile_app_bluetooth_connection
    - platform: state
      entity_id: person.me
      to: 'home'
  action:
    - service: device_tracker.see
      data:
        dev_id: toyota_tacoma
        location_name: "{{ states('device_tracker.my_mobile_app') }}"
        gps: 
          - "{{ state_attr('device_tracker.my_mobile_app', 'latitude') }}"
          - "{{ state_attr('device_tracker.my_mobile_app', 'longitude') }}"

you will need to modify the template to extract the data from the mqtt payload.

based the payload above you would use this:

{% set payload = {"context":"vessels.urn:mrn:signalk:uuid:7afb9ed8-3045-4219-9c0e-90f4c98a8462","updates":[{"timestamp":"2022-05-29T14:31:03.000Z","$source":"Garmin.GP","values":[{"path":"navigation.position","value":{"longitude":7.51889,"latitude":58.0026}}]}]} %}
{{ payload.updates[0]['values'][0]['value'].longitude }}
{{ payload.updates[0]['values'][0]['value'].latitude }}

you will need to change the “payload” variable content to read the actual payload data.

hopefully it can get you started.

Thank you very much! I’ll play around with that and see if I can get that working!

I didn’t get far… :wink: What platform should I use in my config.yaml to set up this device tracker? Is it MQTT?

You don’t need to create the device tracker at all manually. The device tracker is actually created by using the device_tracker.see service.

the entity_id that it creates is based on the “dev_id:” line in the action service call above.

in my case the dev_id is “toyata_tacoma” so the device tracker entity_id it creates is device_tracker.toyota_tacoma.

The only issue with this approach is that the location data won’t persist thru restarts. It only gets updated after the automation to update the device tracker runs the first time after restart.

it can be worked around if that is something that is going to be a problem for you.

let me know and I can post the restore automation.

also as an FYI, unless you tag me in a post using @my-user (so @finity) or by hitting the reply button in my post (not the one at the very bottom) then I don’t get notified that I have a reply. I only notice it if I’m going thru the unread topics I have subscribed to.

@finity Thanks, duly noted. I appreciate the help! I didn’t think of that, I have my settings to automatically subscribe to any thread where I participate.

So I have tried to change it to work with the format the payload that the from Node-RED comes in, which is 58.002635 7.518941666666667 (lat and then long).

- id: ‘1590343021529'
  alias: Madmax-posisjon
  trigger:
    - platform: mqtt
      topic: madmax/madmaxposisjon
  action:
    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        gps:
      data_template:
        - "{{ (trigger.payload.split(' ')[0])+'.longitude' }}
        - "{{ (trigger.payload.split(' ')[1])+'.latitude' }}

But the data template isn’t correct (I’m sure it’s those yaml indentations that bites me in the butt as always), and the automation does of course not create anything since it doesn’t have any valid data. I have a problem understanding where I should put the payload.updates, or if I can use trigger.payload split instead when they are MQTT messages.

Edit: There were two open double quotes there that I should have seen, and I think the way I did it first was all wrong anyway, so I tried a new data template:

"{{ (trigger.payload.split(' ')[0])+'.longitude'}}, {{(trigger.payload.split(' ')[1])+'.latitude' }}"

But that gave me this error:

Invalid config for [automation]: template value should be a string for dictionary value @ data[‘action’][0][‘data_template’]. Got None. (See /home/homeassistant/.homeassistant/configuration.yaml, line 9).

right, so do I so all the posts will be in my “not read” list but I don’t get a notification in my user profile up top unless you reply to my post or tag me.

but anyway no big deal…

remove the “data_template:” line since it is redundant and not needed.

also if you are going to use a comma separated list I’m not sure of the proper syntax for that.

I recommend using the “-” list instead.

And you have to put the latitude first. Otherwise you will be somewhere you won’t expect! :laughing:

    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        gps: 
          - "{{ (trigger.payload.split(' ')[1])+'.latitude' }}"
          - "{{ (trigger.payload.split(' ')[0])+'.longitude' }}"

I don’t know what the payload looks like so I can’t tell you if the logic for that is correct.

@finity Actually I told you, but I didn’t separate it enough from the rest of the text, I see. So the topic is:

madmax/madmaxposisjon

And the payload is:

58.002635 7.518941666666667

First the latitude and then the longitude, split by a space. Which is why I do the payload split with a space and 0 and 1. I see that I have that wrong in thesetup, so this would be as close as I can get:

- id: ‘1590343021529'
  alias: Madmax-posisjon
  trigger:
    - platform: mqtt
      topic: madmax/madmaxposisjon
  action:
    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        gps:
        - "{{(trigger.payload.split(' ')[0])+'.latitude' }}, {{ (trigger.payload.split(' ')[1])+'.longitude'}}"

That doesn’t give me an error, but it doesn’t give me a sensor even if it triggeres the automation. And here’s the reason:

Error while executing automation automation.madmax_posisjon: None for dictionary value @ data[‘gps’]

Error while executing automation automation.madmax_posisjon: None for dictionary value @ data[‘gps’]
Madmax-posisjon: Error executing script. Invalid data for call_service at pos 1: None for dictionary value @ data[‘gps’]

I may have gotten one step further here by trying to understand your code a bit better!

- id: ‘1590343021529'
  alias: Madmax-posisjon
  trigger:
    - platform: mqtt
      topic: madmax/madmaxposisjon
  action:
    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        gps:
        - "{{(trigger.payload.split(' ')[0]), 'latitude' }}"
        - "{{ (trigger.payload.split(' ')[1]),'longitude'}}"
Error while executing automation automation.madmax_posisjon: invalid latitude for dictionary value @ data['gps']
Madmax-posisjon: Error executing script. Invalid data for call_service at pos 1: invalid latitude for dictionary value @ data['gps']

So now it understands that there are coordinates there! Is it possible that the coordinate format is wrong, or something? It’s so annoying that it doesn’t tell me what it actually receives, just that it’s wrong.

Well, yes you did and I completely missed it.

Sorry about that.

try this:

- service: device_tracker.see
  data:
    dev_id: madmaxposisjon
    gps:
      - "{{(trigger.payload.split(' ')[0]) }}"
      - "{{ (trigger.payload.split(' ')[1]) }}"

@finity Bullseye, thanks! The device tracker works, when I look in the entity list. But it doesn’t surface automatically in Lovelace. Maybe it shouldn’t?

Edit: But it does work when I create a map card in Lovelace. I would say that the accuracy is within a meter or two.

So thanks a lot for the help! The next thing will be to create sensors for speed, depth and water temperature, which comes in MQTT as well. But I’ll create a separate thread for that.

You’re welcome! :slightly_smiling_face:

Again, remember that after a restart of HA the location of the device tracker will be unknown until the mqtt payload gets re-sent.

Ah, right, I forgot about that one. Is it a lot of extra code to make it persistent?

I think that’s in the eye of the beholder. :wink:

but here is the way I do it…

I use a custom integration called ‘hass-variables’ (found in HACS).

First I create a variable:

variable:
    location_saved:
      value: 'unknown'
      attributes:
        latitude: ''
        longitude: ''
      restore: true

then in the automation that I use to set the location of the device tracker I also set the values in the variable.

I add this as additional actions under the above device_tracker.see service call above:

- id: ‘1590343021529'
  alias: Madmax-posisjon
  trigger:
    - platform: mqtt
      topic: madmax/madmaxposisjon
  action:
    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        gps:
        - "{{(trigger.payload.split(' ')[0]), 'latitude' }}"
        - "{{ (trigger.payload.split(' ')[1]),'longitude'}}"
    - delay:
        seconds: 10
    - service: variable.set_variable
      data:
        variable: location_saved
        value: "{{ states.device_tracker.madmaxposisjon.state }}"
        attributes:
          latitude: "{{ states.device_tracker.madmaxposisjon.attributes.latitude }}"
          longitude: "{{ states.device_tracker.madmaxposisjon.attributes.longitude }}"

then after a HA restart I run this automation to set the device tracker location again:

- alias: Restore Location After Restart
  trigger:
    - platform: homeassistant
      event: start
  action:
    - delay:
        minutes: 1
    - service: device_tracker.see
      data:
        dev_id: madmaxposisjon
        location_name: "{{ states('variable.location_saved') }}"
        gps: 
          - "{{ state_attr('variable.location_saved', 'latitude') }}"
          - "{{ state_attr('variable.location_saved', 'longitude') }}"

@finity Thanks again! Works like a charm! :+1: The only problem getting it to work was that I had to go through a couple of forks to find one that had the version key, the original didn’t have that. But I fould it in a Wibias fork. I don’t use HACS, but I’m quite used to custom integrations, like Remote Home Assistant, the Mercedes Me API and the Easee EV charger integration.

Yeah, sorry I should have specified that one. It’s the one that is in HACS. But if you don’t use HACS it would be hard to know which one to use.

I used to use the Rogro82 original one but they stopped maintaining it and Wibias took it over.

Glad that I could help. :slightly_smiling_face:

@finity I always have to go through a few extra hoops since I don’t do any easy stuff, like docker and so on. I have a pure core installation, and Hassio is out of the question because I have so many other programs on the Pi’s, both in my car, the boat, the house and the cabin that demand special treatment. The worst is one in the boat, which means that I can’t ever go beyond Buster. :grin: But most of what I do in the boat is not really bleeding edge, it’s regular ESP relays and sensors.

You don’t need anything special installation-wise to use HACS. It can be used with any install method.

Add-ons (which are diferent than custom integrations and not provided by HACS) need a supervisor to use those which you only get with HA OS and HA Supervised installs.

I used to use the core install (before it was called that and it was just simply called “Home Assistant”) and now run in Docker using the HA Container install method (on Debian Stretch OS) .

Both of those had (have) the ability to use HACS.

1 Like