{"id":1610,"date":"2022-12-27T11:55:10","date_gmt":"2022-12-27T11:55:10","guid":{"rendered":"https:\/\/marcel-jan.eu\/datablog\/?p=1610"},"modified":"2022-12-27T12:14:35","modified_gmt":"2022-12-27T12:14:35","slug":"a-strava-dashboard-on-a-raspberry-pi-part-3-the-strava-api","status":"publish","type":"post","link":"https:\/\/marcel-jan.eu\/datablog\/2022\/12\/27\/a-strava-dashboard-on-a-raspberry-pi-part-3-the-strava-api\/","title":{"rendered":"A Strava dashboard on a Raspberry Pi (Part 3): The Strava API"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<p>This is part 3 of a <a rel=\"noreferrer noopener\" href=\"https:\/\/marcel-jan.eu\/datablog\/2022\/12\/12\/strava-dashboard-on-a-raspberry-pi-with-an-e-ink-display\/\" target=\"_blank\">series of blogpost<\/a>s on how I created a Strava dashboard on a Inky Impression e-ink display with a Raspberry Pi.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">OAuth2<\/h2>\n\n\n\n<p>This was the part that I expected to be the hard part: getting my data from Strava. Or, to be more precise: getting the connection right so the Strava API would allow me to get that data. Because it requires authentication via the OAuth2 protocol and I&#8217;ve tried a similar thing a few years back with a Google API and I just didn&#8217;t get it. But now I do. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Strava API documentation<\/h2>\n\n\n\n<p>It requires a whole &#8220;dance&#8221; between your computer code and the Strava API where you exchange all kinds of tokens back and forth. Strava&#8217;s <a rel=\"noreferrer noopener\" href=\"https:\/\/developers.strava.com\/docs\/getting-started\/\" target=\"_blank\">Getting Started with the Strava API<\/a> document explains it quite well. And this blogpost by Graziano Fuccio helped me a lot with the Python code: <a rel=\"noreferrer noopener\" href=\"http:\/\/www.grace-dev.com\/python-apis\/strava-api\/\" target=\"_blank\">http:\/\/www.grace-dev.com\/python-apis\/strava-api\/<\/a>.<\/p>\n\n\n\n<p>Frustratingly I still didn&#8217;t get it to work though. The reason I found out, is because the URL of the authentication has changed. From <a rel=\"noreferrer noopener\" href=\"https:\/\/www.strava.com\/oauth\/token\" target=\"_blank\">https:\/\/www.strava.com\/oauth\/token<\/a> it became &nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/www.strava.com\/api\/v3\/oauth\/token\" target=\"_blank\">https:\/\/www.strava.com\/api\/v3\/oauth\/token<\/a>. I found this elsewhere in the Stava API documentation, where the correct URL was shown. I&#8217;ve <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/marceljankr\/status\/1590470993901821952?s=20&amp;t=Geo9KKWN7cfgH-8pP0TTsA\" target=\"_blank\">told Strava that their Getting Started documentation is outdated<\/a>. They asked me to create a ticket and I&#8217;ve done so, but I don&#8217;t think they changed their document yet. But Graziano Fuccio did though.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">My code and process<\/h2>\n\n\n\n<p>I&#8217;ve used Graziano Fuccio&#8217;s Python code for my Strava Inky Dashboard and changed it a little. My code can be downloaded from Github: <a href=\"https:\/\/github.com\/Marcel-Jan\/StravaInky\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/Marcel-Jan\/StravaInky<\/a><\/p>\n\n\n\n<p>This is how my current process is:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"684\" height=\"1024\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/StravaInky_process-684x1024.png\" alt=\"\" class=\"wp-image-1615\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/StravaInky_process-684x1024.png 684w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/StravaInky_process-200x300.png 200w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/StravaInky_process-768x1150.png 768w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/StravaInky_process.png 900w\" sizes=\"auto, (max-width: 684px) 100vw, 684px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"> 1. Install Pillow<\/h3>\n\n\n\n<p>Actually this is not for authentication, but to show stuff on the Inky Impression display. But if you are going to run <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/Marcel-Jan\/StravaInky\/blob\/main\/stravainky_dashboard.py\" target=\"_blank\">stravainky_dashboard.py<\/a>, you&#8217;re going to need this Python library.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Create your Strava API application<\/h3>\n\n\n\n<p>Now we&#8217;re going to create a Strava API application. For this go to the Strava API Application page: <a rel=\"noreferrer noopener\" href=\"https:\/\/www.strava.com\/settings\/api\" target=\"_blank\">https:\/\/www.strava.com\/settings\/api<\/a>. Create an application. <\/p>\n\n\n\n<p>Now you&#8217;ll get a form that looks something like this. Sorry that this screenshot is in Dutch. I don&#8217;t know how to make Strava show it in English. <\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"757\" height=\"815\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-23.28.55.png\" alt=\"\" class=\"wp-image-1616\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-23.28.55.png 757w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-23.28.55-279x300.png 279w\" sizes=\"auto, (max-width: 757px) 100vw, 757px\" \/><\/figure>\n\n\n\n<p>You&#8217;ll be asked to give it a name, a category (I chose visualisation, but I&#8217;m not sure it matters). I don&#8217;t think the website URL matters that much for this. Authentication of the callback domain will later appear in an URL that is generated. &#8220;localhost&#8221; will be fine here.<\/p>\n\n\n\n<p>After that was successful, you will get something like this (except maybe not in Dutch):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"736\" height=\"801\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-22.49.51.png\" alt=\"\" class=\"wp-image-1617\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-22.49.51.png 736w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-22.49.51-276x300.png 276w\" sizes=\"auto, (max-width: 736px) 100vw, 736px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. Download my code from Github<\/h3>\n\n\n\n<p>The code can be downloaded from here: <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/Marcel-Jan\/StravaInky\" target=\"_blank\">https:\/\/github.com\/Marcel-Jan\/StravaInky<\/a>. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Edit config.py<\/h3>\n\n\n\n<p>Because the secrets and tokens used to authenticate your Strava API application are rather sensitive, I decided to create a separate config.py file where I store client id, client secret (and later the refresh token).<\/p>\n\n\n\n<p>Copy the Client ID and Client Secret from the API application details (screenshot above) and paste them in config.py in client_id and client_secret.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Run stravainky_getaccesstoken.py<\/h3>\n\n\n\n<p>Okay, this is the part where you need to pay close attention. If all goes well, you only need to run stravainky_getaccesstoken.py only once. <\/p>\n\n\n\n<p>When you run it, it will produce an URL and it will ask you for a code.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"896\" height=\"189\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken01.png\" alt=\"\" class=\"wp-image-1620\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken01.png 896w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken01-300x63.png 300w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken01-768x162.png 768w\" sizes=\"auto, (max-width: 896px) 100vw, 896px\" \/><\/figure>\n\n\n\n<p>You get the code by copying the URL (after the &#8220;Click here&#8221; part) and go to that URL in a browser, you&#8217;ll get a page where you need to authorise your app. Of course we want to do that.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"556\" height=\"669\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-21.36.11.png\" alt=\"\" class=\"wp-image-1618\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-21.36.11.png 556w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-10-at-21.36.11-249x300.png 249w\" sizes=\"auto, (max-width: 556px) 100vw, 556px\" \/><\/figure>\n\n\n\n<p>And here&#8217;s the strange thing: when you click Allow (or Toestaan in Dutch), you&#8217;ll be sent to a URL that goes nowhere. It will look like things went wrong, but in the URL is your refresh token that you need to copy.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"862\" height=\"66\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken02.png\" alt=\"\" class=\"wp-image-1621\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken02.png 862w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken02-300x23.png 300w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken02-768x59.png 768w\" sizes=\"auto, (max-width: 862px) 100vw, 862px\" \/><\/figure>\n\n\n\n<p>Now go back to the terminal where you started stravainky_getaccesstoken.py and paste that refresh token (&#8220;Insert the refresh token from the url&#8221;).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"907\" height=\"147\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken03.png\" alt=\"\" class=\"wp-image-1622\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken03.png 907w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken03-300x49.png 300w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/stravainky_getaccesstoken03-768x124.png 768w\" sizes=\"auto, (max-width: 907px) 100vw, 907px\" \/><\/figure>\n\n\n\n<p>This will put the access token and other details in a file called strava_tokens.json. If strava_tokens.json contains errors, check that you entered the correct client id and client secret in config.py and run this step again.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6. Enter refresh token in config.py<\/h3>\n\n\n\n<p>Now that you have the refresh token. Also enter it in config.py:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"570\" height=\"127\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-27-at-12.36.21.png\" alt=\"\" class=\"wp-image-1648\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-27-at-12.36.21.png 570w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/CleanShot-2022-12-27-at-12.36.21-300x67.png 300w\" sizes=\"auto, (max-width: 570px) 100vw, 570px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Run stravainky_dashboard.py<\/h3>\n\n\n\n<p>You should now be able to get data from the Strava API with Python. stravainky_dashboard.py gets this data and displays it on Inky Impression display.<\/p>\n\n\n\n<p>It might have a Christmas theme, like this one. The images can be found in the images directory. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"778\" src=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b-1024x778.jpg\" alt=\"\" class=\"wp-image-1649\" srcset=\"https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b-1024x778.jpg 1024w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b-300x228.jpg 300w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b-768x583.jpg 768w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b-1536x1166.jpg 1536w, https:\/\/marcel-jan.eu\/datablog\/wp-content\/uploads\/2022\/12\/IMG_0874b.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The Strava dashboard on the Inky Impression display with a Christmas theme.<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<p>Nothing ever goes right the first time. What to check when things go wrong?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Check strava_tokens.json<\/h3>\n\n\n\n<p>If it contains errors, check that you put in the correct client id and client secret in config.py. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Check the log<\/h3>\n\n\n\n<p>stravainky_dashboard.py writes a log file called stravainky_dashboard.log that might contain some error messages.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In the next blogpost we will look at how to draw your own dashboard on the Inky Impression with the Pillow library.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 3 of a series of blogposts on how I created a Strava dashboard on a Inky Impression e-ink display with a Raspberry Pi. OAuth2 This was the part that I expected to be the hard part: getting my data from Strava. Or, to be more precise: getting [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1653,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,75],"tags":[355,356,354,76,334,357],"class_list":["post-1610","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-howto","category-python","tag-api","tag-graziano-fuccio","tag-oauth2","tag-python","tag-strava","tag-stravainky_dashboard-py"],"_links":{"self":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1610","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/comments?post=1610"}],"version-history":[{"count":11,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1610\/revisions"}],"predecessor-version":[{"id":1660,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/posts\/1610\/revisions\/1660"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/media\/1653"}],"wp:attachment":[{"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/media?parent=1610"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/categories?post=1610"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/marcel-jan.eu\/datablog\/wp-json\/wp\/v2\/tags?post=1610"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}