始めに
私は普段からFitbitを愛用しています。(愛機はFitbit Versa 4)
Fitbitは健康とフィットネスを追跡するためのデバイスとサービスを提供する会社です。
Fitbitの製品ラインアップには、アクティビティトラッカーやスマートウォッチがあり、歩数、心拍数、睡眠の質、消費カロリーなどのデータを専用のデバイスで取得し管理しています。
このFitbitのアクティビティデータを使ってなにか出来ないかと思い、まずはAPIからのデータ取得方法の手順を調べたのでメモします。
Fitbit開発者アカウントの作成
簡単に流れを説明すると以下の流れです。
- https://accounts.fitbit.com/signupにアクセスして、fitbit.comアカウントの登録
- Fitbit.comのアカウントが登録できたら、https://dev.fitbit.com/appsへアクセスしてAIPを利用するアプリケーションを作成
ダッシュボードへログイン
Fitbit.comのアカウントでダッシュボードにログインできることを確認。
dev.fitbit.comへログイン
Fitbit.comと同じアカウントでdev.fitbit.comへログインできることを確認。
アプリケーションを作成
「Register a new app」をクリック
とりあえず、「Redirect URL」以外のURL入力欄はhttp://localhost
と入力しておきます。
「I have read and agree to the terms of service」をチェックしてピンクの「Register」ボタンを押すと、以下のページが表示されます。
左下の「OAuth2.0 Tutorial」をクリックして、アクセス・トークン等の発行を行います。
App Settings
「Client ID」は自動的に表示されているのでそのままにしておきます。
「Application Type」は「Client (for client-side access)」を選択します。
Getting an Access Token
Step 1: Generate PKCE and State Values
アクセス・トークンを発行するために必要な情報を生成します。
「Generate」ボタンを押します。(2箇所)
生成される文字列は一時的なものなので控えておく必要はありません。
Step 2: Display Authorization Page
APIで取得を認める項目の選択です。
デフォルトでは全てにチェックが入っているので取得を認めない項目からチェックを外します。
The permission scopes to request access to. Only request what your app needs.
アクセスを要求する権限のスコープ。アプリに必要なものだけをリクエストしてください。
権限のチェックができたら「Authorization URL」をクリックします。
自動で認可ページが開きます。
「すべて許可する」をチェックし、「許可」ボタンを押します。
すると、別タブでRedirect URLで指定したhttp://localhost:8000/~
が開き「このサイトにアクセス出来ません」と表示されていると思います。
閉じずにURLをコピー してください。
Step 3: Handle the Redirect
先ほどのページに戻り、下の画像の赤枠のところにコピーしたURLを貼り付けます。
URLからAuthorization CodeとStateを抽出して表示してくれます。
いずれの値も控えておく必要はありません。
Step 4: Get Tokens
上記のURLを元にリクエストを自動生成してくれています。
「SUBMIT REQUEST」のボタンを押します。
下のテキストエリアにエラーが表示されていなければOKです。
Access TokenとRefresh Tokenは必ず控えてください。
Access TokenはAPIを実行するのに使います。
このTokenは8時間の寿命なので、期限が切れた場合はRefresh Tokenを使ってAccess Tokenの再発行を行います。
Access TokenとRefresh Tokenは人に見せないようにしてください。
Access TokenまたはRefresh Tokenが流出した可能性がある場合、ここで作成したアプリを削除してください。
Step 5: Check Scopes
この後のステップは、profile取得のAPIが通るかのテストとRefresh Tokenを使って再発行をするチュートリアルなので、不要なら飛ばしてください。
Profileはそもそも権限を与えていない場合はエラーになるので、試す場合はご留意ください。
また、Refresh Tokenを使って再発行をすると、Access TokenとRefresh Tokenともに更新されるので、更新後の値を控えておいてください。
Access User Data
これまでの手順で作成したAceess TokenとAPI Endpoint URLが自動でセットされているので、「SUBMITREQUEST」を押します。
APIエンドポイントからユーザープロファイルのデータが取得され「Respose」欄に表示されればOKです。
Refresh Tokens
Access Tokenは8時間の寿命なので、期限が切れた場合はRefresh Tokenを使ってAccess Tokenの再発行を行います。
Refresh Tokenが自動でセットされているので、「SUBMITREQUEST」を押します。
Refresh Tokenを用いて、Access Tokenが更新されました。(古いAccess Tokenは使えなくなります)
NodejsからAPIを叩いて情報を取得
さて、本題のFitbitAPIでデータ取得をするアプリケーションを自作します。
今回はNode.jsでFitbit APIを利用して「Get Heart Rate Time Series by Date」を取得する簡単なサーバーサイドアプリケーションを作成します。
Node.jsプロジェクトの初期設定
app.jsを作成します。
mkdir fitbit-nodejs
cd fitbit-nodejs
npm init -y
必要なパッケージのインストール
npm install axios dotenv
環境変数の設定
CLIENT_ID=
ACCESS_TOKEN=
REFRESH_TOKEN=
いずれもこれまでで取得した値で埋めてください。
client_idはアプリの登録ページで、登録したアプリをクリックすると確認できます。
データ取得スクリプトの作成
プロジェクトのルートにindex.jsファイルを作成し、以下のコードを追加します。これにより、指定された日付に基づいて心拍数データを取得します。
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const path = require('path');
async function refreshAccessToken() {
const url = 'https://api.fitbit.com/oauth2/token';
const clientId = process.env.CLIENT_ID;
const refreshToken = process.env.REFRESH_TOKEN;
const body = new URLSearchParams();
body.append('grant_type', 'refresh_token');
body.append('client_id', clientId);
body.append('refresh_token', refreshToken);
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
try {
const response = await axios.post(url, body.toString(), config);
const { access_token, refresh_token } = response.data;
updateEnvFile(clientId, access_token, refresh_token);
console.log('Access token and refresh token updated.');
return access_token;
} catch (error) {
console.error('Error refreshing token:', error.response.data);
}
}
function updateEnvFile(clientId, accessToken, refreshToken) {
const envPath = path.join(__dirname, '.env');
const envContents = `CLIENT_ID=${clientId}\nACCESS_TOKEN=${accessToken}\nREFRESH_TOKEN=${refreshToken}\n`;
fs.writeFileSync(envPath, envContents, 'utf8');
console.log('.env file has been updated with new tokens.');
}
async function getHeartRate(date = 'today', period = '1d') {
const accessToken = await refreshAccessToken();
const url = `https://api.fitbit.com/1/user/-/activities/heart/date/${date}/${period}.json`;
try {
const response = await axios.get(url, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return response.data;
} catch (error) {
console.error('Error fetching heart rate data:', error.response.data);
}
}
(async () => {
const heartRateData = await getHeartRate(); // 今日の1日分の心拍データを取得
console.log(JSON.stringify(heartRateData));
})();
取得結果
{
"activities-heart": [
{
"dateTime": "2024-06-13",
"value": {
"customHeartRateZones": [],
"heartRateZones": [
{
"caloriesOut": 2111.9742600000004,
"max": 109,
"min": 30,
"minutes": 1427,
"name": "Out of Range"
},
{
"caloriesOut": 88.36310999999998,
"max": 133,
"min": 109,
"minutes": 13,
"name": "Fat Burn"
},
{
"caloriesOut": 0,
"max": 163,
"min": 133,
"minutes": 0,
"name": "Cardio"
},
{
"caloriesOut": 0,
"max": 220,
"min": 163,
"minutes": 0,
"name": "Peak"
}
],
"restingHeartRate": 62
}
}
],
"activities-heart-intraday": {
"dataset": [
{
"time": "00:00:00",
"value": 62
},
{
"time": "00:01:00",
"value": 61
},
...長いので割愛
{
"time": "16:34:00",
"value": 78
},
{
"time": "16:35:00",
"value": 74
},
{
"time": "16:36:00",
"value": 76
},
{
"time": "16:37:00",
"value": 76
},
{
"time": "16:38:00",
"value": 80
}
],
"datasetInterval": 1,
"datasetType": "minute"
}
}
最後に
APIからデータ取得ができたので、なにをしようかな。これから考えます。
この記事は個人の見解であり所属する組織を代表しません。