もりはやメモφ(・ω・ )

ITとか読書感想文とか

TanitaのHealthPlanetからFitbitへDailyでデータ連携をする

2023年はそこそこジョグができている、もりはやです。

10年ほど前の私は有酸素運動全般が好きで、かつては”八景島トライアスロン”や”いわて銀河100kmマラソン”にエントリーしては制限時間ぎりぎりでクリアするような週末を年に数回の楽しみとしていました。


しかしながら家族が増えた*1ことや、3年におよびCovid-19によるリモートワークなどを理由に体力は下がり体重は上がる日々です。


このままでは体力の前に健康がまずいと感じ、改めて有酸素運動の再開と継続的な体重測定を再開しました。

Tanitaさんの体重管理SaaSであるHealthPlanet

私が愛用している体重計はTanitaさんのRD-907です。

www.tanita.co.jp

購入した決め手はBluetoothによるスマホ連携で、Health Planetと呼ばれるSaaSにデータを格納していける点です。 https://www.healthplanet.jp/

体重以外の活動計はFitbitを利用してます

体重測定の点で上述した Tanita RD-907 全く不足はありませんが、日常的な活動系としてはFitbitのInspierシリーズを愛用しています。特に最新版のInspier3はInspier2で課題になっていた液晶が暗く陽光の下では全く見えない問題が改善されており、1週間近く持つバッテリーとクリアな液晶に大変満足いく製品となっています。*2

www.fitbit.com

Fitbitで測定できる指標は多くあり、私は特に睡眠時間とその評価、1日の活動量を中心に見ています。そしてFitbitにも体重を管理する項目はあります。本来であればFitbitが販売する以下の製品を購入することで統合的なヘルスメトリクスをFitbitへ集約することができますが、体重計についてはTanitaを離れるつもりが今の所ありません。そうして今回のブログタイトルでもある「TanitaのHealthPlanetからFitbitへDailyでデータ連携をする」ことを検討し始めました。

Fitbit Aria 2™ (https://www.fitbit.com/jp/aria2)Wi-Fi Smart Scale

雑に調べる

TanitaのHealth PlanetおよびFitbitはリリースから数年が経ち十分に成熟したプロダクトです。つまり、似たようなことをしている偉大な先人たちがいると考え調査を始めました。

IFTTTにはなかった

最初に調べたのはIFTTTです。一時期ほどの流行りではなくなった印象がありますが、SaaSとして簡単にデータを連携するなら良いソリューションだと考えました。いくつかFitbit関連のものは見つかりましたが、残念ながら Health Planet to Fitbit を行うものは見つかりませんでした。こう言った時に日本製ハードウェアのグローバルなソフトウェア展開の弱さを少し感じたりするのかもしれません。

https://ifttt.com/search/query/health%20planet%20fitbit

DuckDuckGoで見つかる

DuckDuckGoで"fitbit healthplanet"を検索したところ、早速以下の記事が見つかりました。*3



軽く見たところ後者の記事が2020年とまだ新しい方ではありましたが、コードを手元で動かすのではなく個人提供のWebアプリとしての提供だったため将来性を考えて見送りました。一方で前者は2019年と4年前の記事でしたが手順が丁寧かつコードについても手元の環境で動かすもので良さそうに見えます。

GitHubにもいくつかあり、採用にいたる

上記の2サイトから自分が利用する方法を決めても問題はありませんでしたが、今度はGitHubを検索します。

https://github.com/search?q=fitbit%20healthplanet&type=repositories

Star付きで一番上に出てくるのは上記のブログでも紹介されていた hirotow さんを参考にされた以下でした。動作はGAS(Google App Script)のためJavascriptで書かれています。

https://github.com/YoshihideShirai/Hp2Fit

もう一つの方は以下でこちらはGo言語で書かれており、直近3ヶ月分を取得して一気にFitbitに反映する仕組みとなっているようでした。こちらはDockerfileも用意され、Docker Hubにも公開されているようです。

https://github.com/tattsun/healthplanet-to-fitbit

少し考えた上で以下を理由に後者の tattsun/healthplanet-to-fitbit を利用させてもらうこととしました。サンキュー tattsun さん!

  • Google Spread Sheetを中間として利用せずにシンプルにデータ連携している
  • Docker Hubにイメージがありローカルでシュッと動かすこともできそう
  • Goの方がJavascriptよりもまだ分かる*4
  • DockerfileとかDocker Hubへの公開とかで個人的に好感が持てる

tattsun/healthplanet-to-fitbit をローカルで試すための準備

最終的にはDailyで同期処理を動かしたいですが、まずは手元で動作確認を行います。丁寧なReadmeに感謝しながら読んでいくと以下の情報を変数として渡すことが必要とわかります。

環境変数名 内容
HEALTHPLANET_CLIENT_ID HealthPlanet 公式サイトで発行できるクライアント ID
HEALTHPLANET_CLIENT_SECRET HealthPlanet 公式サイトで発行できるクライアントシークレット
HEALTHPLANET_ACCESS_TOKEN HealthPlanet のアクセストークン(healthplanet-gettoken を使用して取得する)
FITBIT_CLIENT_ID Fitbit 公式サイトで発行できるクライアント ID
FITBIT_CLIENT_SECRET Fitbit 公式サイトで発行できるクライアントシークレット
FITBIT_ACCESS_TOKEN Fitbit のアクセストークン(fitbit-gettoken を使用して取得する)
FITBIT_REFRESH_TOKEN Fitbit のリフレッシュトークン(fitbit-gettoken を使用して取得する)

どうやらHealth PlanetとFitbitそれぞれでクライアントIDとシークレットを取得し、それを使ってアクセストークンを払い出す必要があるようです。

Health Planetのアクセストークンの払い出し

https://www.healthplanet.jp/login.do へアクセスします。少し迷いましたが以下の順にページ遷移して目的のIDとシークレットを手に入れました。*5

HealthPlanet TOP >Change Registration Information >API Settings

このIDとシークレットを利用してアクセストークンを払い出しますが、少し悩むところです。

APIのドキュメントページに丁寧な説明がありますので、そちらを参考にコマンドを作っていきました。具体的には以下のようになります。

https://www.healthplanet.jp/apis/api.html

Readmeにさりげなく書いてありますが Health Planet からアクセストークンを取得するには以下のように実行します。 ~/.healthplanet-to-fitbit.env は任意の場所でOKですし、環境変数として渡しても良いです。

$ docker run -v ~/.healthplanet-to-fitbit.env:/usr/src/app/.env --rm -it tattsun/healthplanet-to-fitbit healthplanet-gettoken

上記実行後に認証用のURLと Input code のプロンプトが表示されるため、リンク先で入手したコードを入力することでアクセストークンを入手することができます。

Screen Shot 2023-10-01 at 11.19.44.png (428.8 kB)

bash-3.2$ docker run -v ~/.healthplanet-to-fitbit.env:/usr/src/app/.env --rm -it tattsun/healthplanet-to-fitbit healthplanet-gettoken
Authorize URL: https://www.healthplanet.jp/oauth/auth?client_id=xxxxx.xxxxxxxx.apps.healthplanet.jp&redirect_uri=https%3A%2F%2Fwww.healthplanet.jp%2Fsuccess.html&response_type=code&scope=innerscan

Input code: RDPGHXefIAxxxxxxxx  <-Webで入手したコードを入力する

AccessToken: xxxxxxxxxxxxx  <-こちらが表示される

こちらの Health Planet のアクセストークンも .env ファイルへ入力しておきましょう。

Fitbitのアクセストークンとリフレッシュトークンの払い出し

https://www.fitbit.com/global/jp/account へアクセスしログインします。がシンプルなアカウント情報とサブスクリプションの状況が分かるだけでAPIを払い出すような箇所は見つかりませんでした。

改めて上述したブログを見たところ通常のページとは異なる開発者用のURL https://dev.fitbit.com/login の存在を知ります。これはちょっとトラップですね。

入力項目もHealth Planetより多く、以下のブログを参考に入力しました。 https://qiita.com/makopo/items/32f41128c2e055cec68f

ただしこちらのブログの Callback URL については http://localhost:8080/callback のようにする必要がありましたので注記しておきます。

例の http://localhost/ とした場合は fitbit-gettoken を実行してページ遷移した先で以下のエラーが発生してしまいます。

The app you're trying to connect did not provide valid information to Fitbit. Please report this issue to them. Developer information: invalid_request - Invalid redirect_uri parameter value

image.png (41.8 kB)

エラーを先に説明しましたが Fitbit のアクセストークンとリフレッシュトークンの取得には以下のように実行します。((dockerではなくGoをビルドして利用しても良い)

$ docker run -v ~/.healthplanet-to-fitbit.env:/usr/src/app/.env -p 8080:8080 --rm -it tattsun/healthplanet-to-fitbit fitbit-gettoken

こうしてページ遷移した先で、許可をすることで2つのトークンを入手できます。 こちらの Fitbit のアクセストークンも .env へ入力しておきましょう。

healthplanet-to-fitbit で同期を実行!!

上記の作業で .env の全ての変数が埋まりました。ドキドキを抑えつつ以下のコマンドを実行して同期を開始します。

$ docker run -v ~/.healthplanet-to-fitbit.env:/usr/src/app/.env -p 8080:8080 --rm -it tattsun/healthplanet-to-fitbit healthplanet-to-fitbit

うまく動作すれば以下のようにレコードの取得が実行されます。*6 すでにレコードが存在している場合は record is found と表示するようでわかりやすいですね。

bash-3.2$ docker run -v ~/.healthplanet-to-fitbit.env:/usr/src/app/.env -p 8080:8080 --rm -it tattsun/healthplanet-to-fitbit healthplanet-to-fitbit
2023/12/30 14:10:49 2023-12-08 12:32:00 +0000 UTC: saved, weight: 73.05, fat: 20.40
2023/12/30 14:10:50 2023-12-05 00:06:00 +0000 UTC: saved, weight: 72.75, fat: 20.30
2023/12/30 14:10:50 2023-12-04 00:03:00 +0000 UTC: saved, weight: 72.40, fat: 20.10
2023/12/30 14:10:51 2023-10-25 22:14:00 +0000 UTC: record is found
2023/12/30 14:10:51 2023-10-23 22:35:00 +0000 UTC: record is found
2023/12/30 14:10:51 2023-10-20 23:51:00 +0000 UTC: record is found
2023/12/30 14:10:52 2023-12-20 23:27:00 +0000 UTC: saved, weight: 72.20, fat: 22.40
2023/12/30 14:10:53 2023-12-11 23:47:00 +0000 UTC: saved, weight: 73.85, fat: 20.60
2023/12/30 14:10:53 2023-11-26 23:49:00 +0000 UTC: saved, weight: 72.20, fat: 20.90
2023/12/30 14:10:54 2023-11-19 23:49:00 +0000 UTC: record is found
2023/12/30 14:10:54 2023-10-24 22:23:00 +0000 UTC: record is found
2023/12/30 14:10:54 2023-11-01 22:39:00 +0000 UTC: record is found
2023/12/30 14:10:54 2023-10-14 23:48:00 +0000 UTC: record is found
2023/12/30 14:10:55 2023-12-10 00:42:00 +0000 UTC: saved, weight: 72.00, fat: 20.50
...

なお、アクセストークンには使用期限があるため、もたもたしていると以下のエラーが発生します。その場合は取得し直すと改善するはずです。

2023/12/29 15:05:20 failed to get weight log from fitbit: failed to get weight log in fitbit(invalid status code): 429 healthplanet-to-fitbit.(*FitbitAPI).GetBodyWeightLog /usr/src/app/fitbit.go:105 main.main /usr/src/app/cmd/healthplanet-to-fitbit/main.go:41 runtime.main /usr/local/go/src/runtime/proc.go:250 runtime.goexit /usr/local/go/src/runtime/asm_arm64.s:1165

成功を確認するためAndroidのアプリを開いたところ、ちゃんと3ヶ月分の体重が表示されるようになっていました。感動!!

Screenshot_20230812-001642.png (225.9 kB)

自動化はまた今度に

こうして無事にローカルで Health Planet から Fitbit へデータを同期することができました。本来はこれをDailyで実行したいところですが、ここまでで少なくない時間を使ったため、自動化はまた今度の機会にするとします。*7

成果としては過去3ヶ月分の体重データがFitbitに同期できたことで、Fitbit側でより統合的に自分の状態を確認することができるようになり大満足です。

改めて素晴らしいツールの開発とそれを公開してくれた tattsun さんに感謝して記事を終わります。tattsun さんサンキュー&Good job!!

*1:最高に感謝

*2:好きなので宣伝っぽくなってしまった

*3:普段は英語検索をしますが、今回のTanitaは日本製品のためJapanで検索しています

*4:日頃開発はしてないのでどっちも素人レベルですが

*5:OSの言語設定をEnglishにしているため、英語で表示されています

*6:weight, fatはマスクしました

*7:やらずに定期的な手作業で満足してしまう未来がちょっと見えますが...