歩数で草を生やしてみたのだー。これで散歩の楽しみが増えたなー
やってること
やってることはシンプルで、こう↓
Fitbit?
最近 Fitbit Sense というスマートウォッチをつけて散歩してる
会社の福利厚生で健康のための補助があるので、それを利用して手に入れたのだ。散歩やジョギングをしたら勝手に記録されてるので便利。睡眠のログも取られてるので面白い
Pixela?
Pixela は日々の活動を記録して見えるようにしてくれる API サービス。いちばん最初に貼った画像みたいに GitHub の草を生やすやつみたいにしてくれる。操作が全部 API 経由なのも楽しい
せっかく散歩してるから
せっかく散歩してるから Fitbit の歩数データを Pixela で見えるようにしたら面白いかなぁって思ったのだった。何か参考にできる記事あるかなぁ?って思ってたら、ようへいさんのブログに参考というかまさにそれという記事がありました
そして気づく。そうか、こういうのに CircleCI 使えばいいのか! CircleCI で仕事してるのに全然頭になかったー(おーい
じゃ、やってみよう
1. Fitbit から API で歩数を取得
やるのはこういうこと
んだけど
という感じ。ちょっとややこしいけど、健康状態の記録を扱うんだからちゃんとしてるのは良いね
Fitbit にアプリを登録してトークンを取得
API トークンみたいなのあるのかなぁ?と思ったけど、ないみたい?で、アプリを登録する必要がある。アプリといっても、自分で API を叩くためのトークンを取得するためだけなので URL は localhost にしておいたら大丈夫。この記事がとても丁寧で分かりやすかった↓
これで、アクセストークンとリフレッシュトークンが取得できた。それと Authorization: Basic
のヘッダーのところのトークンも API を叩くときに使うのでメモしておく(これは Client ID とかから自分で生成できるんだけど)
Fitbit の API をたたいてみる
この API を使う
こんな感じで過去7日分の歩数を取得することができる:
❯ FITBIT_ACCESS_TOKEN="さっき取得したアクセストークン" ❯ curl -sX GET "https://api.fitbit.com/1/user/-/activities/steps/date/today/7d.json" \ -H "accept: application/json" \ -H "authorization: Bearer $FITBIT_ACCESS_TOKEN" \ | jq { "activities-steps": [ { "dateTime": "2022-02-21", "value": "7204" }, { "dateTime": "2022-02-22", "value": "7485" }, { "dateTime": "2022-02-23", "value": "1014" }, { "dateTime": "2022-02-24", "value": "1420" }, { "dateTime": "2022-02-25", "value": "3615" }, { "dateTime": "2022-02-26", "value": "2599" }, { "dateTime": "2022-02-27", "value": "366" } ] }
さっき取得したアクセストークンの有効期限は8時間なことに注意。なので、ずっとデータを取得し続けるためには、リフレッシュトークンを使って定期的にトークンを再生成する必要がある。やってみよう
リフレッシュトークンでトークンを再生成する
使うのは、この API
❯ FITBIT_BASIC_TOKEN="さっき取得した Authorization: Basic のところのトークン" ❯ FITBIT_REFRESH_TOKEN="さっき取得したリフレッシュトークン" ❯ curl -X POST "https://api.fitbit.com/oauth2/token" \ -H "accept: application/json" \ -H "authorization: Basic $FITBIT_BASIC_TOKEN" \ -d "grant_type=refresh_token&refresh_token=$FITBIT_REFRESH_TOKEN" | jq { "access_token": "新しいアクセストークン", "expires_in": 28800, "refresh_token": "新しいリフレッシュトークン", ... }
という感じで、新しいアクセストークンとリフレッシュトークンを取得できる。再生成すると古い方のアクセストークンとリフレッシュトークンは使えなくなることに注意
こんな風にしてリフレッシュトークンを再生成し続ければ、ずっとアクセストークンを取得できそうなので、これで Fitbit のデータ取得は OK かな
2. Pixela に記録
じゃ、取得した歩数データを Pixela に登録しよう。Pixela は日本語ドキュメントがあるので、その通りにやってくだけ
グラフはこんな感じで作った
❯ PIXELA_TOKEN="Pixela に登録したトークン" ❯ curl -X POST https://pixe.la/v1/users/bufferings/graphs \ -H "X-USER-TOKEN:$PIXELA_TOKEN" \ -d '{"id":"steps","name":"steps","unit":"steps","type":"int","color":"ajisai","timezone":"Asia/Tokyo"}' {"message":"Success.","isSuccess":true}
記録もシンプルで、日付をパスパラメータで指定して値を PUT で渡してあげればいい
❯ curl -X PUT https://pixe.la/v1/users/bufferings/graphs/steps/20220221 \ -H 'x-user-token: '$PIXELA_TOKEN \ -d '{"quantity":"7204"}' {"message":"Success.","isSuccess":true}
これで Pixela に歩数を記録することができるようになったーわーい
3. というスクリプトを作成
TypeScript で書いた
https://github.com/bufferings/fitbit-to-pixela/blob/main/src/main.ts
const main = async () => { const [accessToken, newRefreshToken] = await fetchNewTokensFromFitbit(); await saveNewRefreshToken(newRefreshToken); const stepsList = await fetchStepsFromFitbit(accessToken); for (const steps of stepsList) { await putToPixela(steps); } };
やってることはこれだけ:
- リフレッシュトークンを使って新しいアクセストークンとリフレッシュトークンを取得
- 新しいリフレッシュトークンを環境変数に保存
- 新しいアクセストークンを使って過去7日分の歩数を Fitbit から取得
- 7日分の歩数を Pixela に登録
3-1. リフレッシュトークンを使って新しいアクセストークンとリフレッシュトークンを取得
これはさっき手でやってみたやつをコードにしたもの。もしかしたらアクセストークンはまだ有効期限内かもしれないんだけど、1日に数回しか実行しないと思うので、実行するたびに再生成することにした。だから、アクセストークンはどこにも保存してない
3-2. 新しいリフレッシュトークンを環境変数に保存
ローカルでは .env
ファイルを使ってこんな風に環境変数を定義してる(トークンなので .gitignore に追加してあって Git にはコミットしていない。トークンをコミットしないようにね!)
IS_LOCAL=true FITBIT_BASIC_TOKEN="さっき取得した Authorization: Basic のところのトークン" FITBIT_REFRESH_TOKEN="さっき取得したリフレッシュトークン" PIXELA_TOKEN="Pixela のトークン" PIXELA_GRAPH_URL=https://pixe.la/v1/users/(ユーザーID)/graphs/(グラフID)
この FITBIT_REFRESH_TOKEN
を使ってアクセストークンとリフレッシュトークンを再生成すると、元のリフレッシュトークンは使えなくなるので次回実行時のために .env
ファイルの FITBIT_REFRESH_TOKEN
を書き換えてる(ちなみにローカル用と CI 用で別のトークンを使うことができるようにふたつのアプリを Fitbit に登録しといた)
ローカルでは .env
の書き換えをしてるんだけど、CircleCI の場合は API を使ってプロジェクトの環境変数を更新してるので後述する
3-3. 新しいアクセストークンを使って過去7日分の歩数を Fitbit から取得
これもさっきやったやつをコードにしただけ
3-4. 7日分の歩数を Pixela に登録
昨日と今日の分だけ記録したら十分なんだけど、まぁあんまり深く考えずに7日分更新することにした。ちなみに、ローカルでいちど 3m
を指定して3ヶ月分とってきて更新しといた
4. CircleCI で定期的に実行
色々準備できたので、最後にこのスクリプトを CircleCI で定期実行する
4-1. プロジェクトの準備
node の orbs を使って npm run main
を実行する設定ファイルを追加。Orbs が色々面倒みてくれるので楽:
version: 2.1 orbs: node: circleci/node@5.0.0 workflows: fitbit-steps-to-pixela: jobs: - node/run: version: '17.6.0' npm-run: main
4-2. 新しいリフレッシュトークンを環境変数に保存
さっきローカルの .env
を更新したやつの CircleCI バージョン。この API を使う:
User Settings > Personal API Tokens から Pixela 用にトークンを生成した
あとはそのトークンを使って、こんな風にして登録・更新をすることができる
❯ MY_CIRCLE_PROJECT_SLUG="プロジェクトのパス。例: github/bufferings/fitbit-to-pixela" ❯ MY_CIRCLE_API_TOKEN="生成したトークン" ❯ curl --request POST \ --url "https://circleci.com/api/v2/project/${MY_CIRCLE_PROJECT_SLUG}/envvar" \ --header "circle-token: $MY_CIRCLE_API_TOKEN" \ --header 'content-type: application/json' \ --data '{"name":"foo","value":"xxxx12345"}' { "name" : "foo", "value" : "xxxx2345" }
この API を使って、新しいリフレッシュトークンで FITBIT_REFRESH_TOKEN
環境変数を更新してる
4-3. 環境変数の登録
こんな感じ:
- Fitbit 用
FITBIT_BASIC_TOKEN
FITBIT_REFRESH_TOKEN
- Pixela 用
PIXELA_GRAPH_URL
PIXELA_TOKEN
- CircleCI 用
MY_CIRCLE_API_TOKEN
MY_CIRCLE_PROJECT_SLUG
4-4. 定期実行
適当に実行して動いてるなぁってことが確認できたら、あとはパイプラインの定期実行トリガーを設定
Project Settings > Triggers で日本時間の0時、6時、12時、18時に実行するようにしてみた。1日に1回でも良い気はするけどなんとなく
実行されてるみたいでよかった。もうちょっと時間かかるかなぁって思ってたけど 10s くらいか。さくっとしてて良いー
ということで
歩くと草が生えるー!
やってることはシンプルなんだけど、Fitbit のアプリ周りのところを理解するのとか TS のプロジェクト作ったりするのに時間がかかってしまったなぁ。それが楽しかった!
散歩してこー!