1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CloudFunctionsでFitbitアクティビティデータを取得する

Posted at

概要

  • CloudFunctionsを使って、Fitbitのアクティビティデータを取得したい。
  • そのやり方をまとめました。

環境

firebase-tools 11.16.0

前提条件

  • Node.jsのダウンロード・インストール済み

Fitbitのアクティビティデータって何?

  • Fitbitのスマートウォッチ上で、エクササイズ > XXの開始 を行って記録する、運動時のデータです。
    • エクササイズの種類には、 ウォーク、ラン、バイク、スイム、ワークアウト、トレッド があります。
    • 心拍、緯度経度、時刻などが記録されます。
  • Fitbit Home > サインイン > ダッシュボード > 記録 > 運動 > アクティビティ履歴 > 詳細を表示 > で表示されるデータがアクティビティデータです。
    • URLの末尾番号がアクティビティのlog_idです。

image.png

image.png

image.png

image.png

image.png

アクティビティデータの取得の流れ

  1. log_id_listを取得する
    • ex) {510001, 510005, 510007, 510009}
  2. log_idを指定して.tcxデータを取得する

ライブラリインストール

my-project/functions
npm install axios

ログIDリストを取得するサンプルコード

  • Get Activity Log Listの公式ドキュメント にアクティビティのlog_id_listを取得するCURLコマンドが載っています。
  • 公式ドキュメント > Query Parameters に記載ある通り、beforeDateafterDateのどちらかを指定が必要。今回は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)
                // アクセストークンを更新する ※ここは割愛します
                // ...
                // ...
                // ...
            }
        })
}

アクティビティデータを取得するサンプルコード

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情報を取得できないので、LatitudeDegreesLongitudeDegreesのデータは入っていないです。
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>
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?