Strava sensors

Well, looks like there are no “long-time” tokens any more. Strava has replaced them for short-time tokens. The good news are that there is a refresh token to reauthorize the app periodically. I am not sure if I can write an app for this but it looks feasible for someone with experience.
agur!

SCREENSHOT-2020-06-17 at 15-34-46

Hey I got Strava working, with a couple of python scripts:

  1. You need to get all necessary tokens, here is a really good video that explains how to get them
    (you need to install Postman, but curl should also work)

    https://www.youtube.com/watch?v=sgscChKfGyg&t=791s

    for the Python Script: https://www.youtube.com/watch?v=2FPNb1XECGs&t=611s

  2. make sure you have python_script: in your configuration.yaml and folder named python_scripts

  3. create a python script for each data, for example, distance of last ride is called strava_distance.py,
    with this content: (replace all three XXX with your data)

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

auth_url = "https://www.strava.com/oauth/token"
activites_url = "https://www.strava.com/api/v3/athlete/activities"

payload = {
    'client_id': "XXX",
    'client_secret': 'XXX',
    'refresh_token': 'XXX',
    'grant_type': "refresh_token",
    'f': 'json'
}

# print("Requesting Token...\n")
res = requests.post(auth_url, data=payload, verify=False)
access_token = res.json()['access_token']
# print("Access Token = {}\n".format(access_token))

header = {'Authorization': 'Bearer ' + access_token}
param = {'per_page': 200, 'page': 1}
my_dataset = requests.get(activites_url, headers=header, params=param).json()

print(my_dataset[0]["distance"])
  1. add this sensor to your yaml:
  - platform: command_line
    name: Strava Distanz
    command: "python3 /config/python_scripts/strava_distance.py"
    command_timeout: 60
    scan_interval: 180

that’s it! you need to restart Home Assistant but all sensors should work, tested it now for a couple of weeks and it works without problems or API errors


here some useful template sensors:

distance in kilometers:

  - platform: template
    sensors:
     strava_distanz_km:
      friendly_name: "Strava Distanz Km"
      value_template: >
        {{ (states('sensor.strava_distanz') |int  / 1000) | round(2) }}

average speed in km/h:

  - platform: command_line
    name: Strava Average Speed
    command: "python3 /config/python_scripts/strava_average_speed.py"
    value_template: '{{ (value | multiply(3600) / 1000) | round(2) }}'
    unit_of_measurement: km/h
    command_timeout: 60
    scan_interval: 180

duration time in HH:MM:SS:

  - platform: template
    sensors:
      strava_duration_insgesamt_hh_mm_ss:
        friendly_name: Strecke Insgesamt
        icon_template: mdi:bike
        entity_id: sensor.time
        value_template: >-
          {% set t = states('sensor.strava_elapsed_time') | int %}
          {{ '{:02d}:{:02d}:{:02d}'.format((t // 3600) % 24, (t % 3600) // 60, (t % 3600) % 60) }}

arrive time in HH:MM:SS:

  - platform: template
    sensors:
      strava_ankunft_uhrzeit:
        friendly_name: Startzeit
        icon_template: mdi:timer-off-outline
        entity_id: sensor.time
        value_template: >-
          {% set minutes = states('sensor.strava_elapsed_time') | int %}
          {{ (as_timestamp(strptime(states.sensor.strava_start_time.state, "%Y-%m-%dT%XZ")) + (minutes)) | timestamp_custom('%H:%M:%S') }}
2 Likes

Great! I was doing the same thing but inside an addon. Have you think about putting that in one of them?
I will continue doing it but it is my first one.

Continuing with the idea of @drimpart, I have done the following to avoid creating several python files and many sensors.

  1. As before, you need to obtain all the data: the client_ID and client_secret from the Strava API, and a refresh_token with the proper scope (I used “activity:read_all”).
  2. Create one py file as @drimpart says. I have just modified it a little:
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import logging
logging.basicConfig(level=logging.DEBUG)

auth_url = "https://www.strava.com/oauth/token"
activites_url = "https://www.strava.com/api/v3/athlete/activities"

payload = {
    'client_id': "XXXXX",
    'client_secret': 'XXXXX',
    'refresh_token': 'XXXXX',
    'grant_type': "refresh_token",
    'f': 'json'
}

res = requests.post(auth_url, data=payload, verify=False)
access_token = res.json()['access_token']
print(access_token)
  1. Create a main sensor which executes the script and obtains the new token:
  - platform: command_line
    name: Strava Access Token
    command: "python3 /config/python_scripts/strava.py"
    scan_interval: 900
  1. Create a second sensor that uses the token and obtains many data at once (wait for the scan interval after restarting HA):
  - platform: rest
    name: Strava Last Activity
    json_attributes_path: "$.[0]"
    json_attributes:
      - distance
      - total_elevation_gain
      - average_speed
      - average_heartrate
      - moving_time
    resource_template: https://www.strava.com/api/v3/athlete/activities?access_token={{ states('sensor.strava_access_token') }}
    method: GET
    value_template: '{{ value_json[0].name }}'
    scan_interval: 300
  1. Create as many templates as you want:
  - platform: template
    sensors:  
      strava_last_distance:
        friendly_name: 'Strava Last Distance'
        value_template: "{{ ((state_attr('sensor.strava_last_activity', 'distance'))/1000) | round(2) }}"
        unit_of_measurement: "km"
        entity_id: sensor.strava_last_activity
      strava_last_elevation:
        friendly_name: 'Strava Last Elevation'
        value_template: "{{ (state_attr('sensor.strava_last_activity', 'total_elevation_gain')) | round(1) }}"
        unit_of_measurement: "m"
        entity_id: sensor.strava_last_activity

image
Thank you @drimpart for sharing.

I think the pro of this way is that requires less files and lines, the contra is that you expose the token in a sensor value.

I am still thinking about doing an addon for the first step (granting authorization).

EDIT: I have seen that there are two limits: 100 requests per 15min and 1000 requests per day.
Check the “scan_interval” of your py script files and rest commands, to avoid reaching the daily limit.
If you have 2 urls (one for last activity and another for your total stats), you could have an interval of 300s = 5min -> 288 times a day * 2 urls = 576 requests for the rest + 96 more for the refresh token.

4 Likes

thanks @icaballero, the json_attributes makes it definitely more compact and easier, I tried something similar, but id didn’t work out, I might change it to this version.
Unfortunately, I have no idea how to create an addon, but it would make the whole process definitely easier to have one, maybe somebody else can help.
Also, it seems I have different request limits (Premium Account), so my scan_interval of 180 seconds is no problem. you can always check if you are on the limit on your profile https://www.strava.com/settings/api

SCREENSHOT-2020-06-18 at 21-16-12

Is the Premium Account the same as having a summit subscription?
I have one but my limits are the standard ones.

Rhythm (min per km) for runners using the speed:

  - platform: template
    sensors:  
      strava_last_rhythm:
        friendly_name: 'Strava Last Rhythm'
        value_template: >-
         {% set t = (100/ (state_attr('sensor.strava_last_activity', 'average_speed')|multiply(0.06)) ) %}
         {{ '{:02d}:{:02d}'.format((t//100)|int,((t%100)*0.6) |int) }}
        unit_of_measurement: "min/km"

Thank you for sharing your solution.
Unfortunately I’ve got error message when tried to repeat it on my Home Assistant 0.104.3 (in docker). During config check I see following :

Invalid config for [sensor.rest]: [json_attributes_path] is an invalid option for [sensor.rest]. Check: sensor.rest->json_attributes_path.

Do you have any ideas why it could happen? I suppose your config above are working and I just copied it in my strava_sensor.yaml file (within sensors folder when other sensor files are located).

Not sure why is happening. Have you tried single quotes in the json_attributes_path?

I just tried following yaml file for strava sensor but with same error message :frowning:

# Rate Limits 	600 requests every 15 minutes, 30000 daily


  - platform: command_line
    name: Strava Access Token
    command: "python3 /config/python_scripts/strava.py"
    scan_interval: 3600
      
  - platform: rest
    name: Strava Last Activity
    json_attributes_path: '$.[0]'
    json_attributes:
      - distance
      - total_elevation_gain
      - average_speed
      - average_heartrate
      - moving_time
    resource_template: https://www.strava.com/api/v3/athlete/activities?access_token={{ states('sensor.strava_access_token') }}
    method: GET
    value_template: '{{ value_json[0].name }}'
    scan_interval: 3600   

  - platform: template
    sensors:  
      strava_last_distance:
        friendly_name: 'Strava Last Distance'
        value_template: "{{ ((state_attr('sensor.strava_last_activity', 'distance'))/1000) | round(2) }}"
        unit_of_measurement: "km"
        entity_id: sensor.strava_last_activity
      strava_last_elevation:
        friendly_name: 'Strava Last Elevation'
        value_template: "{{ (state_attr('sensor.strava_last_activity', 'total_elevation_gain')) | round(1) }}"
        unit_of_measurement: "m"
        entity_id: sensor.strava_last_activity

It looks like the code is correct. I am thinking that it has to be related to your HA version. In february 2020, there was a change regarding json/xml in rest sensors. I am working with the updated HA 0.111.4, so it must be that: https://github.com/home-assistant/core/pull/31809
Anyone?

You can try to remove both json_attributes_path and json_attributes, but that will force you to do a rest sensor for each value.

Thank you very much for assistance!
I’ll continue my research and may be update to latest version of HA (despite the fact that it might be painful due to breaking changes)

After update to the latest version of HA strava sensor start to work!

@hellad glad to hear that everything is working!

I am correcting my previous post of rhythm #43

Many people in this thread have asked for a dedicated Strava Home Assistant Component; one that avoids rate-limit-, authentication-, and other issues with Strava’s API.

I’ve recently started building a Strava Home Assistant Integration and I’d love to hear your thoughts: https://github.com/codingcyclist/ha_strava

As of now, the integration can only pull data about the ten most recent activities on Strava. Down the line, it’d be great to add picture-, summary stats-, and other cool features, but it’s unclear yet when I’ll find the time to do that.

Please reach out if you have feedback and/or would like to contribute.

1 Like

Thank you very much for writing this - it works great. Just a shame that Strava class everyone in Europe as wanting to use KM’s and not Miles like we do in the UK. Will sort out a template sensor to fix that but it’s nice to get my strava rides back into HA :grinning:

Good job! Hope you can implement the summary stats soon!

@rcont @BertrumUK I’m glad you like custom component so far!

I’m already working on pulling YTD and all-time summary stats for biking, running, and swimming. Also, I’m adding a camera entity to feature recent activity pictures as a photo-carousel…stay tuned

1 Like

Summary Stats and Photo Carousel are now available. Feel free to check it out (Link) and let me know what you think :slight_smile:

Thanks Simon. Would still like a metric/Imperial option for us UK users :grimacing:

@codingcyclist good afternoon, I would like a help because I don’t know what to put in the Website field on the website https://www.strava.com/settings/api because I can generate the client id and client secret, but when will I put it in HA gives the following error when opening the page:

{“message”: “Bad Request”, “errors”: [{“resource”: “Application”, “field”: “redirect_uri”, “code”: “invalid”}]}