Garmin LiveTrack tracking package 🗺️

I want to share a package file for integrate information from a Garmin LiveTrack event.

I use the IMAP sensor get the url automatic. IMAP integration need to fetch the first 9200 bytes because where the link is placed in the mail.

It’s also possible to share the url into the input field. input_text.garmin_livetrack_link

Create an empty json file for less error logs when HA starts.

# /www/none_garmin.json
{"trackPoints":[]}

garmin_livetrack.yaml package file

# Garmin Livetrack
# garmin_livetrack.yaml

input_text:
  garmin_livetrack_link:
    name: Garmin LiveTrack link
    max: 250
    icon: mdi:link
    initial: ""

sensor:
  - platform: rest
    resource_template: "{{ state_attr('sensor.garmin_livetrack_id','data_link') if state_attr('sensor.garmin_livetrack_id','data_link') != None else 'http://localhost/local/none_garmin.json'}}"
    
    name: Garmin LiveTrack
    value_template: "{{ value_json.trackPoints[-1].dateTime if 'trackPoints' in value_json and (value_json.trackPoints | length) > 0 }}"
    scan_interval: 2592000 #30d
    timeout: 10
    headers:
      Content-Type: application/json
    json_attributes_path: "$.trackPoints[-1:]"
    json_attributes: 
    - dateTime
    - position
    - fixType
    - altitude
    - fitnessPointData
    - speed

template:
- trigger:
  - platform: state
    entity_id: input_text.garmin_livetrack_link
  - platform: state
    entity_id: sensor.garmin_livetrack_link_from_email 
  sensor:
  - name: Garmin LiveTrack id
    state: >-
      {%- if "livetrack.garmin.com" in trigger.to_state.state -%}
      {{- (trigger.to_state.state | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0] -}}
      {%- elif trigger.entity_id == "sensor.garmin_livetrack_link_from_email" and trigger.to_state.state != "" -%}
      {{- trigger.to_state.state -}}
      {%- endif -%}
    attributes:
      link: >-
        {%- if "livetrack.garmin.com" in trigger.to_state.state -%}
        {{- (trigger.to_state.state | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0] -}}
        {%- elif trigger.entity_id == "sensor.garmin_livetrack_link_from_email" and trigger.to_state.state != "" -%}
        {{- trigger.to_state.state -}}/token/{{- trigger.to_state.attributes.token -}}
        {%- endif -%}
      data_link: >-
        https://livetrack.garmin.com/services/session/
        {%- if "livetrack.garmin.com" in trigger.to_state.state -%}
        {{- (trigger.to_state.state | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-]+)/token', ignorecase=True))[0] -}}
        {%- elif trigger.entity_id == "sensor.garmin_livetrack_link_from_email" and trigger.to_state.state != "" -%}
        {{- trigger.to_state.state -}}
        {%- endif -%}/trackpoints
      session: >-
        {%- if "livetrack.garmin.com" in trigger.to_state.state -%}
        {{- (trigger.to_state.state | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0].0 -}}
        {%- elif trigger.entity_id == "sensor.garmin_livetrack_link_from_email" and trigger.to_state.state != "" -%}
        {{- trigger.to_state.attributes.session -}}
        {%- endif -%}
      token: >-
        {%- if "livetrack.garmin.com" in trigger.to_state.state -%}
        {{- (trigger.to_state.state | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0].1 -}}
        {%- elif trigger.entity_id == "sensor.garmin_livetrack_link_from_email" and trigger.to_state.state != "" -%}
        {{- trigger.to_state.attributes.token -}}
        {%- endif -%}
    icon: mdi:map

# Sensor to get link from email with integration: https://www.home-assistant.io/integrations/imap_email_content/
- trigger:
  - platform: event
    event_type: "imap_content"
    event_data:
      sender: [email protected]
  sensor:
  - name: Garmin LiveTrack link from email
    state: >-
      {{ (trigger.event.data['text'] | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token', ignorecase=True))[0] | replace('\n','') | replace('\r','') | replace('=','') }}
    attributes:
      date: "{{ trigger.event.data['date'] }}"
      link: >-
        {{ ((trigger.event.data['text'] | regex_findall(find='(https://livetrack.garmin.com/session/+[a-z0-9-\n\r=]+/token/+[a-z0-9-\n\r=][^"]+)', ignorecase=True))[0]) | replace('\n','') | replace('\r','') | replace('=','') }}
      session: >-
        {{ ((trigger.event.data['text'] | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0].0) | replace('\n','') | replace('\r','') | replace('=','') }}
      token: >-
        {{ ((trigger.event.data['text'] | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token/([a-z0-9-\n\r=][^"]+)', ignorecase=True))[0].1) | replace('\n','') | replace('\r','') | replace('=','') }}


- binary_sensor:
  - name: Garmin Livetrack
    state: "{{ is_state_attr('automation.garmin_livetrack', 'current',1) }}"
    attributes:
      latitude: "{{ states.sensor.garmin_livetrack.attributes.position.lat if 'position' in states.sensor.garmin_livetrack.attributes }}"
      longitude: "{{ states.sensor.garmin_livetrack.attributes.position.lon if 'position' in states.sensor.garmin_livetrack.attributes }}"
      start: "{{ states.sensor.garmin_livetrack.attributes.fitnessPointData.activityCreatedTime if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      datetime: "{{ states.sensor.garmin_livetrack.attributes.dateTime if 'dateTime' in states.sensor.garmin_livetrack.attributes }}"
      duration: "{{ timedelta(seconds=(states.sensor.garmin_livetrack.attributes.fitnessPointData.totalDurationSecs | float(0) ))[:-7] if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      activity: "{{ (states.sensor.garmin_livetrack.attributes.fitnessPointData.activityType if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes ) | lower }}"
      speed_ms: "{{ states.sensor.garmin_livetrack.attributes.speed if 'speed' in states.sensor.garmin_livetrack.attributes }}"
      speed: "{{ (states.sensor.garmin_livetrack.attributes.speed)*3.6 if 'speed' in states.sensor.garmin_livetrack.attributes }}"
      pace: "{{ 16.666666667/(states.sensor.garmin_livetrack.attributes.speed) if 'speed' in states.sensor.garmin_livetrack.attributes and float(states.sensor.garmin_livetrack.attributes.speed) > 0 }}"
      altitude: "{{ states.sensor.garmin_livetrack.attributes.altitude if 'altitude' in states.sensor.garmin_livetrack.attributes }}"
      distance: "{{ (states.sensor.garmin_livetrack.attributes.fitnessPointData.distanceMeters | int(0) )/1000 | round(1) if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      heartbeats: "{{ states.sensor.garmin_livetrack.attributes.fitnessPointData.heartRateBeatsPerMin if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      watt: "{{ (states.sensor.garmin_livetrack.attributes.fitnessPointData.powerWatts if 'powerWatts' in states.sensor.garmin_livetrack.attributes.fitnessPointData) if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      event: "{{ states.sensor.garmin_livetrack.attributes.fitnessPointData.eventTypes if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes }}"
      status: "{{ (states.sensor.garmin_livetrack.attributes.fitnessPointData.pointStatus if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes ) | lower }}"

    
automation:
- alias: Garmin LiveTrack new link
  id: "garmin_livetrack"
  mode: restart
  trigger:
  - platform: state
    entity_id: sensor.garmin_livetrack_id
  condition:
    - condition: template
      value_template: "{{ trigger.to_state.state != '' }}"
    - condition: template
      value_template: "{{ trigger.to_state.state != 'unknown' }}"
    - condition: template
      value_template: '{{ trigger.from_state.state != trigger.to_state.state }}'
  action:
  - variables:
      update_min: 2
  - alias: "Notify start"
    service: notify.mobile_app_xxx
    data:
        title: "Garmin LiveTrack"
        message: "Start"
        data:
          group: Garmin
          tag: garmin_livetrack
          clickAction: "entityId:binary_sensor.garmin_livetrack"
          timeout: 180
          notification_icon: "mdi:run-fast"
          actions:
          - action: "URI"
            uri: "{{ state_attr('sensor.garmin_livetrack_id','link') }}"
            title: "Show Garmin livetrack"
  - repeat:
      until:
      - condition: or
        conditions:
        - condition: template
          value_template: "{{ (as_timestamp(states('sensor.garmin_livetrack')) | int(0) ) > 0 }}"
        - condition: template
          value_template: "{{ repeat.index >= 10 }}"
      sequence:
      - service: homeassistant.update_entity
        entity_id: sensor.garmin_livetrack
      - delay: "00:00:15"
  - repeat:
      until:
      - condition: or
        conditions:
          - condition: template
            value_template: "{{ 'END' in states.sensor.garmin_livetrack.attributes.fitnessPointData.eventTypes if 'fitnessPointData' in states.sensor.garmin_livetrack.attributes else false }}"
          - condition: template
            value_template: "{{ as_timestamp(now())-int(as_timestamp(states('sensor.garmin_livetrack'),as_timestamp(now())) ) > 60*30 }}"
          - condition: template
            value_template: "{{ trigger.to_state.state != states('sensor.garmin_livetrack_id') }}"
          - condition: template
            value_template: "{{ repeat.index > (11*60/update_min) }}" # 11h * 60 min /2 min
      sequence:
      - service: homeassistant.update_entity
        entity_id: sensor.garmin_livetrack
      - delay:
          minutes: "{{ update_min }}"
      
  - alias: "Notify end"
    service: notify.mobile_app_xxx
    data:
        title: "Garmin Livetrack"
        message: |
          End!
          {{ state_attr('binary_sensor.garmin_livetrack','duration') }}
          {{ state_attr('binary_sensor.garmin_livetrack','distance') }}
          {{ state_attr('binary_sensor.garmin_livetrack','activity') }}
          {{ state_attr('binary_sensor.garmin_livetrack','event') }}
        data:
          group: Garmin
          tag: garmin_livetrack
          clickAction: "entityId:binary_sensor.garmin_livetrack"
          timeout: 300
          notification_icon: "mdi:stop-circle"
          actions:
          - action: "URI"
            uri: "{{ state_attr('sensor.garmin_livetrack_id','link') }}"

Good luck! :man_running::dash::japan::world_map:

2 Likes

Wow! this project is great! I test it tomorrow. Thanks :wink:

I added the package, could you explain how to display the data in the dashboard? Thank you

You could lockup binary_sensor.garmin_livetrack in the map or put it in a dashboard map.
Or use the data to activate som automation.

1 Like

I have 2 warning:
Template variable warning: ‘value_json’ is undefined when rendering ‘{{ value_json.trackPoints[-1].dateTime if ‘trackPoints’ in value_json and (value_json.trackPoints | length) > 0 }}’

  • Template variable warning: ‘None’ has no attribute ‘attributes’ when rendering ‘{{ (state_attr(‘sensor.homeassistant’,‘body’) | regex_findall(find=‘https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token’, ignorecase=True))[0] | replace(’\n’,’’) | replace(’\r’,’’) | replace(’=’,’’) if ‘body’ in states.sensor.homeassistant.attributes }}’
  • Template variable warning: ‘None’ has no attribute ‘attributes’ when rendering ‘{{ ((state_attr(‘sensor.homeassistant’,‘body’) | regex_findall(find=‘Garmin Live Tracking - Powered by Garmin Connect™’, ignorecase=True))[0]) | replace(’\n’,’’) | replace(’\r’,’’) | replace(’=’,’’) if ‘body’ in states.sensor.homeassistant.attributes }}’

Do you use the IMAP sensor?
Otherwise you could remove the senor:

- sensor:
  - name: Garmin LiveTrack link from email
    state: >-
      {{ (state_attr('sensor.homeassistant','body') | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+)/token', ignorecase=True))[0] | replace('\n','') | replace('\r','') | replace('=','')
         if 'body' in states.sensor.homeassistant.attributes }}
    attributes:
      token: >-
        {{ ((state_attr('sensor.homeassistant','body') | regex_findall(find='https://livetrack.garmin.com/session/([a-z0-9-\n\r=]+/token/+[a-z0-9-\n\r=][^"]+)', ignorecase=True))[0]) | replace('\n','') | replace('\r','') | replace('=','')
         if 'body' in states.sensor.homeassistant.attributes }}

Yes, I have IMAP sensor

Change sensor.homeassistant to the name of your sensor.

Nothing to do, in my case it doesn’t work.
It doesn’t fetch the link from the mail and it doesn’t show tracking, even if the imap sensor correctly shows the arrival of a new mail

Nice package @fatuuse ! Helped me a lot for what I planned.
Now I cast the livetrack web to a nest hub and show a phone notification linked to a lovelace card with some info and a direct gmaps link to my location.

Thanks for sharing

1 Like

I updated the post with new settings to use with the new IMAP integration that uses events.

1 Like

Hi, I have a problem with the link to display,
I attach the working link that is generated in the email and the one that gets me the sensor.garmin_livetrack_id sensor

as you can see the part is missing /token/BCF8F228A199A28673073EC67383DE is missing
which is replaced by trackpoints

from garmin mail:    https://livetrack.garmin.com/session/2818c519-4fa8-47c0-ac6e-bd1f4e661d09/token/BCF8F228A199A28673073EC67383DE
form package sensor: https://livetrack.garmin.com/session/2818c519-4fa8-47c0-ac6e-bd1f4e661d09/trackpoints

also as you can see from the sensors list

  • token value is missing
  • some tracking attributes are missing

Is there something wrong with me or something is missing in the sensor syntax?

thanks

Since you stopped the recording your activity you got the END event and no more speed data will populate.

From what I can see, it look like it works.

Token is not needed for this, just for look at Garmin map. I will see if I could correct the regex for it.

OK thanks,
it would be really helpful at least for me, i’d like to open the link in the wall tablet, so they know where i am.
I’m trying to figure out how to extract from the mail but I don’t have knowledge of regex filters but I still want to try.

I see!
Right now the binary_sensor.garmin_livetrack will include lat and long from your watch and shows up in HA map, and you can create a map in any dashboard
including that entity.

a card in the dashboard could be a solution, but I see the link to the original page as more practical and nicer for those who open it

thanks again

I updated the templates sensors in the first post.
I hope it works! :+1:

1 Like

Perfect, I tried and it works perfectly, now it gives me the token.

A last question,
I’m not a programmer but self-taught, I tried to search but there are too many results and I don’t know which ones are the most valid for my limited knowledge.
I ask if you can point me to a “guide” to understand how to extract values from the mail body for a job like the one you did

anyway, thanks a lot.

You could try this: https://regex-generator.olafneumann.org

thanks for the reply.