Strava sensors

I ride bikes. Strava is cool. Their API… is pretty good.

I’ve been using rest sensor lately to pull some info like stats, most recent ride etc.

Why? I don’t know really, but it’s neat and I can put all the data into graphs. Graphs are cool.

- platform: rest
  name: strava_ride_last_miles
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer API_TOKEN
  value_template: '{{ value_json[0].distance | multiply(0.000621371) | round(1) }}'
  unit_of_measurement: mi
  scan_interval: 300

And even send notifications of summary of rides uploaded by specific users, something Strava does not support in their current mobile applications:

- alias: Notify Last Team Ride
  trigger:
    - platform: state
      entity_id: sensor.strava_last_team_ride_profile
  condition:
    condition: template
    value_template: "{{ states('sensor.strava_last_team_ride_profile') != 'unknown' }}"
  action:
    - service: notify.ios_myiphone
      data_template:
        message: "{{ states('sensor.strava_last_team_ride_name') }} just uploaded a ride titled: {{ states('sensor.strava_last_team_ride_title') }} to Strava traveling {{ states('sensor.strava_last_team_ride_distance') }} miles at an average of {{ states('sensor.strava_last_team_ride_avg_speed') }}mph at {{ states('sensor.strava_last_team_ride_avg_watts') }} watts and an average HR of {{ states('sensor.strava_last_team_ride_avg_hr') }}bpm"
        data:
          subtitle: "via Strava"
          attachment:
            url: "{{ states('sensor.strava_last_team_ride_profile') }}"
            content-type: jpeg
            hide-thumbnail: false

Thoughts so far…

Strava’s API rate limit is not conducive to creating rest_sensors for all the data you’d like to pull. Use of throttling through a sensor component would be ideal but scan_interval helps.

Another way I’ve dealt with this, it to pull only a handful of specific types of requests that retrieve all the json data from Strava and then use template sensors to build what information I want to see in a card or notification.

It would be ideal to create some sensors that have attributes in order to assign profile pictures, decode polylines to display a small map of the activity, names or titles as entity_id, entity_picture etc… but the only way I think that can be done is to actually create a sensor component.

A good starting point looks to be using Stravalib as the library. I’ve started poking around with some options but it’s been slow going so far.

For now the rest and template sensors seem to work pretty well. I’m really not sure how much interest there is in this but I’d personally like to see it happen for a few reasons:

  1. Would add another component to the very lonely Health category.
  2. Possibly use example example for similar types of services like Garmin, Withthings, etc.
  3. Explore use of encode/decode polyline data for use in existing or future Homeassistant components.

Let me know your thoughts.

16 Likes

could you advise how you setup the component using REST? i assume we need to create a new app on Strava Dev portal? Whats the callback url?

You need to go to https://www.strava.com/settings/api and create an application. You can find instructions for how to set that up at http://strava.github.io/api/v3/oauth/

I used localhost as the callback url

Shortly after this post I switched to using shell_command to pull down the data I wanted to a text file and then used command_line sensors to parse out the data I wanted to display. However, command_line sensors do not respond quickly or as expected to changes in a text file.

What has worked well so far is using shell_command to pull the bulk of the data I want while simultaneously pushing it to a local json-server, then use REST sensors for everything I want to display.

1 Like

Hi Devdelay, could you give anymore details on setting this up, with some examples? It’s sounds great, I would love to have a “Cycle” view with all this information from Strava. Also would like to build into the view wind direction then suggesting a local route.

Hey @devdelay!

Thank you so much for this brief explanation! I managed to get some sensors set up with your instructions. I did not manage, however, to get sensors like YTD or monthly miles. Can you please share you config for those ones? They have entities inside entities in the API, and I’m not really sure how to set them up.

I also set up a ‘Calories’ sensor with the reference to kilojoules and a multiply formula of 1 kilojoule to 0.239006 (so, ‘{{ value_json[0].kilojoules | multiply(0.239006) | round(1) }}’) but it did not work as expected. The value was nowhere near the one my last ride on the Strava app indicated. I have to take a look at that, also.

Thanks for your contribution!

1 Like

Does this configuration works ?

I tried without success :frowning:

I can’t make it work either. Would love to see an official Strava component @devdelay

1 Like

Can’t make it work either … :frowning:

This works for me :

  - platform: rest
    name: Strava Distance Last Activity
    resource: https://www.strava.com/api/v3/athlete/activities
    method: GET
    headers:
      Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    value_template: '{{ value_json[0].distance / 1000  }}'
    unit_of_measurement: km
    scan_interval: 300
1 Like

So thanks to @anon57099626, I got it working. Took me a while to figure out that it was showing my last public activity (as my last few runs have been set to private).

What else can this API do?
And, how do you get around the sensor reading a value of 10.1341000332 km instead of 10.13?

I use the following to round to 2 decimal places.

value_template: '{{ value_json[0].distance / 1000 | round(2) }}'

Perfect @sturgeo - that worked exactly as expected :slight_smile:

Any ideas what else we can do with this sensor? What about weekly totals?

Guys, is the REST client a one-shot deal or I need to install that on my Rpi3? I’m running HA on Rpi3.

Having a hard time setting those sensors

This is everything i get from Strava at the moment, some basic last ride stats with some crudish calcs, monthly ride totals (distance, elevation gain, achievements) and Year to Date totals for distance, elevation gain and total number of rides.

############################################################
#
# Strava - Last Ride
#
############################################################
- platform: rest
  name: strava_ride_last_miles
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].distance | multiply(0.000621371) | round(1) }}'
  unit_of_measurement: mi
  scan_interval: 300
- platform: rest
  name: strava_ride_last_elevation
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].total_elevation_gain | multiply(3.2808399) | round(1) }}'
  unit_of_measurement: ft
  scan_interval: 300
- platform: rest
  name: strava_ride_last_speed
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].average_speed | multiply(2.236936) | round(1) }}'
  unit_of_measurement: mph
  scan_interval: 300
- platform: rest
  name: strava_ride_last_watts
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].average_watts | round(1) }}'
  unit_of_measurement: W
  scan_interval: 300
- platform: rest
  name: strava_ride_last_hr
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].average_heartrate | round(1) }}'
  unit_of_measurement: bpm
  scan_interval: 300
- platform: rest
  name: strava_ride_last_calories
  resource: https://www.strava.com/api/v3/athlete/activities
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json[0].kilojoules | multiply(1.1140) | round(1) }}'
  unit_of_measurement: cal
  scan_interval: 300
############################################################
#
# Strava - Recent Totals
#
############################################################
- platform: rest
  name: strava_recent_miles
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json["recent_ride_totals"]["distance"] | multiply(0.000621371) | round(1) }}'
  unit_of_measurement: mi
  scan_interval: 300
- platform: rest
  name: strava_recent_elevation
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json["recent_ride_totals"]["elevation_gain"] | multiply(3.2808399) | round(1) }}'
  unit_of_measurement: ft
  scan_interval: 300
- platform: rest
  name: strava_recent_achievements
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer *****
  value_template: '{{ value_json["recent_ride_totals"]["achievement_count"] | round(1) }}'
  scan_interval: 300
############################################################
#
# Strava - YTD Totals
#
############################################################
- platform: rest
  name: strava_ytd_miles
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json["ytd_ride_totals"]["distance"] | multiply(0.000621371) | round(1) }}'
  unit_of_measurement: mi
  scan_interval: 300
- platform: rest
  name: strava_ytd_elevation
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json["ytd_ride_totals"]["elevation_gain"] | multiply(3.2808399) | round(1) }}'
  unit_of_measurement: ft
  scan_interval: 300
- platform: rest
  name: strava_ytd_count
  resource: https://www.strava.com/api/v3/athletes/****/stats
  method: GET
  headers:
    Authorization: Bearer ****
  value_template: '{{ value_json["ytd_ride_totals"]["count"] | round(1) }}'
  scan_interval: 300
1 Like

Hey,

Quick question. I want to separate virtual ride (indoor trainer) and outdoor ride in my YTD Total. Anyone has found a way to do this?

Thanks

Node Red

You can filter the rides out as this information is available in the ride there is also commute so you can filter out the every day ride to work. You would then need to total it up. I don’t think strava api has this information directly available

So, I’m struggling to get this to work. I’ve taken one of the working examples above, added it under my sensor domain, and set the access token to be "Bearer " where is the value in “your access token” in the strava API.

I think what’s going on is there’s some setup steps to get this to work. They’re described under the developers page but seem to assume an application that is coded to specifically integrate with strava (with redirection etc). Can anyone whose gotten this to work speak to what I may be missing?

Thanks!

Hi, it looks like Strava has changed the key management.

It might be possible that the apps created after the update (around 15 Oct 2018) need to perform more tasks in order to access the data.
The key are also expiring only after a few hours and there is another key used to refresh the fist one.

I wanted to try this also yesterday and found out that nothing works as described in the topic before.

Is there anything new on this. I didn’t try it yet but I also want to set this up.

Regarding to your post it seems like this doesn’t work any more.

Can someone clear that up for me? Don’t want to spend time setting this up when it’s already known that it won’t work,

Unfortunately, the sensor does not work anymore due to changes how we need to authenticate against the Strava API.

Hi all,

I implemented a new platform for Strava sensors which fixes this token issue:
https://github.com/home-assistant/home-assistant/pull/22151

It is currently work in progress. Please feel free to test it. Some feedback is appreciated :slight_smile:
It is based on the PyPi package “stravalib”.