ようへいの日々精進XP

よかろうもん

冬休みの自由研究 (3) 〜 俺は一日どのくらい走って (歩いて) いるのかを Fitbit Web API と pixela を使って可視化する検証 CircleCI を添えて 〜

追記

CircleCI で定期的に... (01/04)

CircleCI の Schedule Job を利用して定期的に処理させようと思ったいたんだけど, Access Token の期限切れ対策で, 一日に一回よりも短い周期でスクリプトを動かす必要がありそう.

circleci.com

となると, CircleCI の Schedule Job では, crontab によく書く以下のような書き方をする必要があるんだけど...

# 5 分おきに my-script.rb を実行する
*/5 * * * * ruby my-script.rb

上記のドキュメントに,

Note: Cron step syntax (for example, /1, /20) is not supported. Range elements within comma-separated lists of elements are also not supported.

と書かれていて, 本案件では, 残念ながら CircleCI は利用出来なさそう.

Access Token の期限切れ対策 (01/03)

Access Token が expire する可能性があるので, Refresh Token を利用して Access Token を再取得しないといけないかもしれない.... 「かもしれない」じゃなくて, Refresh Token を利用して, Access Token の再取得が必須でした.

Fitbit Charge2 はスマートウォッチではなくトラッカー (01/03)

Fitbit Charge2 はスマートウォッチではなく, トラッカーという位置付けのようです.

f:id:inokara:20200103195836p:plain

tl;dr

箱根駅伝で母校を応援しながら, 自分は故障で走れない悔しさをどこかにぶつけたくて Fitbit Web API を pixela を使って可視化 (草を生やす) してみたいと思います.

www.hakone-ekiden.jp

モチベーション

自分は, 常日頃から Fitbit Charge2 を単純に腕時計, 走る時にストップウォッチと参考程度に心拍数を確認する目的で利用しています.

既に Fitbit Charge3 なる製品もリリースされているようですが, 今のところ Charge2 で間に合っているので買い換える予定はありません. 歩数や距離はイマイチ信頼性は低い印象ですが, バンドを変えられたり出来るので長く使えるスマートウォッチトラッカーなんじゃないかと思っています.

この Fitbit Charge2 (のみならず, 各種 Fitbit 製品) で収集した各種活動量等は REST API を介して取得することが出来ます. せっかくなので, この活動量 (この記事では走行 or 歩行距離のみを扱います) を年間を通して可視化してみたいと思います.

Pixela とは

過去に Fitbit Web API を利用して心拍数を Mackerel を使って可視化する記事を書きました.

inokara.hateblo.jp

似たようなことを, 今度は pixela という草を生やすサービス (ずいぶん乱暴な説明ですいません) を使って可視化してみたいと思います. 草を生やすというのは, Github のプロフィールページに掲載されているグラフです.

f:id:inokara:20200103123055p:plain

pixela は REST API を利用して, このグラフを生成するようなサービスです. 以下のように REST API で全ての操作が可能です.

# ユーザー登録
curl -X POST https://pixe.la/v1/users -d '{"token":"my-token-please-self-generate", "username":"username", "agreeTermsOfService":"yes", "notMinor":"yes"}'

# グラフ作成
curl -X POST https://pixe.la/v1/users/username/graphs -H 'X-USER-TOKEN:my-token-please-self-generate' -d '{"id":"my-graph","name":"my-graph","unit":"km","type":"int","color":"shibafu"}'

# グラフの設定 Timezone を Asia/Tokyo に設定
curl -X PUT https://pixe.la/v1/users/username/graphs/my-graph -H 'X-USER-TOKEN:my-token-please-self-generate' -d '{"timezone":"Asia/Tokyo"}'

# グラフの設定 selfSufficient を increment に設定
curl -X PUT https://pixe.la/v1/users/username/graphs/my-graph -H 'X-USER-TOKEN:my-token-please-self-generate' -d '{"selfSufficient":"increment"}'

# データの登録
curl -X POST https://pixe.la/v1/users/username/graphs/my-graph -H 'X-USER-TOKEN:my-token-please-self-generate' -d '{"date":"20200102","quantity":"5"}'

# データの削除
curl -X DELETE https://pixe.la/v1/users/username/graphs/my-graph/20200102 -H 'X-USER-TOKEN:my-token-please-self-generate'

# 登録データの取得
curl -X GET https://pixe.la/v1/users/username/graphs/my-graph/stats -H 'X-USER-TOKEN:my-token-please-self-generate  

ドキュメントが充実しているので, シュッと使い始めることが出来ます.

docs.pixe.la

Fitbit の Web API

dev.fitbit.com

OAuth で認証する部分がややこしいですが, アクセストークンを取得してしまえば, curl なり, 各種言語の HTTP ライブラリを利用して活動量を JSON で取得出来るようになります.

今回は, 以下の記事を参考にさせて頂いて, OAuth 認証を設定し, Ruby スクリプトも参考にさせていただきました. 有難うございます.

qiita.com

作ったもの

リポジトリ

実装したスクリプトは以下のリポジトリにアップしています.

github.com

活動量の取得

Fitbit Web API から, 活動量を取得する部分は以下の通りです.

class FitBitActivity
  def initialize(date)
    @base_url = 'https://api.fitbit.com/1/user/-'
    @request_header = { 'Authorization' => "Bearer #{ENV['FITBIT_ACCESS_TOKEN']}" }
    @date = date
  end

  def method_missing(resource)
    send("fetch_resource", resource, @date)
  end

  private

  def fetch_error(e)
    puts 'Error: ' + e['errors'][0]['errorType']
    exit 1
  end

  def fetch_resource(resource, date)
    activity_url = "#{@base_url}/activities/#{resource}/date/#{date}/1d.json"
    res = fetch(activity_url)
    res["activities-#{resource}"][0]['value'] if res.has_key?("activities-#{resource}")
    fetch_error(res) if res.has_key?('success')
  end
  
  def fetch(url)
    uri = URI.parse(url)
    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    req = Net::HTTP::Get.new(uri.request_uri, @request_header)
    begin
      res = https.request(req)
      JSON.parse(res.read_body)
    rescue => ex
      puts 'Error: ' + ex
      exit 1
    end
  end
end

dev.fitbit.com

Activity から以下のような歩数, 距離数, カロリー毎に値を取得することを想定しています.

activities/calories
activities/steps
activities/distance
activities/floors
activities/elevation

例えば, 前日の消費カロリー数を取得したい場合には, 以下のように書きます.

d = Date.today - 1
puts FitBitActivity.new(d.strftime("%Y-%m-%d")).calories

実行すると, 以下のように出力されます.

$ ruby my-activity.rb
1608

単位はキロカロリーとなります.

同様に歩数を取得したい場合には, 以下のように書きます.

d = Date.today - 1
puts FitBitActivity.new(d.strftime("%Y-%m-%d")).steps

実行すると, 以下のように出力されます.

$ ruby my-activity.rb
4230

単位は歩数となりますな. 冬休みと故障中ということで歩数が全然伸びていないのが切ないですね...

草を生やしてみると...

サンプルのスクリプトを使って実際に草を生やしてみたのが以下の通りです.

https://pixe.la/v1/users/inokappa/graphs/my-test-graph1

パラメータに mode=short を付けると以下のようになります.

https://pixe.la/v1/users/inokappa/graphs/my-test-graph1?mode=short

おおー, いい感じです.

そして, CircleCI

毎日一回, このスクリプトを叩かせたい. となると思いつくのは Lambda + CloudWatch Events の組み合わせなんですが, とりあえず CircleCI で転がしてみようと思います.

version: 2.1

executors:
  default:
    docker:
      - image: circleci/ruby:2.6.5

jobs:
  check:
    executor: 
      name: default
    steps:
      - checkout
      - run:
          name: Check Activity
          command: |
            ruby my-activity.rb

workflows:
  check:
    jobs:
      - check:
          filters:
            branches:
              only:
                - master
  scheduled-check:
    triggers:
      - schedule:
          cron: "57 15 * * *"
          filters:
            branches:
              only:
                - master
    jobs:
      - check:
          filters:
            branches:
              only:
                - master

とてもシンプルな設定だと思います. ちゃんとした運用を考慮するならば, Slack 通知の orbs なんかを追加していい感じで自動化出来るのではと考えていますが, まだやっていません.

以上

Fitbit Web API と pixela を使って走行 (歩行) 距離を可視化を検討してみました. 毎日書いている日記に貼り付けてドヤってみようかなと考えています. やっぱり可視化は楽しいですね.