default eye-catch image.

FitbitAPI BMI、体重データを取得する

体脂肪率と同様にオプション的な扱いだが、Fitbit では 体重、BMI を記録、閲覧できる。 体脂肪率と体重・BMI の両方を取得するにはそれぞれ異なるAPIを叩く必要があるが、これは計測機器への配慮だろう。体重・BMIと体脂肪率を一度に取得するということは、体重・BMI・体脂肪率が全て揃っていることが前提となり、両方測定できる体組成計があることを前提としなければならない。 体重だけであればかなり安価な計測機器があるはずだ。一方で、安価な体重計とWiFi機能の組み合わせは不釣り合いという事情がある。これもIoTのジレンマかな。 ※実際は、体脂肪率データが一緒に記録された場合、このAPIによって体重、BMI、体脂肪率を1度に取得できる。FitbitAPIは仕様との互換性を保ちつつ便利な方向に拡張される傾向がある。 API概要 体重・BMIを取得するRESTfulAPI は以下の通り3パターン用意されている。 GET /1/user/[user-id]/body/log/weight/date/[date].json GET /1/user/[user-id]/body/log/weight/date/[base-date]/[period].json GET /1/user/[user-id]/body/log/weight/date/[base-date]/[end-date].json parameterformatmeaning dateyyyy-MM-ddデータの日付 base-dateyyyy-MM-dd[period]が指定された場合は指定期間の末尾を指定する。[end-date]が指定された場合は指定期間の最初を指定する。 period1d, 7d, 30d, 1w, 1m取得範囲を指定する。1d=1日, 7d=7日, 30d=30日, 1w=1週間, 1m=1か月。1か月と30日が別に用意されていて親切だ。 end-dateyyyy-MM-dd指定期間の末尾を指定する。 データの取得期間は31日を超えてはならない。単位系はHTTPヘッダに付与したAccept-Languageによる。日本は\"ja-JP\"である。 レスポンス レスポンスのスキーマは以下の通りである。 keyformatmeaning bmiXX.XBMI値、(formatは有効桁数を表すと思われる) dateyyyy-MM-ddデータ日付 weightX.XX体重値 (formatは有効桁数を表すと思われる) logIdログID、ユーザ毎にユニークだが、グローバルにユニークではない timeH:i:s計測時刻 (Time of the mesurement) 計測機器がサポートしない場合は前日の最終時刻。 sourceAria,Withthings,APIなど計測装置、入力経路の識別子、[オプション] API経由で入力すると\"API\"が設定される 1日に数回計測した場合の扱いは計測装置の事情によるようだ。Withthingの場合、複数回計測すると最後の値で上書きされる。朝と夜、2回の計測は必須だと思うが、残念ながら計測装置によって目的を達成できない 実行例 最初の書式を叩いてみた。この日は2回計測したのだがデータは1個だけである。上記で説明したスキーマ以外に体脂肪率も付いてきた。仕様通りbmiとweightについては有効桁数が考慮されている。一方、体脂肪率は考慮されていない。 /1/user/[user-id]/body/log/weight/date/2016-06-14.json Array ( [weight] => Array ( [0] => Array ( [bmi] => 22.01 [date] => 2016-06-14 [fat] => 15.918999671936 [logId] => 1465948799000 [source] => Withings [time] => 23:59:59 [weight] => 67.4 ) ) ) 2個目の書式を叩いてみた。「2016-06-14を末尾として1週間取得する」という意味。 GET /1/user/[user-id]/body/log/weight/date/2016-06-14/1w.json Array ( [weight] => Array ( [0] => Array ( [bmi] => 22.01 [date] => 2016-06-07 [fat] => 14.520999908447 [logId] => 1465343999000 [source] => Withings [time] => 23:59:59 [weight] => 67.4 ) [1] => Array ( [bmi] => 22.27 [date] => 2016-06-08 [fat] => 15.515999794006 [logId] => 1465430399000 [source] => Withings [time] => 23:59:59 [weight] => 68.2 ) [2] => Array ( [bmi] => 22.14 [date] => 2016-06-09 [fat] => 14.659999847412 [logId] => 1465516799000 [source] => Withings [time] => 23:59:59 [weight] => 67.8 ) [3] => Array ( [bmi] => 21.94 [date] => 2016-06-10 [fat] => 14.616000175476 [logId] => 1465603199000 [source] => Withings [time] => 23:59:59 [weight] => 67.2 ) [4] => Array ( [bmi] => 21.94 [date] => 2016-06-11 [fat] => 16.163000106812 [logId] => 1465689599000 [source] => Withings [time] => 23:59:59 [weight] => 67.2 ) [5] => Array ( [bmi] => 22.07 [date] => 2016-06-12 [fat] => 14.592000007629 [logId] => 1465775999000 [source] => Withings [time] => 23:59:59 [weight] => 67.6 ) [6] => Array ( [bmi] => 21.94 [date] => 2016-06-13 [fat] => 14.64999961853 [logId] => 1465862399000 [source] => Withings [time] => 23:59:59 [weight] => 67.2 ) [7] => Array ( [bmi] => 22.01 [date] => 2016-06-14 [fat] => 15.918999671936 [logId] => 1465948799000 [source] => Withings [time] => 23:59:59 [weight] => 67.4 ) ) )

default eye-catch image.

FitbitAPI 体脂肪率を取得する

Fitbitは WiFi体組成計と連携して体重、体脂肪率を記録することができる。Fitbit自体が製造販売しているWiFi体組成計「Fitbit Aria」との連携が一番自然だが、Fitbit以外が製造販売しているメジャーなWiFi体組成計とも連携できる。内部的に体組成計を識別する文字列を一緒に記録している。 運動するきっかけとして「減量のため」というのはかなり上位に来るだろうから、もう少し扱いの重要度を上げても良さそうなのだが、オプション的な扱いである。「Aria」で計測したデータであってもサードパーティ製体重計で計測したデータであっても、機能上は同じように扱われる。 API概要 以下に体脂肪率を取得するRESTfulAPIを示す。RESTfulAPI は3パターン用意されている。なお、別のエントリで紹介するAPIにより、体重、体脂肪率、BMIを一度に取得できる。 GET /1/user/[user-id]/body/log/fat/date/[date].json GET /1/user/[user-id]/body/log/fat/date/[date]/[period].json GET /1/user/[user-id]/body/log/fat/date/[base-date]/[end-date].json ドキュメントには以下の記述がある。\"should not be\"は「すべきではない」ではなく「あってはならない」。1か月以上のデータを1度に取得することはできない。 Range end date when date range is provided. Note: The range should not be longer than 31 days. このAPIに限らず、FitbitAPIは、HTTPヘッダにAccept-Languageを設定することで単位系を設定する。成功すると以下の配列を取得できる。 date計測日時?取得日時? fat計測値、Accept-Languageにより単位を設定する logidユーザ内でユニークなID、グローバルではユニークでない time計測時刻?取得時刻? source計測装置、\"Aria\",\"Withthing\"など 時刻まで記録されることから、1日に数回計測した場合にはそれぞれ記録される。 実行例 PHPによりOAuth2のAuthenticate grant flowをパスしてAPIを叩く散布については過去エントリを参照のこと。なお、Wifi体組成計は Withthings製である。 /1/user/-/body/fat/date/2016-06-13.json この日は2度計測しているのだが... Array ( [fat] => Array ( [0] => Array ( [date] => 2016-06-14 [fat] => 15.9 [logId] => 1465948799000 [source] => Withings [time] => 23:59:59 ) ) ) 1個しか得られなかった。また、時刻がテキトウな値になっている。 純正のAriaなら、ちゃんと2個入って正しい時刻が入るのかもしれない。

default eye-catch image.

FitbitAPI 日中の詳細なActivityデータを時系列に取得する

秒単位の消費カロリーなど詳細なデータを取得する Fitbit APIにより、秒単位の消費カロリーなど、詳細な分析を行うのに必要なデータを取得することもできる。 Access to the Intraday Time Series for personal use (accessing your own data) is available through the \"Personal\" App Type. 詳細なActivityデータの利用は個人的な利用に限られる ドキュメントの先頭に以下のような注意書きがあり、あくまで個人的な利用を想定しているようだ。商用利用を目的とする場合は api@fitbit.com を通じて許可を得る必要があるようだ。理由は、かなりのデータ量になるためリソースに対するチャージであったり、fitbitの真髄である「24時間秒単位の心拍数」を\"ヒドいアプリに応用されたくない\"というイメージ的なモノであったり、いろいろだと思う。 OAuth2 Authenticate grant flowの一番最初のフェーズにおいてWebアプリを登録する際に、アプリケーションタイプとして「Personal」を選択しないとデータを得られないようだ。 Access to the Intraday Time Series for personal use (accessing your own data) is available through the \"Personal\" App Type. Access to the Intraday Time Series for all other uses is currently granted on a case-by-case basis. Applications must demonstrate necessity to create a great user experience. Fitbit is very supportive of non-profit research and personal projects. Commercial applications require thorough review and are subject to additional requirements. Only select applications are granted access and Fitbit reserves the right to limit this access. To request access, email api@fitbit.com. 申告しないといけないのか、このAPIを叩いても秒単位の心拍数データを得られなかった。秘伝のタレは簡単には渡してもらえないということか。ドキュメント上は以下のデータに限って詳細データを取得できる。 activities/calories activities/steps activities/distance activities/floors activities/elevation 諸々の注意事項 trackerは7日分のデータを蓄えることができる。それを超えると古いデータから順に失われる。 trackerが7日以上同期されていない場合、辻褄合わせが発生する。抜けた分について、概要は同期されている分だけ、詳細な歩数はゼロ、詳細なカロリーはBMR、EERから計算される。なお、失われた詳細な歩数はFitbitが用意する式によって計算できる。 現在の実装では、ユーザのタイムゾーンにおけるタイムスタンプ値のみ出力する。 trackerデータ欠損時のデータ補完アルゴリズム ちょっと難しくて訳せず... If a user had no Fitbit tracker data for the specific day then the greater of Logged Activities + BMR (for minutes when there is no activity) and the calories calculated from the EER for that day (if EER enabled for this user\'s profile) are taken. In case, there was some data from the tracker for the specific day, that data where available is used and for time where data is unavailable, the BMR is used. If the total is less than 20% greater than BMR then the EER (cals < EER * 0.8) is used. EER never used to calculate calories for today. Fitbitが採用するBMR算出式 身長、体重など簡単に取得できるデータからBMRを推定する計算する式がいくつか存在する。 その中で、Fitbitは MD Mifflin-St Jeor equation を採用している。 9.99 * weightKg + 6.25*heightCm - 4.92*ageYears + 5 (男性) 9.99 * weightKg + 6.25*heightCm - 4.92*ageYears - 161 (女性) 参考)他には以下のようなものがある。それぞれに関して資料を見つけた。 Harris–Benedict式 Owen式 Wang式 Cunningham式 日本人のための式? 母集団が脳梗塞急性期の患者だしn数が30と少ないので俯瞰するに留めるが、推定の精度について議論がある分野のようで、特に欧米人向けの推定式を日本人にそのままあてはめて良いか、という大きな議論があるようだ。 推定式平均値分散二乗平均平方根 Harris–Benedict 式1087980099 Mifflin 式101234829187 Owen 式129051573227 日本人のための簡易式128547083217 Wang 式120832631181 Cunningham 式117527216165 このペーパーの中では、Miffin式は、間接熱量計実測値よりも推定量が下振れする傾向を示している。 Fitbitが採用するEER(TEE,Total energy expenditure) EERとは、Estimated Energy Requirement の略。何のこっちゃであるが、括弧付きで TEE ,Total energy expenditure が付与されているところから、カロリー消費を推定する指標なのだろう。Fitbitは 米国CDC(疾病予防管理センター)のペーパー(An Easy Approach to Calculating Estimated Energy Requirements)のEER推定式を採用している。 式は以下である。 TEE = 864 - 9.72 x age (years) + 1.0 x (14.2 x weight(kg) + 503 x height (meters)) (男性) TEE = 387 - 7.31 x age (years) + 1.0 x (10.9 x weight(kg) + 660.7 x height (meters)) (女性) 取得方法 PHPを使ったOAuth2認証については過去エントリ(djchen/oauth2-fitbitを使ってPHPで簡単にFitbitのデータを取得する)を参考のこと。 詳細データのリソースURIは、過去エントリ(FitbitAPI Activityデータを時系列で取得する)で紹介した概要データのリソースURIに似ている。ただし、詳細データの取得を要求できるリソースは以下に限定される。 activities/calories activities/steps activities/distance activities/floors activities/elevation RESTFul APIは以下の通りである。 GET https://api.fitbit.com/1/user/-/[resource-path]/date/[date]/[date]/[detail-level].json GET https://api.fitbit.com/1/user/-/[resource-path]/date/[date]/1d/[detail-level].json GET https://api.fitbit.com/1/user/-/[resource-path]/date/[date]/[date]/[detail-level]/time/[start-time]/[end-time].json GET https://api.fitbit.com/1/user/-/[resource-path]/date/[date]/1d/[detail-level]/time/[start-time]/[end-time].json 概要データの取得に加え、[detail-level]という項目が追加されている。その値として「1min」か「15min」のいずれかを使用する。 実行例 せっかくなので、日課の1時間ランで絶賛カロリー消費中の時間帯について1分ごとの詳細データを取得してみた。 /1/user/-/activities/colories/date/2016-06-05/1d/1min/time/22:45/23:00.json Array ( [activities-calories] => Array ( [0] => Array ( [dateTime] => 2016-06-05 [value] => 129.13 ) ) [activities-calories-intraday] => Array ( [dataset] => Array ( [0] => Array ( [level] => 3 [mets] => 87 [time] => 22:45:00 [value] => 9.6848402023315 ) [1] => Array ( [level] => 3 [mets] => 88 [time] => 22:46:00 [value] => 9.7961597442627 ) [2] => Array ( [level] => 3 [mets] => 87 [time] => 22:47:00 [value] => 9.6848402023315 ) [3] => Array ( [level] => 3 [mets] => 87 [time] => 22:48:00 [value] => 9.6848402023315 ) [4] => Array ( [level] => 3 [mets] => 89 [time] => 22:49:00 [value] => 9.9074802398682 ) [5] => Array ( [level] => 3 [mets] => 74 [time] => 22:50:00 [value] => 8.2376804351807 ) [6] => Array ( [level] => 3 [mets] => 62 [time] => 22:51:00 [value] => 6.9018402099609 ) [7] => Array ( [level] => 3 [mets] => 66 [time] => 22:52:00 [value] => 7.347119808197 ) [8] => Array ( [level] => 3 [mets] => 64 [time] => 22:53:00 [value] => 7.1244797706604 ) [9] => Array ( [level] => 3 [mets] => 64 [time] => 22:54:00 [value] => 7.1244797706604 ) [10] => Array ( [level] => 3 [mets] => 64 [time] => 22:55:00 [value] => 7.1244797706604 ) [11] => Array ( [level] => 3 [mets] => 66 [time] => 22:56:00 [value] => 7.347119808197 ) [12] => Array ( [level] => 3 [mets] => 66 [time] => 22:57:00 [value] => 7.347119808197 ) [13] => Array ( [level] => 3 [mets] => 64 [time] => 22:58:00 [value] => 7.1244797706604 ) [14] => Array ( [level] => 3 [mets] => 64 [time] => 22:59:00 [value] => 7.1244797706604 ) [15] => Array ( [level] => 3 [mets] => 68 [time] => 23:00:00 [value] => 7.5697598457336 ) ) [datasetInterval] => 1 [datasetType] => minute ) )

default eye-catch image.

FitbitAPI Activityデータを時系列で取得する

API概要 ドキュメントはこちら。このAPIを使用することで、Fitbitが管理している各種Activityデータを時系列に取得できる。取得できるActivityデータは大きくactivity系とtracker系の2種類に分類され、それぞれ以下のように分類されている。 Fitbitデバイスの運動計測機能をtrackerと呼び、純粋に運動計測機能により取得したデータをtracker系で取得するようだ。また、その上位層として、手動入力値等の追加データを含むデータをactivity系で取得するようだ。 activity系 keymeans of value activities/calories消費カロリーの集計値。基礎代謝(BMR)とtracker系消費カロリー、および手動入力された消費カロリーの合計 activities/caloriesBMR基礎代謝(BMR)の集計値。Fitbitデバイスが計測したデータに基づき計算された値 activities/steps歩数 activities/distance距離 activities/floors縦方向の移動の階数表現 activities/elevation縦方向の移動距離 activities/minutesSedentary強度により4個に分類された運動強度のうち1レベル目(最弱)の運動時間 activities/minutesLightlyActive2レベル目の運動時間 activities/minutesFairlyActive3レベル目の運動時間 activities/minutesVeryActive4レベル目の運動時間(最強) activities/activityCaloriesSedentaryレベル以上の運動強度について1分毎に計算された消費カロリー。基礎代謝(BMR)や手動入力を含む。 tracker系 keymeans of value activities/tracker/caloriesFitbit trackerにより計測された基礎代謝を含む消費カロリー。手動入力は含まない。 activities/tracker/stepsFitbit trackerにより計測された歩数 activities/tracker/distanceFitbit trackerにより計測された距離 activities/tracker/floorsFitbit trackerにより計測された縦方向の移動の階数表現 activities/tracker/elevationFitbit trackerにより計測された縦方向の移動距離 activities/tracker/minutesSedentary強度により4個に分類された運動強度のうち1レベル目(最弱)の運動時間 activities/tracker/minutesLightlyActive2レベル目の運動時間 activities/tracker/minutesFairlyActive3レベル目の運動時間 activities/tracker/minutesVeryActive4レベル目の運動時間(最強) activities/tracker/activityCaloriesFitbit trackerにより計測されたSedentaryレベル以上の運動強度について1分毎に計算された消費カロリー。基礎代謝(BMR)を含む。手動入力は含まない。 Fitbit Charge/Fitbit Charge HR 保護カバー (Dark Purple) API詳細 RESTfulAPIの書式は2通りある。以下は取得日付が1日のみの場合。 GET /1/user/[user-id]/[resource-path]/date/[date]/[period].json 開始日時、終了日時を指定し、2日以上の時系列データを取得することもできる。 GET /1/user/[user-id]/[resource-path]/date/[base-date]/[end-date].json いずれの形式ともに、resource-pathとして、API概要にて示した key 値を指定する。 実行例 それでは実行例を紹介する。まず、今日(today, 2016/06/09) の activities/calories を取得してみる。 activities-calories-intraday という1日分の非常に詳細なデータも付いてくるが本エントリでは省略する。次回紹介。 /1/user/-/activities/calories/date/today/1d.json Array ( [activities-calories] => Array ( [0] => Array ( [dateTime] => 2016-06-09 [value] => 1082 ) ) [activities-calories-intraday] => Array ( [dataset] => Array ( … ) ) ) 次に、今日(today)と、1週間(1w) を合わせて activities/calories を取得してみる。すると、今日を末日とする1週間分の消費カロリーを取得できた。1w を指定した場合、1日分の詳細データ activities-calories-intraday は出力されなかった。 /1/user/-/activities/calories/date/today/1w.json Array ( [activities-calories] => Array ( [0] => Array ( [dateTime] => 2016-06-03 [value] => 2805 ) [1] => Array ( [dateTime] => 2016-06-04 [value] => 2653 ) [2] => Array ( [dateTime] => 2016-06-05 [value] => 2834 ) [3] => Array ( [dateTime] => 2016-06-06 [value] => 2734 ) [4] => Array ( [dateTime] => 2016-06-07 [value] => 2822 ) [5] => Array ( [dateTime] => 2016-06-08 [value] => 2652 ) [6] => Array ( [dateTime] => 2016-06-09 [value] => 1098 ) ) ) 基礎代謝(BMR)を期間を指定して取得してみる。一定値ではなかった!。Fitbitはtrackerから基礎代謝を計算で求めているということのようだ。すごいすごい。 /1/user/-/activities/tracker/calories/date/2016-06-05/2016-06-06.json Array ( [activities-caloriesBMR] => Array ( [0] => Array ( [dateTime] => 2016-06-05 [value] => 1603 ) [1] => Array ( [dateTime] => 2016-06-06 [value] => 1604 ) [2] => Array ( [dateTime] => 2016-06-07 [value] => 1594 ) [3] => Array ( [dateTime] => 2016-06-08 [value] => 1603 ) ) ) activities/tracker/calories を1日だけ取得してみる。today を指定すると1日分の詳細データが含まれていたが、期間指定により1日分を指定すると詳細データが含まれない。今回のケースでは、activities/calories と activities/tracker/calories は同一値(2834)である。 /1/user/-/activities/tracker/calories/date/2016-06-05/2016-06-05.json Array ( [activities-tracker-calories] => Array ( [0] => Array ( [dateTime] => 2016-06-05 [value] => 2834 ) ) )

default eye-catch image.

FitbitAPI Daily Activity Summary を取得する

API概要 ドキュメントはこちら。Daily Activity Summaryとは、その名の通り、Fitbitデバイスが取得しFitbitにストアされたデータに関して1日の活動にフォーカスして集計した統計情報だ。 用途にもよるだろうが、ほとんどのケースで、このDaily Activity Summaryを取得するだけで事が足りるのではないだろうか。 Summaryのレベルでさえ、ダッシュボードには表示されない詳細なデータが記録されている。ダッシュボードやアプリが、UI/UXを実現するために、どのようにデータを選別しているかがわかる。 注釈 ドキュメントには以下の記述がある。Fitbitの製品にはセンサや機能の違いによりいくつかバリエーションがあるが、スキーマはハイエンドに揃えていて、取得できないデータには0, null, falseなど、空データが入るようだ。 Daily summary data and daily goals for elevation (elevation, floors) only included for users with a device with an altimeter. リスエストヘッダに付与した「Accept-Language」によりデータの単位を設定できる。APIを介さず普通にダッシュボードにアクセスすると設定画面から単位を設定できるのだが、それとは異なる単位を指定できるということになる。 The Get Daily Activity Summary endpoint retrieves a summary and list of a user\'s activities and activity log entries for a given day in the format requested using units in the unit system which corresponds to the Accept-Language header provided. ドキュメントに実行例と今回の実行例を比較するとスキーマが大きく異なることがわかる。競争が激しい業界なのでスキーマのアップデートも速いのだろうか。このあたりはドキュメントに記述がなく将来に渡って現在のスキーマが存在し得るのか不安になる。 API詳細 RESTful APIは以下の通り。 GET https://api.fitbit.com/1/user/[user-id]/activities/date/[date].json パラメタは以下の通り。 parameter name explain sample user-id ユーザIDを取得する。\"-\"を指定するとログイン中のユーザ(通常、自分)となる。自分以外のIDを取得したいケースというのは...?。(Fitbitにはグループで実績を共有する概念があるのでグループメンバのIDを指定するのかも...詳細は不明)。 \"-\" date 取得したいデータの日付を指定する。フォーマットは\"yyyy-mm-dd\"。 2016-06-08 summaryオブジェクト key name value means activeScore不明 activityCaloriesFitbitが検出したActivityにより消費したカロリー caloriesBMR基礎代謝 caloriesOut直訳すると、プレミアムプラン会員の専属トレーナー(または自分)が設定した消費カロリー目標。 Calorie burn goal (caloriesOut) represents either dynamic daily target from the premium trainer plan or manual calorie burn goal. Goals are included to the response only for today and 21 days in the past. distancesdistanceオブジェクトの配列 elevation垂直方向の移動距離(?と思われる。) fairlyActiveMinutesまずまず運動している時間 [分] 運動強度は sedentary < light < fairly < veryActive floors垂直方向の移動距離を建物の階数に変換した数値。ダッシュボードではこちらが使われている。 heartRateZonesheartRateZoneオブジェクトの配列 lightlyActiveMinutes軽く運動している時間 [分] marginalCalories不明。あまり重要でないカロリー? restingHeartRate安静時心拍数 sedentaryMinutes座っている、静止している時間 [分] steps1日の歩数 veryActiveMinutes激しく運動している時間 [分] distanceオブジェクト key name value means activity距離を集計するActivity、total, tracker, loggedActivities, veryActive, moderatelyActive, lightlyActive, sedentaryActive distance距離 [km] heartRateZoneオブジェクト key name value means min心拍数ゾーンを定義する最低心拍数 [bmp] max心拍数ゾーンを定義する最大心拍数 [bmp] name心拍数ゾーンに付けられた名称 minutes1日のうち、該当心拍数ゾーンにあった時間 [分] caloriesOut1日のうち、該当心拍数ゾーンで消費したカロリー [Cal] 実行してみる OAuth2 Authenticate grant flow の実装例は前のエントリに書いた。このシリーズを始めてみて気づいたが、FitbitのAPIから得られるデータだけでは個人を識別できない。すなわち、APIから取得できるデータは個人情報ではない。管理側のアプリが個人識別情報とセットでストアして初めて個人情報になる。 データ項目についてはドキュメントを参照のこと。見慣れないデータも出てくるので新鮮だ。caloriesBMRとは基礎代謝だろうか。総消費カロリー(caloriesOut) と別のデータとなっている。 Array ( [activities] => Array ( ) [goals] => Array ( [activeMinutes] => 30 [caloriesOut] => 2722 [distance] => 8.05 [floors] => 10 [steps] => 10000 ) [summary] => Array ( [activeScore] => -1 [activityCalories] => 1380 [caloriesBMR] => 1594 [caloriesOut] => 2822 [distances] => Array ( [0] => Array ( [activity] => total [distance] => 12.61 ) [1] => Array ( [activity] => tracker [distance] => 12.61 ) [2] => Array ( [activity] => loggedActivities [distance] => 0 ) [3] => Array ( [activity] => veryActive [distance] => 7.65 ) [4] => Array ( [activity] => moderatelyActive [distance] => 0.83 ) [5] => Array ( [activity] => lightlyActive [distance] => 4.14 ) [6] => Array ( [activity] => sedentaryActive [distance] => 0 ) ) [elevation] => 54.86 [fairlyActiveMinutes] => 21 [floors] => 18 [heartRateZones] => Array ( [0] => Array ( [caloriesOut] => 1549.5324 [max] => 92 [min] => 30 [minutes] => 1155 [name] => 範囲外 ) [1] => Array ( [caloriesOut] => 646.17384 [max] => 128 [min] => 92 [minutes] => 157 [name] => 脂肪燃焼 ) [2] => Array ( [caloriesOut] => 538.62588 [max] => 156 [min] => 128 [minutes] => 59 [name] => 有酸素運動 ) [3] => Array ( [caloriesOut] => 0 [max] => 220 [min] => 156 [minutes] => 0 [name] => ピーク ) ) [lightlyActiveMinutes] => 158 [marginalCalories] => 904 [restingHeartRate] => 68 [sedentaryMinutes] => 580 [steps] => 15669 [veryActiveMinutes] => 69 ) ) 結論 FitbitAPIからDaily Activity Summary 実データを俯瞰してスキーマの定義をまとめた

default eye-catch image.

Authenticate code grant flow で FitbitAPI からデータを取得する例

Fitbit APIからデータを取得するためには OAuth2 をパスする必要がある。FitbitAPI は以下の2つのflowをサポートしている。 Authenticate grant flow implicit grant flow ドキュメントでは、サーバサイドコードからFitbitAPIにアクセスする場合、Authenticate grant flow が推奨されている。 このエントリでは、djchen/oauth2 というoauth2クライアントを利用して、Authenticate grant flow をパスするサンプルを作成してみる。 composerによるoauth2クライアント(djchen/oauth2)の導入手順 dev.fitbit.comへのサンプルアプリ登録 FitbitのOAuth2認証をパスするためのサンプル実装 Fitbitからサンプルデータの取得 composerによるoauth2クライアントの導入手順 phpスクリプトをブラウザから叩ける環境を用意する。VagrantでもVPSでも何でも。 ~/wwwが DocumentRoot となるように httpd を構成する。 $ pwd /home/ikuty/www 次に、composerを使ってOAuth2クライアントをインストールする。 $ composer require league/oauth2-client djchen/oauth2-fitbit dev.fitbit.comへのサンプルアプリ登録 これから作るサンプルアプリを dev.fitbit.com に登録する必要がある。 例えば以下のような登録を行う。 Application Name: Test Application Description: This is my first test. Application Web Site: http://hoge.com:8001 Organization: Personal Organization Web Site: https://ikuty.com/ OAuth2.0 Application Type: Personal Callback URL: http://hoge.com:8001/test.php Default Data Access: readonly すると、以下を発行してもらえる。 OAUth 2.0 Client ID Client Secret OAuth 2.0: Authorization URI OAUth 2.0: Access/Refresh Token Request URI FitbitのOAuth2認証をパスするためのサンプル実装 サンプル実装、といっても本家のサンプルをそのまま流用しただけだが…。 以下の Authenticate grant flow の流れの通りとなっている。 ClientID,ClientSecretを使用して認証コードを取得する 認証コード を accessToken に変換する accessToken の有効期限に達したら refreshToken を使って accessToken を更新する $ pwd /home/ikuty/www $ vi test.php 中身は以下 <?php require_once \'./vendor/autoload.php\'; use djchenOAuth2ClientProviderFitbit; use LeagueOAuth2ClientTokenAccessToken; $provider = new Fitbit([ \'clientId\' => \'{client_id}\', // 登録時に取得したclientId \'clientSecret\' => \'{clientSecret}\', //登録時に取得したclientSecret \'redirectUri\' => \'http://hoge.com:8002/test.php\' ]); session_start(); if(!isset($_GET[\'code\'])){ $authorizationUrl = $provider->getAuthorizationUrl(); $_SESSION[\'oauth2state\'] = $provider->getState(); header(\'Location: \'.$authorizationUrl); exit; } elseif ( empty($_GET[\'state\']) || ($_GET[\'state\'] != $_SESSION[\'oauth2state\'])){ unset($_SESSION[\'oauth2state\']); exit(\'Invalid state\'); } else { try { $forceToAuth = false; $needToRewrite = false; $lines = file(\'token.txt\',FILE_IGNORE_NEW_LINES); if (($lines == false) || (count($lines) == 0) || $forceToAuth) { echo \'authorization_code->\'; //ここで 認証コード と accessToken を交換する $accessToken = $provider->getAccessToken(\'authorization_code\',[\'code\'=>$_GET[\'code\']]); $needToRewrite = true; } else { echo \'existing AccessToken->\'; $_accessToken = $lines[0]; $_refreshToken = $lines[1]; $_expiredToken = $lines[2]; $accessToken = new AccessToken([\'access_token\'=>$_accessToken, \'refresh_token\'=>$_refreshToken, \'expires_in\'=>$_expiredToken]); // accessToken の有効期限に達したら refreshToken を使って新しい accessTokenを要求する if ($accessToken->hasExpired()) { echo \'refresh AccessToken->\'; $refreshToken = $accessToken->getRefreshToken(); $accessToken = $provider->getAccessToken(\'refresh_token\',[\'refresh_token\'=>$refreshToken]); $needToRewrite = true; } } if ($needToRewrite) { $file = fopen(\"token.txt\",\"wb\"); fputs($file, $accessToken->getToken()); fputs($file, \"n\"); fputs($file, $accessToken->getRefreshToken()); fputs($file, \"n\"); fputs($file, $accessToken->getExpires()); fputs($file, \"n\"); fputs($file, $accessToken->getResourceOwnerId()); fputs($file, \"n\"); fclose($file); } } catch (Exception $e){ } } 最初、「$accessToken->getToken()を保存しておいて次回利用時に使いまわす」具体的な方法が分からなかった。oauth2-client の AccessTokenクラスの実装を見ると、コンストラクタにアクセストークン等を渡してあげればインスタンス化できることがわかった。 アクセストークンの有効期限に達すると、hasExpired()メソッドがtrueを返すようになる。その場合、リフレッシュトークンを使って新しいアクセストークンを要求する。DBに保存しておいたアクセストークンを新しい値で上書きする。 Fitbitからサンプルデータの取得 RESTfulなAPIを指定することでデータが得られる。例えばユーザのプロフィールを取得するには以下の通りとする。厳密にAPIにパラメータを全て埋め込むタイプではなくログイン情報等のセッション情報も用いられるタイプ。以下では、user-idとして\"-\"を渡すとセッションにあるユーザIDが使われるようだ。 $request = $provider->getAuthenticatedRequest( \'GET\', Fitbit::BASE_FITBIT_API_URL . \'/1/user/-/profile.json\', $accessToken, [\'headers\' => [\'Accept-Language\' => \'ja_JP\'],[\'Accept-Locale\' => \'ja_JP\']] ); $response = $provider->getResponse($request); var_dump($response);