3

I got a problem with the Google Api Php Client and Google Fit.

I want to get the sum of steps I made in a day.

I found a response but it doesn't work (look at the gist).

My php code:

// Retrive oauth data
$clientData = json_decode(file_get_contents("../Devbook-87e2bafd84e6.json")); 
$client_email = $clientData->client_email;
$private_key = $clientData->private_key;
$scopes = array(Google_Service_Fitness::FITNESS_ACTIVITY_READ);
$credentials = new Google_Auth_AssertionCredentials(
     $client_email,
     $scopes,
     $private_key
);

$client = new Google_Client();
$client->setState('offline');
$client->setRedirectUri('urn:ietf:wg:oauth:2.0:oob');  // Used in hybrid flows
$client->setAssertionCredentials($credentials);
if ($client->getAuth()->isAccessTokenExpired()) {
     $client->getAuth()->refreshTokenWithAssertion();
}

$fitness_service = new Google_Service_Fitness($client);

$dataSources = $fitness_service->users_dataSources;
$dataSets = $fitness_service->users_dataSources_datasets;

$listDataSources = $dataSources->listUsersDataSources("me");

$timezone = "GMT+0100";
$today = date("Y-m-d");
$endTime = strtotime($today.' 00:00:00 '.$timezone);
$startTime = strtotime('-1 day', $endTime);

while($listDataSources->valid()) {
     $dataSourceItem = $listDataSources->next();
     if ($dataSourceItem['dataType']['name'] == "com.google.step_count.delta") {
            $dataStreamId = $dataSourceItem['dataStreamId'];
            $listDatasets = $dataSets->get("me", $dataStreamId, $startTime.'000000000'.'-'.$endTime.'000000000');

            $step_count = 0;
            while($listDatasets->valid()) {
                $dataSet = $listDatasets->next();
                $dataSetValues = $dataSet['value'];

                if ($dataSetValues && is_array($dataSetValues)) {
                   foreach($dataSetValues as $dataSetValue) {
                       $step_count += $dataSetValue['intVal'];
                   }
                }
            }
            print("STEP: ".$step_count."<br />");
     };
 }

The problem here is it didn't enter in the first while loop : $listDataSources->valid() returns always false.

My question : Why it returns false ? And how can I get steps ?

I tried to retrieve data with the Oauth Playground (https://www.googleapis.com/fitness/v1/users/me/dataSources/derived:com.google.step_count.delta:com.google.android.gms:estimated_steps) but I didn't get any data.

I got this :

{
    "dataType": {
         "field": [{
               "name": "steps", 
               "format": "integer"
         }], 
         "name": "com.google.step_count.delta"
    }, 
    "application": {
         "packageName": "com.google.android.gms", 
         "version": ""
    }, 
    "dataStreamId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps", 
    "type": "derived", 
    "dataStreamName": "estimated_steps"
}
Community
  • 1
  • 1

2 Answers2

6

First things first: Do you have any data at all on the device that you're using? I've made that mistake: try to get data from an account with no Google Fit data at all. Don't repeat my mistake, please.

I used the same example as you and it worked for me. The only difference is that I hard coded my client API, like this:

$APIKey = '1231231231231231231231231231123123';
$client_id = '12312312312-dkoasodiajsdaosdjh12h1kjakdahs.apps.googleusercontent.com';
$client_secret = '123123123-1231231-123123123';
$redirect_uri = 'http://localhost/fit/code.php';

Maybe you're having problems with your credentials. Did you start your session? Add a session_start() after the code above.

The code below is working for me. Adapt it and I hope it helps you.

<?php
/*
 * This code is an adaptation of Google API URL Shortener example from Google PHP API github.
 * This was modified to work with Google Fit.
 * This example will count steps from a logged in user.
 */

// I created an Autoloader to load Google API classes
require_once(__DIR__ . '/Autoloader.php');

$APIKey = '1231231231231231231231231231123123';
$client_id = '12312312312-dkoasodiajsdaosdjh12h1kjakdahs.apps.googleusercontent.com';
$client_secret = '123123123-1231231-123123123';
$redirect_uri = 'http://localhost/fit/code.php';

//This template is nothing but some HTML. You can find it on github Google API example. 
include_once "templates/base.php";

//Start your session.
session_start();

$client = new Google_Client();
$client->setApplicationName('google-fit');
$client->setAccessType('online');
$client->setApprovalPrompt("auto");
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);

$client->addScope(Google_Service_Fitness::FITNESS_ACTIVITY_READ);
$service = new Google_Service_Fitness($client);

/************************************************
If we're logging out we just need to clear our
local access token in this case
 ************************************************/
if (isset($_REQUEST['logout'])) {
    unset($_SESSION['access_token']);
}
/************************************************
If we have a code back from the OAuth 2.0 flow,
we need to exchange that with the authenticate()
function. We store the resultant access token
bundle in the session, and redirect to ourself.
 ************************************************/
if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']);
    $_SESSION['access_token'] = $client->getAccessToken();
    $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
    echo "EXCHANGE";
}
/************************************************
If we have an access token, we can make
requests, else we generate an authentication URL.
 ************************************************/
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
    $client->setAccessToken($_SESSION['access_token']);
    echo "GOT IT";
    echo "<pre>";

    // Same code as yours
    $dataSources = $service->users_dataSources;
    $dataSets = $service->users_dataSources_datasets;

    $listDataSources = $dataSources->listUsersDataSources("me");

    $timezone = "GMT+0100";
    $today = date("Y-m-d");
    $endTime = strtotime($today.' 00:00:00 '.$timezone);
    $startTime = strtotime('-1 day', $endTime);

    while($listDataSources->valid()) {
        $dataSourceItem = $listDataSources->next();
        if ($dataSourceItem['dataType']['name'] == "com.google.step_count.delta") {
            $dataStreamId = $dataSourceItem['dataStreamId'];
            $listDatasets = $dataSets->get("me", $dataStreamId, $startTime.'000000000'.'-'.$endTime.'000000000');

            $step_count = 0;
            while($listDatasets->valid()) {
                $dataSet = $listDatasets->next();
                $dataSetValues = $dataSet['value'];

                if ($dataSetValues && is_array($dataSetValues)) {
                    foreach($dataSetValues as $dataSetValue) {
                        $step_count += $dataSetValue['intVal'];
                    }
                }
            }
            print("STEP: ".$step_count."<br />");
        };
    }
    echo "</pre>";
} else {
    $authUrl = $client->createAuthUrl();
}

/************************************************
If we're signed in and have a request to shorten
a URL, then we create a new URL object, set the
unshortened URL, and call the 'insert' method on
the 'url' resource. Note that we re-store the
access_token bundle, just in case anything
changed during the request - the main thing that
might happen here is the access token itself is
refreshed if the application has offline access.
 ************************************************/
if ($client->getAccessToken() && isset($_GET['url'])) {
    $_SESSION['access_token'] = $client->getAccessToken();
}

//Dumb example. You don't have to use the code below.
echo pageHeader("User Query - URL Shortener");
if (strpos($client_id, "googleusercontent") == false) {
    echo missingClientSecretsWarning();
    exit;
}
?>
<div class="box">
    <div class="request">
        <?php
        if (isset($authUrl)) {
            echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
        } else {
            echo <<<END
    <form id="url" method="GET" action="{$_SERVER['PHP_SELF']}">
      <input name="url" class="url" type="text">
      <input type="submit" value="Shorten">
    </form>
    <a class='logout' href='?logout'>Logout</a>
END;
        }
        ?>
    </div>

    <div class="shortened">
        <?php
        if (isset($short)) {
            var_dump($short);
        }
        ?>
    </div>
</div>
0

Thanks for the code, but there were bugs:

  • a couple of places there is a call to next() method, which skips first item in the loop

  • one place needed to check if item was an object

Below is my function. Some important points:

  • I get step count for current and prior day, and store it in session. I also have some other session settings.

-$GooglefitCode global is set in the redirect from OAuth, using the 'code' argument. The code here is in a script called pull.php, and the callback URL is pull_googlefit.php, which just stashes code into $Googlefit global and does PHP include of pull.php.

  • I don't do header() redirect if the OAuth code is passed like the prior code. I think they did this to drop code/state arguments from URL displayed. I just wipe the URL using Javascript elsewhere.

  • See the comment about getting step count in the code. Google strangeness, use "estimated_steps" if you want to match their app. "merge_step_deltas" gets actual step count.

    session_start();
    require 'vendor/autoload.php';
    
    /***************************************************************
    Google Fit Setup
    ****************************************************************/
    $GooglefitClientId = '...';
    $GooglefitClientSecret = '...';
    $GooglefitCallbackURL = '...';
    
    /***************************************************************
    Google Fit Initialization
    ****************************************************************/
    
    if (!isset($_SESSION['googlefit']['pull_retrieved'])) $_SESSION['googlefit']['pull_retrieved'] = 0;
    if (!isset($_SESSION['googlefit']['pull_errors'])) $_SESSION['googlefit']['pull_errors'] = array();
    if (!isset($_SESSION['googlefit']['pull_data'])) $_SESSION['googlefit']['pull_data'] = array();
    if (!$_SESSION['googlefit']['pull_retrieved'] && !$_SESSION['googlefit']['pull_errors'])
    {
      GooglefitGet();
    }
    
    function GoogleFitGet()
    {
      global $GooglefitClientId, $GooglefitClientSecret, $GooglefitCallbackURL, $GooglefitCode;
      // $GooglefitCode is set in pull_googlefit.php, which is the redirect and just includes this file.
      $_SESSION['googlefit']['pull_retrieved'] = 0;
      $_SESSION['googlefit']['pull_errors'] = array();
      $_SESSION['googlefit']['pull_data'] = array();
    
      $client = new Google_Client();
      $client->setApplicationName('google-fit');
      $client->setAccessType('online');
      $client->setApprovalPrompt("auto");
      $client->setClientId($GooglefitClientId);
      $client->setClientSecret($GooglefitClientSecret);
      $client->setRedirectUri($GooglefitCallbackURL);
    
      $client->addScope(Google_Service_Fitness::FITNESS_ACTIVITY_READ);
      $service = new Google_Service_Fitness($client);
      if (isset($GooglefitCode) && ($GooglefitCode != "")) // from pull_googlefit.php
      {
          $client->authenticate($GooglefitCode);
          $_SESSION['googlefit']['access_token'] = $client->getAccessToken();
      }
      // If we have an access token, we can make requests, else we generate an authentication URL.
      if (isset($_SESSION['googlefit']['access_token']) && $_SESSION['googlefit']['access_token']) 
      {
        // There should be a try/catch in here for errors!
        $client->setAccessToken($_SESSION['googlefit']['access_token']);
        $data_sources = $service->users_dataSources;
        $data_sets = $service->users_dataSources_datasets;
        $list_data_sources = $data_sources->listUsersDataSources("me");
        $_SESSION['googlefit']['pull_retrieved'] = 1;
        $_SESSION['googlefit']['pull_errors'] = array();
        date_default_timezone_set('America/New_York');
        $timezone = "";
    
        $ymd = date("Y-m-d");
        $day = "$ymd 00:00:00 $timezone";
        $today_st = strtotime($day);
        $day = "$ymd 23:59:59 $timezone";
        $today_et = strtotime($day);
        $yesterday_st = strtotime("-1 day", $today_st);
        $yesterday_et = strtotime("-1 day", $today_et);
        $ranges = array();
        $ranges[1] = $today_st.'000000000'.'-'.$today_et.'000000000';
        $ranges[0] = $yesterday_st.'000000000'.'-'.$yesterday_et.'000000000';
        $any_list_data_sources = 0;
        while($list_data_sources->valid()) 
        {
          if (!$any_list_data_sources)
          {
            $any_list_data_sources = 1;
            $data_source_item = $list_data_sources->current();
          }
          else
          {
            $data_source_item = $list_data_sources->next();
          }
          if (!is_object($data_source_item)) continue;
          /* Google strangeness:
              This returns actual steps taken:
                if ($data_source_item['dataStreamName'] != 'merge_step_deltas') continue;
              It is the total from See Source Data on Google Fit app which shows each data point.
              I know because I have added up some tests, including 223 data point day.
              Instead, the Fit app does some additional processing on top of the steps. 
              It estimates steps based on the activity when none are recorded.
              This is the number that it shows you on the app.
          */
          if ($data_source_item['dataStreamName'] != 'estimated_steps') continue; 
          if ($data_source_item['dataType']['name'] == "com.google.step_count.delta") 
          {
            $data_stream_id = $data_source_item['dataStreamId'];
            for ($days=0;$days<2;$days++) 
            {
              $list_data_sets = $data_sets->get("me", $data_stream_id, $ranges[$days]);
              $step_count = 0;
              $any_list_data_sets = 0;
              while($list_data_sets->valid()) 
              {
                if (!$any_list_data_sets) 
                {
                   $data_set = $list_data_sets->current();
                   $any_list_data_sets = 1;
                }
                else
                {                             
                  $data_set = $list_data_sets->next();
                }
                if (!is_object($data_set) || !$data_set) continue;
                $data_set_values = $data_set['value'];
                if ($data_set_values && is_array($data_set_values)) 
                {
                  foreach($data_set_values as $data_set_value) 
                  {
                    $step_count += $data_set_value['intVal'];
                  }
                }
              }
              $str = ($days==0)?"steps_yesterday":"steps_today";
              $_SESSION['googlefit']['pull_data']['steps'][$days]['value'] = $step_count;
            }
          }
        }
      } 
      else 
      {
        $authUrl = $client->createAuthUrl();
        header("Location: $authUrl");
        exit;
      }
    }

DaveC
  • 1
  • 3