Saturday 22 November 2014

Raspberry Pi and Strava API #1

My 3 favourite things in order are:
  • Being a Dad
  • Being a Geek
  • Doing exercise (cycling, running, swimming and general physical jerks).
I recently found a way to combine the last two on this list!

I created a LAMP webserver on my RasPi  (using this guide) and proceeded to muck about with various bits of HTML (basic made up pages), PHP, CSS and Javascript.  This enabled me to create a simple home intranet that cycled through various pages of "interesting" information:



...but what I really wanted was some new and interesting data to process and present on my intranet.

For a recent major birthday I got a present that's useful for my third hobby.  It's a Garmin Forerunner 910XT, a GPS multi-sports watch allowing me to log and track runs, bike rides and swims.  In it's own right it's a cool gadget for a geek; for example allowing you to track open water swims by logging GPS points as and when your wrist pops put the water.  Here's one I did earlier that was an accurate record of a short lake swim I did:


I upload all my exercise logs to Strava and whilst browsing around the site I came across details of the Strava API.  This enables you to build your own custom web pages and applications based upon exercise data you have logged on Strava.  First you need to go to the Strava developer site to log your application and get an API key.  Then you can call the API using URLs like this (which just lists all activities):

https://www.strava.com/api/v3/activities?access_token=<your token here>&per_page=200

This yields a JSON response which you can parse; for example picking out particular activities and looking at them in detail:

So:

https://www.strava.com/api/v3/activities/188107339?access_token=<your token here>&per_page=200

...gives:


{"id":188107339,"resource_state":3,"external_id":"activity_579805014.tcx","upload_id":208194475,"athlete":{"id":43095234,"resource_state":1},"name":"Dutch Lake Swim","distance":195.3,"moving_time":59,"elapsed_time":484,"total_elevation_gain":0.0,"type":"Swim","start_date":"2014-08-22T15:51:31Z","start_date_local":"2014-08-22T08:51:31Z","timezone":"(GMT-08:00) America/Vancouver","start_latlng":[51.65,-120.06],"end_latlng":[51.65,-120.06],"location_city":null,"location_state":"British Columbia","location_country":"Canada","start_latitude":51.65,"start_longitude":-120.06,"achievement_count":0,"kudos_count":0,"comment_count":0,"athlete_count":1,"photo_count":0,"map":{"id":"a188102339","polyline":"ofgzH|jx{U@wBKCYtBf@F","resource_state":3,"summary_polyline":"ofgzH|jx{UI{BL|B"},"trainer":false,"commute":false,"manual":false,"private":false,"flagged":false,"gear_id":"g397875","average_speed":3.31,"max_speed":0.6,"truncated":null,"has_kudoed":false,"description":"Garmin Accurate on Course and Distance","calories":0,"segment_efforts":[],"gear":{"id":"g397875","primary":true,"name":"Brooks blue ones","resource_state":2,"distance":135654.0}}

So with some PHP jiggery-pokery I created this intranet page to summarise my Strava activities in a new and interesting way:


All the PHP and HTML is listed below.  Note I'm a novice in this area so 0 marks for style I know...

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Weeks Family Strava Fun</title>
    <link rel="stylesheet" type="text/css" href="/stylesheet/style1.css">
  </head>
  <body>
   <!-- Get the value from Strava-->
   <?php
     //Use cURL to get the value from Strava.  Max 200 actvities per file.  After that will need to play with pages...
     $curl = curl_init();
     curl_setopt ($curl, CURLOPT_URL, "https://www.strava.com/api/v3/activities?access_token=<your key here>");
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
     $result = curl_exec ($curl);
     curl_close ($curl);
     $json_a=json_decode($result,true);
     //Good debug code
     //foreach($json_a as $num)
     //{
     //print $num[start_date]."-".$num[name]."-".$num[type]."-".$num[distance]."-".$num[total_elevation_gain]."-".$num[average_speed]."<br>";
     //}
     //Some code for themain summary that goes in the middle of the top of the page
     //First see if we write km or m
     if ($json_a[0][distance] > 999)
     {
     $distanceToWrite = round(($json_a[0][distance] / 1000),1)."km";
     }
     else
     {
     $distanceToWrite = round($json_a[0][distance],1)."m";
     }
     //Now form the last exercise string to go at the top
     $lastExercise = $json_a[0][type]."-".$distanceToWrite;

     //Now we want some variables for a table that shows Activity,Activity Count,Total Distance (in km)
     //Also use this for fastest, longest and climingest activities
     foreach($json_a as $num)
     {
       if ($num[type] == "Run")
       {
       //Add distance and activity count
       $runCount = $runCount + 1;
       $runDistance = $runDistance + $num[distance];
       //Find the longest activity
       if ($num[distance] > $maxRunDistance)
         {
         $maxRunDistance = $num[distance];
         $maxRunExplanation = substr($num[start_date],0,10)."-".$num[name]."-".round($num[distance]/1000,2)."km";
         }
       //Find the fastest actvity
       if ($num[average_speed] > $fastestRun)
         {
         $fastestRun = $num[average_speed];
         $fastestRunExplanation = substr($num[start_date],0,10)."-".$num[name]."-".round(((1/($num[average_speed] / 1000))/60),2)." minutes per km";
         }
       //Find the activity with the most climbing
       if ($num[total_elevation_gain] > $mostClimbRun)
         {
         $mostClimbRun = $num[total_elevation_gain];
         $mostClimbRunExplanation = substr($num[start_date],0,10)."-".$num[name]."-".$num[total_elevation_gain]."m";
         }
       }
       elseif ($num[type] == "Ride")
       {
       //Add distance and activity count
       $rideCount = $rideCount + 1;
       $rideDistance = $rideDistance + $num[distance];
       //Find the longest activity
       if ($num[distance] > $maxRideDistance)
         {
         $maxRideDistance = $num[distance];
         $maxRideExplanation = substr($num[start_date],0,10)."-".$num[name]."-".round($num[distance]/1000,2)."km";
         }
       //Find the fastest activity
       if ($num[average_speed] > $fastestRide)
         {
         $fastestRide = $num[average_speed];
         $fastestRideExplanation = substr($num[start_date],0,10)."-".$num[name]."-".round((($num[average_speed]/1000)*3600),2)." km per hour";
         }
       //Find the activity with the most climbing
       if ($num[total_elevation_gain] > $mostClimbRide)
         {
         $mostClimbRide = $num[total_elevation_gain];
         $mostClimbRideExplanation = substr($num[start_date],0,10)."-".$num[name]."-".$num[total_elevation_gain]."m";
         }
       }
       elseif ($num[type] == "Swim")
       {
       //Add distance and activity count
       $swimCount = $swimCount + 1;
       $swimDistance = $swimDistance + $num[distance];
       //Find the longest activity
       if ($num[distance] > $maxSwimDistance)
         {
         $maxSwimDistance = $num[distance];
         $maxSwimExplanation = substr($num[start_date],0,10)."-".$num[name]."-".$num[distance];
         }
       }
     }
     //Turn all the distances in to km
     $runDistance = round(($runDistance / 1000),1);
     $rideDistance = round(($rideDistance / 1000),1);
     $swimDistance = round(($swimDistance / 1000),1);
     //Put in the context figures.  First the %of a channel swim (which is 34 km)
     $swimContext = round(100*($swimDistance / 34),1)."% of the distance from Dover to Calais";
     //Now the ride context which is the % of the distance from London to Sydney which is 16885 km
     $rideContext = round(100*($rideDistance / 16885),1)."% of the distance from London to Sydney";
     //Finally run context which is the distance for a LEJOG, 970km
     $runContext = round(100*($runDistance / 970),1)."% of the distance from Land's End to John o' Groats";
     ?>
      <h3>Last Strava: <?php echo substr($json_a[0][start_date],0,10)." - ".$json_a[0][name] ?></h3>
      <div id="container" style="width:750px">
        <div id="left" style="height:60px;width:250px;float:left;text-align:right">
          <img src="/images/strava.jpg" alt="Strava" width="100" height="60">
        </div>
        <div id="middle" style="height:60px;width:250px;float:left;background-color:green;text-align:center;vertical-align:middle">
          <p><font size="5"><?php echo $lastExercise ?></font></p>
        </div>
        <div id="right" style="height:60px;width:250px;float:left;">
          <img src="/images/strava.jpg" alt="Strava" width="100" height="60">
        </div>
      </div>
      <br><br><br>
      <!-- Show a summary table of all Stravas-->
      <h3>Summary of all Stravas:</h3>
      <table>
        <tr>
          <th></th>
          <th>Activity Count</th>
          <th>Distance (km)</th>
          <th>Context</th>
        </tr>
        <tr>
          <td><img src="/images/swim.PNG" alt="Swim" width="25" height="25"></td>
          <td><?php echo $swimCount ?></td>
          <td><?php echo $swimDistance ?></td>
          <td><?php echo $swimContext ?></td>
        </tr>
        <tr>
          <td><img src="/images/bike.PNG" alt="Ride" width="25" height="25"></td>
          <td><?php echo $rideCount ?></td>
          <td><?php echo $rideDistance ?></td>
          <td><?php echo $rideContext ?></td>
        </tr>
        <tr>
          <td><img src="/images/run.PNG" alt="Run" width="25" height="25"></td>
          <td><?php echo $runCount ?></td>
          <td><?php echo $runDistance ?></td>
          <td><?php echo $runContext ?></td>
        </tr>
    </table>
    <h3>Strava Records:</h3>
    <table>
      <tr>
        <th></th>
        <th><img align="center" src="/images/swim.PNG" alt="Swim" width="25" height="25"></th>
        <th><img align="center" src="/images/bike.PNG" alt="Ride" width="25" height="25"></th>
        <th><img align="center" src="/images/run.PNG" alt="Run" width="25" height="25"></th>
      </tr>
      <tr>
        <td>Longest</td>
        <td><?php echo $maxSwimExplanation ?></td>
        <td><?php echo $maxRideExplanation ?></td>
        <td><?php echo $maxRunExplanation ?></td>
      </tr>
      <tr>
        <td>Fastest</td>
        <td>n/a</td>
        <td><?php echo $fastestRideExplanation ?></td>
        <td><?php echo $fastestRunExplanation ?></td>
      </tr>
      <tr>
        <td>Most Climbing</td>
        <td>n/a</td>
        <td><?php echo $mostClimbRideExplanation ?></td>
        <td><?php echo $mostClimbRunExplanation ?></td>
      </tr>

    </table>
    <p>Warning: This only shows a max of 200 Stravas!  Will have to play with  pages after that...</p>
  </body>
</html>