先日 Node.js で Google Fit から歩数や体重データを取得するのをやりました。
体重データはスマホアプリから登録できるけど Fit を使う前の過去データは手入力やってられてられないので、API による登録方法を調べました。
公式ドキュメントには記述が見つけられなくて API のヘッダーコメントのサンプルを頼りに試行錯誤が必要でした。
https://github.com/googleapis/google-api-nodejs-client/blob/master/src/apis/fitness/v1.ts
Python 実装ですがこのリポジトリが参考になりました。
GCP のプロジェクトで Fitness API を有効にしたり OAuth 2.0 クライアント ID を作成したり googleapis の NPM パッケージを使用したりするのは前回と同様です。
まず体重データを登録するための DataSource を登録する必要があります。これには、
fitness.users.dataSources.create
メソッドを使用します。dataType
の定義が重要で、name
属性に com.google.weight
を指定し、field
属性の配列にに体重データを格納するための name
属性 weight
、format
属性 floatPoint
を指定します。
device
の model
や manifacturer
などはダミーデータで OK です。*1
async function createDataSource() { const res = await fitness.users.dataSources.create({ userId: "me", requestBody: { "application": { name: "patch_weight", detailsUrl: 'https://example.com', version: "1" }, "dataType": { name: "com.google.weight", field: [ { name: "weight", format: "floatPoint" } ] }, "dataStreamName": "patch_weight", "type": "raw", "device": { manufacturer: "mh", model: "hoge", type: "scale", uid: "pw-01", version: "1.0" } } }); console.log(res.data); }
この関数を、認証関数に続けて実行します。
authenticate(scopes)
.then(client => createDataSource())
.catch(console.error);
次のようなレスポンスが得られます。PROJECT_NO
には Fitness API を有効化している GCP のプロジェクト番号が入ります。
{ dataStreamId: 'raw:com.google.weight:PROJECT_NO:mh:hoge:pw-01:patch_weight', dataStreamName: 'patch_weight', type: 'raw', dataType: { name: 'com.google.weight', field: [ [Object] ] }, device: { uid: 'pw-01', type: 'scale', version: '1.0', model: 'hoge', manufacturer: 'mh' }, application: { version: '1', detailsUrl: 'https://example.com', name: 'patch_weight' }, dataQualityStandard: [] }
この dataStreamId
を指定して、体重データを登録していくことになります。
fitness.users.dataSources.datasets.patch
メソッドを使用して体重データを登録する関数を定義します。
async function patch(dataSourceId, datasetId, start, end, time, val) { const res = await fitness.users.dataSources.datasets.patch({ datasetId: datasetId, dataSourceId: dataSourceId, userId: "me", requestBody: { "dataSourceId": dataSourceId, "minStartTimeNs": start, "maxEndTimeNs": end, "point": [ { dataTypeName: "com.google.weight", startTimeNanos: time, endTimeNanos: time, value: [ { fpVal: val } ] } ] }, }); console.log(res.data); }
時刻についてはなぜかナノ秒単位での指定が必要なので変換関数を用意し、特定日付で dataset を刻んで1件登録。測定時刻は午前7時としました。
function getNano(day) { const dt = new Date(day); return dt.getTime() * 1000000; } const dataSourceId = "raw:com.google.weight:PROJECT_NO:mh:hoge:pw-01:patch_weight"; const start = getNano("2020-12-31T00:00:00"); const end = getNano("2021-12-31T23:59:59"); const time = getNano("2020-12-31T07:00:00"); const datasetId = `${start}-${end}`; const val = 69.3; authenticate(scopes) .then(client => patch(dataSourceId, datasetId, start, end, time, val)) .catch(console.error);
実行結果。エラーにはなりませんでした。
{ minStartTimeNs: '1609340400000000000', maxEndTimeNs: '1640962799000000000', dataSourceId: 'raw:com.google.weight:PROJECT_NO:mh:hoge:pw-01:patch_weight', point: [ { startTimeNanos: '1609365600000000000', endTimeNanos: '1609365600000000000', dataTypeName: 'com.google.weight', originDataSourceId: '', value: [Array] } ] }
ちゃんとデータが入ったか確認するため、前回の歩数と体重データ取得関数で日付を指定して取得してみます。
const from = new Date("2020-12-31T00:00:00"); const to = new Date("2020-12-31T23:59:59"); authenticate(scopes) .then(client => aggregate(client, from, to)) .catch(console.error);
実行結果。歩数は Pixel から、体重は API で登録した DataSource から取得され、他のデバイスと同等に集計されているようです。
raw:com.google.step_count.cumulative:Google:Pixel 3 XL:caa195e4620531ba:Step Counter 2020/12/31 15:05:19 2020/12/31 16:39:24 com.google.step_count.delta 3648 raw:com.google.weight:PROJECT_NO:mh:hoge:pw-01:patch_weight 2020/12/31 7:00:00 2020/12/31 7:00:00 com.google.weight.summary 69.3
ちなみに、Fit アプリで登録したデータはこのようにアプリでユーザの手入力とわかるようになっています。*2
raw:com.google.weight:com.google.android.apps.fitness:user_input 2021/7/23 8:58:26 2021/7/23 8:58:26 com.google.weight.summary 67.80000305175781
Pixel の Fit アプリでも反映まで少し時間がかかりましたが、API で登録したデータが確認できました。
ということで、過去データも Fit に登録できるようになりました。