概要
- CloudFunctionsを使って、Fitbitのアクティビティデータを取得したい。
- そのやり方をまとめました。
環境
firebase-tools 11.16.0
前提条件
- Node.jsのダウンロード・インストール済み
Fitbitのアクティビティデータって何?
- Fitbitのスマートウォッチ上で、エクササイズ > XXの開始 を行って記録する、運動時のデータです。
- エクササイズの種類には、 ウォーク、ラン、バイク、スイム、ワークアウト、トレッド があります。
- 心拍、緯度経度、時刻などが記録されます。
-
Fitbit Home > サインイン > ダッシュボード > 記録 > 運動 > アクティビティ履歴 > 詳細を表示 > で表示されるデータがアクティビティデータです。
- URLの末尾番号がアクティビティの
log_id
です。
- URLの末尾番号がアクティビティの
アクティビティデータの取得の流れ
-
log_id_list
を取得する- ex) {510001, 510005, 510007, 510009}
-
log_id
を指定して.tcxデータを取得する
ライブラリインストール
-
CloudFunctionsフォルダ直下へ以下のライブラリをインストールします。
-
Axios
- GET, POST用
- 公式ドキュメント https://www.npmjs.com/package/axios
my-project/functions
npm install axios
ログIDリストを取得するサンプルコード
-
Get Activity Log Listの公式ドキュメント にアクティビティの
log_id_list
を取得するCURLコマンドが載っています。 - 公式ドキュメント > Query Parameters に記載ある通り、
beforeDate
かafterDate
のどちらかを指定が必要。今回はafterDate
を指定したサンプルです。- 実際に使う場合は、本日の日付-n日を指定すると良いと思います。
-
status===401
の場合はアクセストークンの有効期限切れなので、リフレッシュトークンを使用してアクセストークンの更新を行うことが必要です。- やり方は以前書いた記事 を参照。
- Curl Converter のサイトを使うと、CURLコマンドをnode-fetchやaxiosの書き方へコンバートできるので便利です。
my-project/functions/index.js
const functions = require("firebase-functions");
const axios = require('axios')
// n日前のDate型データを取得する
function getPastDate(n) {
let pastDate = new Date()
pastDate.setDate(pastDate.getDate() - Number(n))
return pastDate
};
// Date型データをFitbitのGETメソッドに適したyyyy-MM-ddに整形するメソッド (例: "2022-09-01")
function formatDateForFitbit(mDate) {
const year = mDate.getFullYear()
const month = ('00' + (mDate.getMonth()+1)).slice(-2)
const day = ('00' + mDate.getDate()).slice(-2)
const convertedDate = year+"-"+month+"-"+day
return convertedDate
};
// ログIDリストの取得
async function getLogIdList(accessToken) {
// アクセストークン
const currentAccessToken = accessToken
// 何日前からのlog_idを取得するか(今回は直近14日間のデータとする)
const pastDay = 14
// log_idを取得する開始日付(yyyy-MM-ddの形式で任意の日付を指定する。例:"2022-10-20")
const afterDate = formatDateForFitbit(getPastDate(pastDay))
// 取得するデータ最大数(最大100)
const dataCountLimit = "100"
// log_idを格納する配列
let logIdArray = new array()
// Fitbitからactivity_log_id_listを取得
await axios
.get(
'https://api.fitbit.com/1/user/-/activities/list.json', {
params: {
'afterDate': afterDate,
'sort': 'asc',
'offset': '0',
'limit': dataCountLimit
},
headers: {
'accept': 'application/json',
'authorization': 'Bearer '+currentAccessToken,
}
})
.then(response => {
// 取得したログを配列へ格納
for (let i = 0; i < response.data.activities.length; i++) {
// 取得したlogIdは数値型なので扱いやすいString型に変換する
const logId = String(response.data.activities[i].logId)
logIdArray.push(logId)
}
})
.catch(error => {
console.log("log_id_listを取得に失敗 -> error ", error)
console.log("status: "+error.response.status)
if(error.response.status===401) {
// アクセストークンが有効期限切れの場合(status401)
// アクセストークンを更新する ※ここは割愛します
// ...
// ...
// ...
}
})
}
アクティビティデータを取得するサンプルコード
-
Get Activity TCXの公式ドキュメント に指定した
log_id
のアクティビティデータを取得するCURLコマンドが載っています。
my-project/functions/index.js
const functions = require("firebase-functions");
const axios = require('axios')
// ログIDリストの取得
async function getActivityData(mAccessToken, mLogId) {
const currentAccessToken = mAccessToken
const logId = mLogId
await axios
.get(
"https://api.fitbit.com/1/user/-/activities/"+logId+".tcx", {
headers: {
'accept': 'application/json',
'authorization': 'Bearer '+currentAccessToken,
'content-type': 'application/json',
}
})
.then(async(response) => {
// 受信データはXML形式になっています
const xmlDoc = response.data
// xmlDocから必要なデータを取得・加工 ※ここは割愛します
// ...
// ...
// ...
})
.catch(error => {
console.log("activity_dataの取得失敗 -> error: ", error)
})
}
- ちなみに、
const xmlDoc = response.data
で受け取るデータサンプルはこんな感じです。 - アクティビティデータ記録時に、スマホとBLE接続していない場合は、GPS情報を取得できないので、
LatitudeDegrees
やLongitudeDegrees
のデータは入っていないです。
response.data
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
<Activities>
<Activity Sport="Other">
<Id>2022-09-11T10:39:59.000+09:00</Id>
<Lap StartTime="2022-09-11T10:39:59.000+09:00">
<TotalTimeSeconds>1804.0</TotalTimeSeconds>
<DistanceMeters>195.06</DistanceMeters>
<Calories>71</Calories>
<Intensity>Active</Intensity>
<TriggerMethod>Manual</TriggerMethod>
<Track>
<Trackpoint>
<Time>2022-09-11T10:39:59.000+09:00</Time>
<Position>
<LatitudeDegrees>26.327766180038452</LatitudeDegrees>
<LongitudeDegrees>127.75293326377869</LongitudeDegrees>
</Position>
<AltitudeMeters>26.6</AltitudeMeters>
<DistanceMeters>0.0</DistanceMeters>
<HeartRateBpm>
<Value>79</Value>
</HeartRateBpm>
</Trackpoint>
・・・以下、<Trackpoint></Trackpoint>が沢山続きます・・・
<Trackpoint>
<Time>2022-09-11T11:10:04.000+09:00</Time>
<Position>
<LatitudeDegrees>26.266780853271484</LatitudeDegrees>
<LongitudeDegrees>127.72172796726227</LongitudeDegrees>
</Position>
<AltitudeMeters>16.89</AltitudeMeters>
<DistanceMeters>0.0</DistanceMeters>
<HeartRateBpm>
<Value>96</Value>
</HeartRateBpm>
</Trackpoint>
</Track>
</Lap>
<Creator xsi:type="Device_t" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<UnitId>0</UnitId>
<ProductID>0</ProductID>
</Creator>
</Activity>
</Activities>
</TrainingCenterDatabase>