LoginSignup
27
8

タニタの「Health Planet API」から1週間の体重の平均値を毎朝LINE通知させる

Last updated at Posted at 2023-12-20

これは、ディップ Advent Calendar 2023の記事になります。

経緯

私は趣味で筋トレやボディメイクに取り組んでいます。その一環として毎朝、タニタの体重計で体重を測っています。

タニタの体重計は、Bluetoothを介して、測定したデータを専用アプリ「Health Planet」に送信してくれる機能があります。この機能を使って、毎日の測定結果をデジタルデータとして記録しています。

人間の体重は、食事量や水分量、睡眠時間などで常に変動しているため、ボディメイクでは、1日ごとの体重の変化よりも、週単位、月単位での中長期的な平均の推移を見てあげることが重要です。しかし、毎日の体重を記録し、その平均を手計算するのはとても面倒くさい作業です。

そこで、タニタの「Health Planet API」の存在を知りました。このAPIを利用して、自動でデータの取得し、平均値や増減の算出を行い、毎日必ず見るLINEに通知を送れば、自身のボディメイクにかなり役に立つと思い、実装してみようと考えました。

Health Planet API とは?

「Health Planet API」は、日時や取得項目などを指定して、測定日時や体重、体脂肪率などを取得できるAPIです。

詳しくは、Health Planet API 仕様書をご覧ください。

今回作ったもの

直近7日間の平均体重と先週分(8~14日前)の平均体重との増減を、毎朝8時にLINE通知するシンプルなものです。

スクリーンショット 2023-12-10 11.25.24.png

実装

今回は、大まかに

  1. Health Planet APIから測定データ取得
  2. 取得した測定データから平均値や増減の算出
  3. LINE Notifyを使って通知
  4. GitHub Actionsのscheduleイベントから毎朝8時に通知処理を実行

という流れで実装しました。

実装のコードは掻い摘んで載せるので、全貌はGitHubのリポジトリをご覧ください。

1. Health Planet API から体重データを取得

その前に)Health Planet APIのユーザー認証

Health Planet APIは、誰でもすぐ叩けるというわけではなく、ユーザー認証を行う必要があります。
ユーザー認証、データ取得までの過程は以下の記事でまとめられていたので、こちらをご覧ください。

直近2週間分の体重データを取得

次にデータを取得します。
言語はTypeScriptを使いました。(言語はなんでもいいと思います)

今回、実行環境はブラウザではなくNode.jsを想定しているため、ブラウザのみでサポートされているJavaScript標準のfetchメソッドではなく、axios で、データフェッチを行いました。

また、日時の処理は「date-fns」を使用しています。

/** 体重データを取得 */
export const fetchWeight = async () => {
  /** Health Planet API URL */
  const url = "https://www.healthplanet.jp/status/innerscan.json";

  /** 現在のJST時間を取得 */
  const jstNow = utcToZonedTime(new Date(), "Asia/Tokyo");

  /** 14日前(YYYYMMDD000000) */
  const date14DaysAgo = format(subDays(jstNow, 14), "yyyyMMdd") + "000000";
  /** 1日前(YYYYMMDD235959) */
  const date1DayAgo = format(subDays(jstNow, 1), "yyyyMMdd") + "235959";

  const params = {
    access_token: process.env.HEALTH_PLANET_ACCESS_TOKEN,
    date: "1",
    from: date14DaysAgo,
    to: date1DayAgo,
    tag: "6021",
  };

  try {
    const response = await axios.get(url, { params });
    if (response.status !== 200) {
      throw new Error("Network response was not ok");
    }
    return response.data;
  } catch (error) {
    console.error("Error fetching data from Health Planet API", error);
    throw error;
  }
};

すると、以下のようなデータが返ってきます。(平気で晒す)

{
  birth_date: '19991027',
  data: [
    {
      date: '202312090750', // 2023年12月9日7時50分
      keydata: '74.10', // 74.10kg
      model: '01000099', // 測定機器名(01000099は、RD-903)
      tag: '6021' // 体重
    },
    {
      date: '202312080728',
      keydata: '73.90',
      model: '01000099',
      tag: '6021'
    },
    // 略
    {
      date: '202311270816', // 2週間前まで取得
      keydata: '74.00',
      model: '01000099',
      tag: '6021'
    }
  ],
  height: '173',
  sex: 'male'
}

こちらのデータをもとに、平均値や増減の算出を行っていきます。

2. 取得した測定データから平均値と増減の算出

date-fnsのisWithinIntervalメソッドを使い、取得した2週間分のデータから、今週分の体重(1~7日前の体重)と先週分の体重(8~14日前の体重)で分けました。

そこから、今週分の体重の平均と先週分との体重の増減を算出しました。
calcWeightAverageDiff関数では、以下のような返り値が返ってきます。

/** 今週分(1~7日前)の体重の平均と先週分との体重の増減 */
const { currentWeekAverageWeight, weeklyWeightDiff } = calcWeightAverageDiff(fetchData);
console.log(currentWeekAverageWeight, weeklyWeightDiff);

>> 73.63000000000001 -0.15

これは直近1週間の平均体重が73.63kg、先週の体重平均と比較して-0.15kgだったことを表しています。

ここで算出した値を、LINE通知させていきます。

3. LINE Notifyでの通知

「LINE Notify」は、文字通りLINEでメッセージを通知するためのLINE公式サービスです。

LINE Notify トークンの発行

LINE Notifyでも、アクセストークンを発行する必要があります。

トークンの発行方法は以下の記事にまとめられていましたので、こちらをご覧ください。

LINE通知する関数を作成

まず、今週の体重 XX.Xkg (±〇〇.〇kg)の形になるように通知するメッセージを整形します。

/** 通知メッセージの整形 */
export const formatMessage = (currentWeekAverageWeight: number, weeklyWeightDiff: string) => {
  /** 通知メッセージ */
  const message = `\n今週の体重 ${currentWeekAverageWeight.toFixed(2)}kg(${weeklyWeightDiff}kg)`;

  return message;
};

整形した通知メッセージを引数に渡して、以下のようにsendLineNotification関数でLINE通知を行います。

export const sendLineNotification = async (message: string) => {
  const url = "https://notify-api.line.me/api/notify";
  const token = process.env.LINE_NOTIFY_TOKEN;

  const headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    Authorization: `Bearer ${token}`,
  };

  try {
    const response = await axios.post(url, `message=${encodeURIComponent(message)}`, { headers });
    if (response.status !== 200) {
      throw new Error("Network response was not ok");
    }
  } catch (error) {
    console.log("Error sending LINE notification", error);
    throw error;
  }
};

次のステップで、体重取得、計算処理、LINE通知の一連の処理を定期実行させていきます。

4. GitHub Actionsで毎朝8時に定期実行させる

当初、cronジョブを設定したり、Macの「Automator」を使用して、定期実行させていました。ただこれらの方法だと、JavaScriptを実行するコンピューターがスリープ状態のときは、実行できないという懸念点がありました。

そこで、GitHub Actionsのscheduleイベントで毎朝8時に定期実行させようと考えました。

アクセストークンをSecretsに追加

コードでは、Health Planet API と LINE Notify のトークンを環境変数にしているので、GitHub Actionsでも環境変数を使用するために、リポジトリのセキュリティ設定に値を追加する必要があります。

以下の手順で追加

  1. タブ「Settings」を選択
  2. サイドバー「Secrets and variables」から「Actions」を選択
  3. 「New repository secret」で追加

以下のようになればOKです。
image.png

ワークフローの作成

協定世界時(UTC)で23時(日本標準時で翌朝の8時)に定期実行させるようにワークフローを作成して、Pushします。

name: Daily Weight Notification

on:
  schedule:
    - cron: "0 23 * * *" # UTCで毎日23時(JSTで翌日の8時)
  workflow_dispatch:

jobs:
  build-and-run:
    runs-on: ubuntu-latest

    env: # 環境変数を設定
      LINE_NOTIFY_TOKEN: ${{ secrets.LINE_NOTIFY_TOKEN }}
      HEALTH_PLANET_ACCESS_TOKEN: ${{ secrets.HEALTH_PLANET_ACCESS_TOKEN }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm install

      - name: Compile TypeScript
        run: npm run build # tscを実行するように、事前にbuildコマンドを用意

      - name: Run script
        run: node dist/index.js

そうすると、リポジトリのActionsタブから定期実行されていることが確認できました。

image.png

感想

感想としては、「便利すぎる」という点に尽きます。

実際に今年の夏、人生で初めて本格的な減量を行いましたが、12~3キロほど、計画的に減量させることができました。もちろん減量期だけではなく、増量期にも使っています。

今回の開発で、日常の「こういうことできたらいいけど、現状ちょっと足りないな...」というかゆい機能を自分でプラスできるのは、開発してて楽しいなと感じました。

あと、ドヤ顔で晒せるぐらいムキムキになりたい.....。

補足

使用している体重計

innerscanDUAL RD-903

27
8
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
27
8