Fitbit One がなかなか楽しい。
⇒ 参考: [ヅ] Fitbit One (万歩計・睡眠測量機)を購入した

Fitbit One が溜め込んだログは Fitbit サイトにアップロードされる。

この Fitbit サイトには Web API (Fitbit API) が用意されていて、これをラップした Java のライブラリ Fitbit4J もある。

Fitbit4J を使うには、3つの外部ライブラリが必要とドキュメントに書いてある。

In addition to the standard Java runtime library, the Fitbit API Java Client library depends on the following third-party libraries:

Apache Commons Logging 1.1.1
Joda Time Library 1.5
JSON Java Library

The Fitbit Java client uses a significant amount of code from Twitter4J, the Java client for Twitter. If you have already used Twitter4J or any other client of an API service using OAuth authentication, your authentication code and flow using the Fitbit client will be similar.

API Java Client - API - Confluence

実際に Fitbit4J を使ってみたら、「Apache Commons Lang が無い」というエラーが発生した。なので、今回 Apache Commons Lang も導入。

今回のサンプルに使用したライブラリは、以下。

  • Fitbit4J 1.0.25 (fitbit4j-1.0.25.jar)
  • Apache Commons Lang 2.6 (commons-lang-2.6.jar)
  • Apache Commons Logging 1.1.1 (commons-logging-1.1.1.jar)
  • Joda Time Library 1.5.2 (joda-time-1.5.2.jar)
  • JSON Java Library (douglascrockford/JSON-java · GitHub)

以下、Fitbit4J を使って OAuth 認証のサンプルコード(2パターン)と、ユーザーのログデータを取得するサンプルコードを残しておく。

OAuth でユーザーごとのToken と TokenSecret を取得する (Webアプリケーション向け)

Webアプリ向けのやり方をテキストコンソールベースで書いてみたサンプルコード。


import java.io.*;
import java.util.*;
 
import com.fitbit.api.client.*;
import com.fitbit.api.client.http.*;
import com.fitbit.api.client.service.*;
 
import com.fitbit.api.model.*;
import com.fitbit.api.common.model.user.*;
 
public class FitbitOAuthAccessTokenGetter {
 
  public static void main(String[] args) throws Exception {
 
    String apiBaseUrl = "api.fitbit.com";
    String webBaseUrl = "https://www.fitbit.com";
 
    // https://dev.fitbit.com/apps/new にて登録したアプリケーションの
    // Consumer key と Consumer secret
    String consumerKey = "0123456789abcde0123456789abcde01";
    String consumerSecret = "aabbccddee00112233445566778899aa";
 
    // oauth_token と oauth_verifier を受け取るためのURL
    String callbackUrl = "http://www.nilab.info/lab/nilogger/";
 
    FitbitAPIEntityCache entityCache = new FitbitApiEntityCacheMapImpl();
    FitbitApiCredentialsCache credentialsCache = new FitbitApiCredentialsCacheMapImpl();
    FitbitApiSubscriptionStorage subscriptionStore = new FitbitApiSubscriptionStorageInMemoryImpl();
    FitbitApiClientAgent apiClientAgent = new FitbitApiClientAgent(apiBaseUrl, webBaseUrl, credentialsCache);
 
    FitbitAPIClientService<FitbitApiClientAgent> apiClientService
      = new FitbitAPIClientService<FitbitApiClientAgent>(
        apiClientAgent,
        consumerKey,
        consumerSecret,
        credentialsCache,
        entityCache,
        subscriptionStore
    );
 
    LocalUserDetail userDetail = new LocalUserDetail("-");
    String authorizationURL = apiClientService.getResourceOwnerAuthorizationURL(userDetail, callbackUrl);
 
    System.out.println(authorizationURL + " にウェブブラウザでアクセスして認証を許可してください。");
    System.out.println("認証を許可したらウェブブラウザのロケーションバーにリダイレクトされたURLが表示されます。");
    System.out.println("リダイレクトされたURLコードを入力して[Enter]キーを押してください。");
 
    // 入力待ち
    BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
    String redirectUrl = r.readLine();
 
    // query string を分解
    Properties params = getParameters(redirectUrl);
 
    String oauth_token = params.getProperty("oauth_token");
    String oauth_verifier = params.getProperty("oauth_verifier");
    APIResourceCredentials resourceCredentials = apiClientService.getResourceCredentialsByTempToken(oauth_token);
 
    if (resourceCredentials == null) {
      throw new Exception("Unrecognized temporary token when attempting to complete authorization: " + oauth_token);
    }
    if (!resourceCredentials.isAuthorized()) {
      resourceCredentials.setTempTokenVerifier(oauth_verifier);
      apiClientService.getTokenCredentials(new LocalUserDetail(resourceCredentials.getLocalUserId()));
    }
 
    // 認証情報を表示
    String userId = resourceCredentials.getLocalUserId();
    String token = resourceCredentials.getAccessToken();
    String tokenSecret = resourceCredentials.getAccessTokenSecret();
    System.out.println("UserId=" + userId);
    System.out.println("Token=" + token);
    System.out.println("TokenSecret=" + tokenSecret);
 
    LocalUserDetail user = new LocalUserDetail(userId);
    FitbitApiClientAgent agent = apiClientService.getClient();
    //agent.setOAuthAccessToken(accessToken);
    UserInfo userInfo = agent.getUserInfo(user);
    System.out.println(userInfo.getNickname());
  }
 
  // query string を分解
  private static Properties getParameters(String url){
    Properties params = new Properties();
    String query_string = url.substring(url.indexOf('?') + 1);
    String[] pairs = query_string.split("&");
    for(String pair : pairs){
      String[] kv = pair.split("=");
      params.setProperty(kv[0], kv[1]);
    }
    return params;
  }
}

サンプルの実行結果。


https://www.fitbit.com/oauth/authorize?oauth_token=99887766554433221100aabbccddeeff にウェブブラウザでアクセスして認証を許可してください。
認証を許可したらウェブブラウザのロケーションバーにリダイレクトされたURLが表示されます。
リダイレクトされたURLコードを入力して[Enter]キーを押してください。
http://www.nilab.info/lab/nilogger/?oauth_token=99887766554433221100aabbccddeeff&oauth_verifier=abcdefghijklmnopqrst012345
UserId=-
Token=1234512345abcdeabcde6789067890fg
TokenSecret=abcabcdefdef123123456456ghighijk
NI-Lab.

OAuth でユーザーごとのToken と TokenSecret を取得する (デスクトップアプリケーション向け)

デスクトップアプリ向けのやり方 (Out of Band (PIN-based) OAuth workflow) をテキストコンソールベースで書いてみたサンプルコード。


import java.io.*;
 
import com.fitbit.api.client.*;
import com.fitbit.api.client.http.*;
import com.fitbit.api.client.service.*;
 
import com.fitbit.api.model.*;
import com.fitbit.api.common.model.user.*;
 
public class FitbitOAuthAccessTokenGetterPinBased {
 
  public static void main(String[] args) throws Exception {
 
    String apiBaseUrl = "api.fitbit.com";
    String webBaseUrl = "https://www.fitbit.com";
 
    // https://dev.fitbit.com/apps/new にて登録したアプリケーションの
    // Consumer key と Consumer secret
    String consumerKey = "0123456789abcde0123456789abcde01";
    String consumerSecret = "aabbccddee00112233445566778899aa";
 
    FitbitAPIEntityCache entityCache = new FitbitApiEntityCacheMapImpl();
    FitbitApiCredentialsCache credentialsCache = new FitbitApiCredentialsCacheMapImpl();
    FitbitApiSubscriptionStorage subscriptionStore = new FitbitApiSubscriptionStorageInMemoryImpl();
    FitbitApiClientAgent apiClientAgent = new FitbitApiClientAgent(apiBaseUrl, webBaseUrl, credentialsCache);
 
    FitbitAPIClientService<FitbitApiClientAgent> apiClientService
      = new FitbitAPIClientService<FitbitApiClientAgent>(
        apiClientAgent,
        consumerKey,
        consumerSecret,
        credentialsCache,
        entityCache,
        subscriptionStore
    );
 
    TempCredentials credentials = apiClientAgent.getOAuthTempToken();
    String authorizationURL = credentials.getAuthorizationURL();
 
    System.out.println(authorizationURL + " にウェブブラウザでアクセスして認証を許可してください。");
    System.out.println("認証を許可したらウェブブラウザにPINコードが表示されます。");
    System.out.println("PINコードを入力して[Enter]キーを押してください。");
 
    // 入力待ち
    BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
    String pin = r.readLine();
 
    // AccessTokenオブジェクトを生成
    AccessToken accessToken = apiClientAgent.getOAuthAccessToken(credentials, pin);
 
    // 認証情報を表示
    String userId = accessToken.getEncodedUserId();
    String token = accessToken.getToken();
    String tokenSecret = accessToken.getTokenSecret();
    System.out.println("UserId=" + userId);
    System.out.println("Token=" + token);
    System.out.println("TokenSecret=" + tokenSecret);
 
    System.out.println("TempCredentials#getToken=" + credentials.getToken());
    System.out.println("TempCredentials#getTokenSecret=" + credentials.getTokenSecret());
 
    APIResourceCredentials resourceCredentials = new APIResourceCredentials(userId, token, tokenSecret);
    resourceCredentials.setAccessToken(token);
    resourceCredentials.setAccessTokenSecret(tokenSecret);
    resourceCredentials.setResourceId(userId);
 
    LocalUserDetail user = new LocalUserDetail(userId);
 
    apiClientService.saveResourceCredentials(user, resourceCredentials);
 
    FitbitApiClientAgent agent = apiClientService.getClient();
    UserInfo userInfo = agent.getUserInfo(user);
    System.out.println(userInfo.getFullName());
  }
}

サンプルの実行結果。


https://www.fitbit.com/oauth/authorize?oauth_token=99887766554433221100aabbccddeeff にウェブブラウザでアクセスして認証を許可してください。
認証を許可したらウェブブラウザにPINコードが表示されます。
PINコードを入力して[Enter]キーを押してください。
igyrsk154649abcde12345fghi
UserId=24LD3R
Token=1234512345abcdeabcde6789067890fg
TokenSecret=abcabcdefdef123123456456ghighijk
TempCredentials#getToken=98986565ababcdcd12123434fefedcdc
TempCredentials#getTokenSecret=7788aabb9944d5e6c1a239a2a1987654
Nick Labadie

情報取得

取得したユーザーごとの Token と TokenSecret を使って、ユーザーのデータを取得するサンプル。


import java.util.*;
 
import org.joda.time.LocalDate;
 
import com.fitbit.api.client.*;
import com.fitbit.api.client.service.*;
 
import com.fitbit.api.model.*;
import com.fitbit.api.common.model.activities.*;
import com.fitbit.api.common.model.body.*;
import com.fitbit.api.common.model.sleep.*;
import com.fitbit.api.common.model.user.*;
import com.fitbit.api.common.service.*;
 
public class FitbitUserInfoPrinter {
 
  public static void main(String[] args) throws Exception {
 
    String apiBaseUrl = "api.fitbit.com";
    String webBaseUrl = "https://www.fitbit.com";
 
    // https://dev.fitbit.com/apps/new にて登録したアプリケーションの
    // Consumer key と Consumer secret
    String consumerKey = "0123456789abcde0123456789abcde01";
    String consumerSecret = "aabbccddee00112233445566778899aa";
 
    // ユーザーの ID (不要) と Token と TokenSecret
    String userId = "-";
    String token = "1234512345abcdeabcde6789067890fg";
    String tokenSecret = "abcabcdefdef123123456456ghighijk";
 
    FitbitAPIEntityCache entityCache = new FitbitApiEntityCacheMapImpl();
    FitbitApiCredentialsCache credentialsCache = new FitbitApiCredentialsCacheMapImpl();
    FitbitApiSubscriptionStorage subscriptionStore = new FitbitApiSubscriptionStorageInMemoryImpl();
    FitbitApiClientAgent apiClientAgent = new FitbitApiClientAgent(apiBaseUrl, webBaseUrl, credentialsCache);
 
    FitbitAPIClientService<FitbitApiClientAgent> apiClientService
      = new FitbitAPIClientService<FitbitApiClientAgent>(
        apiClientAgent,
        consumerKey,
        consumerSecret,
        credentialsCache,
        entityCache,
        subscriptionStore
    );
 
    LocalUserDetail user = new LocalUserDetail(userId);
 
    APIResourceCredentials resourceCredentials = new APIResourceCredentials(userId, token, tokenSecret);
    resourceCredentials.setAccessToken(token);
    resourceCredentials.setAccessTokenSecret(tokenSecret);
 
    apiClientService.saveResourceCredentials(user, resourceCredentials);
 
    FitbitApiClientAgent agent = apiClientService.getClient();
 
    UserInfo userInfo = agent.getUserInfo(user);
    System.out.println(userInfo.getNickname());
 
    LocalDate date = FitbitApiService.getValidLocalDateOrNull("2013-01-11");
 
    // activity
    System.out.println("***** Activity *****");
    Activities activities = agent.getActivities(user, FitbitUser.CURRENT_AUTHORIZED_USER, date);
    ActivitiesSummary activitiesSummary = activities.getSummary();
    System.out.println(activitiesSummary.getCaloriesOut() + " calories burned");
    System.out.println("Elevation: " + activitiesSummary.getElevation());
    System.out.println(activitiesSummary.getFloors() + " floors climbed");
    System.out.println("Sedentary Minutes: " + activitiesSummary.getSedentaryMinutes() + "min");
    System.out.println("Very Active Minutes: " + activitiesSummary.getVeryActiveMinutes() + "min");
    System.out.println(activitiesSummary.getSteps() + " steps taken");
    for(ActivityDistance activityDistance : activitiesSummary.getDistances()){
      System.out.println("Distance(" + activityDistance.getActivity() + "): " + activityDistance.getDistance() + " km");
    }
 
    // sleep
    System.out.println("***** Sleep *****");
    Sleep sleep = agent.getSleep(user, FitbitUser.CURRENT_AUTHORIZED_USER, date);
    List<SleepLog> sleepLogList= sleep.getSleepLogs();
    for(SleepLog sleepLog : sleepLogList){
      if(sleepLog.isMainSleep()){
        System.out.println("Actual sleep time: " + (sleepLog.getMinutesAsleep() / 60) + "hrs " + (sleepLog.getMinutesAsleep() % 60) + "min");
        System.out.println("Bed time: " + sleepLog.getStartTime());
        System.out.println("Fell asleep in: " + sleepLog.getMinutesToFallAsleep() + "min");
        System.out.println("Awakened: " + sleepLog.getAwakeningsCount() + " times");
        System.out.println("In bed time: " + (sleepLog.getTimeInBed() / 60) + "hrs " + (sleepLog.getTimeInBed() % 60) + "min");
        System.out.println("Sleep efficiency: " + sleepLog.getEfficiency() + "%");
        System.out.println("Duration: " + sleepLog.getDuration());
        System.out.println("After Wakeup: " + sleepLog.getMinutesAfterWakeup() + "min");
        System.out.println("Awake: " + sleepLog.getMinutesAwake() + "min");
      }
    }
 
    // weight
    System.out.println("***** Weight *****");
    List<WeightLog> wwightLogLst = agent.getLoggedWeight(user, FitbitUser.CURRENT_AUTHORIZED_USER, date);
    for(WeightLog weightLog : wwightLogLst){
      System.out.println("Date: " + weightLog.getDate());
      System.out.println("Time: " + weightLog.getTime());
      System.out.println("Weight: " + weightLog.getWeight());
      System.out.println("BMI: " + weightLog.getBmi());
    }
  }
}

サンプルの実行結果。


NI-Lab.
***** Activity *****
2243 calories burned
Elevation: 76.2
25 floors climbed
Sedentary Minutes: 875min
Very Active Minutes: 37min
7912 steps taken
Distance(total): 5.68 km
Distance(tracker): 5.68 km
Distance(loggedActivities): 0.0 km
Distance(veryActive): 2.59 km
Distance(moderatelyActive): 2.65 km
Distance(lightlyActive): 0.44 km
Distance(sedentaryActive): 0.0 km
***** Sleep *****
Actual sleep time: 5hrs 51min
Bed time: 2013-01-10T23:32:00.000
Fell asleep in: 8min
Awakened: 19 times
In bed time: 6hrs 38min
Sleep efficiency: 90%
Duration: 23880000
After Wakeup: 0min
Awake: 39min
***** Weight *****
Date: 2013-01-11
Time: 06:17:40
Weight: 65.0
BMI: 21.72

参考までに、該当するログ(2013-01-11のデータ)をFitbitサイトで表示したときのスクリーンショット。

Fitbit

Fitbit

参考資料

Fitbit4J の内部の挙動がよくわからなかったので、公開されているFitbit4Jのソースコードもかなり参考になった。

tags: fitbit webapi java

Posted by NI-Lab. (@nilab)