👋

fitbitに保存された体重をspreadsheetに出力する

2022/12/11に公開約3,600字

はじめに

google pixel watchを購入して、健康情報がfitbitに集まるようになりました。
また、Homdoxという家電ブランドの体重計を買ったんですが、体重の情報はHealthU+というアプリに集まります。
https://www.fitbit.com/global/jp/home

体重計はこちら
https://www.amazon.co.jp/gp/product/B08R35SMS1

HealthU+はfitbitへデータ連携が可能であるため、fitbitには健康情報の大半が集まっていそうです。というわけでfitbitの情報が取れないか(ゆくゆくはNotionなどに格納?)確認してみました。

fitbitのAPIが使えるようになるまで

以下を参考にトークン情報を入手
https://life-table.com/gas-fitbit-api/

code

メイン処理。

main.js
function main() {
  const fitbitManager = new FitbitManager();
  fitbitManager.updateToken();
  const weightList = fitbitManager.getTodayWeightList();

  if(!weightList.length) return;

  addSheetLastRow(
    SHEET.weight,
    weightList.map(weight => weight.getOutList())
  );
}

fitbitとAPI接続を行う処理をクラス化。
トークン更新の処理は以下参考。
https://dev.fitbit.com/build/reference/web-api/authorization/refresh-token/

FitbitManager.js
class FitbitManager{
  constructor(){
    const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET.config.name);
    this.id = sheet.getRange(SHEET.config.cell.id).getValue();
    this.accessToken = sheet.getRange(SHEET.config.cell.accessToken).getValue();
    this.refreshToken = sheet.getRange(SHEET.config.cell.refreshToken).getValue();
  }

  getTodayWeightList(){
    const option = {
      method: 'get',
      headers: {
        Authorization: `Bearer ${this.accessToken}`,
      },
    };
    
    const res = UrlFetchApp.fetch(`https://api.fitbit.com/1/user/${this.id}/body/log/weight/date/today.json`, option);
    const json = JSON.parse(res);
    return json.weight.map(json => new Weight(json));
  }

  updateToken(){
    const option = {
      method: 'post',
      headers: {
        Authorization: `Bearer ${this.accessToken}`,
      },
    };
    
    const res = UrlFetchApp.fetch(`https://api.fitbit.com/oauth2/token?grant_type=refresh_token&expires_in=90000&refresh_token=${this.refreshToken}`, option);
    const json = JSON.parse(res);
    this.accessToken = json.access_token;
    this.refreshToken = json.refresh_token;

    const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET.config.name);
    sheet.getRange(SHEET.config.cell.accessToken).setValue(this.accessToken);
    sheet.getRange(SHEET.config.cell.refreshToken).setValue(this.refreshToken);
  }
}

体重情報(クラス)。

Weight.js
class Weight{
  constructor(json){
    this.bmi = json.bmi;
    this.fat = json.fat;
    this.time = dayjs.dayjs(`${json.date} ${json.time}`);
    this.weight = json.weight;
  }

  getOutList(){
    return [
      this.time.format('YYYY/MM/DD HH:mm'),
      this.weight,
      this.fat,
      this.bmi
    ];
  }
}

spreadsheet操作の処理。

sheet.js
const SHEET = {
  config: {
    name: 'config',
    cell: {
      id: 'b2',
      accessToken: 'b3',
      refreshToken: 'b4',
    },
  },
  weight: {
    name: 'weight',
  },
};

function getSheetData(sheetConfig){
  let data = getSheetDataFull(sheetConfig);
  [...Array(sheetConfig.row.data - 1)].forEach(_ => data.shift());
  return data;
}

function getSheetDataFull(sheetConfig){
  const sheet = SpreadsheetApp.getActive().getSheetByName(sheetConfig.name);
  return sheet.getDataRange().getValues();
}

function addSheetLastRow(sheetConfig, list, column){

  const sheet = SpreadsheetApp.getActive().getSheetByName(sheetConfig.name);

  sheet.getRange(
    sheet.getLastRow() + 1,
    column !== undefined ? column : 1,
    list.length,
    list[0].length
  ).setValues(list);
}

あとはトリガーで23時頃に実行させる。

できたもの

Discussion

ログインするとコメントできます