PythonからFitbit APIを使ってデータを取得する(OAuth2)

昨年2016年にFitbitデビューしました。APIを使って遊んでみたかったのですができていませんでした。 以前この記事でもFitbit APIを使ってみたい的なことを言ったのでこの度やってみました。

この手のやってみた系は日本や海外ブログでいくつか見つけたのですが、最初のトークン取得を別の方法でできたのと、OAuth2トークンを更新してから保存する部分について説明しているところが見当たらず試行錯誤したので共有します。

アプリの登録

ここは他の方が書いていることと大して変わらないためさらっといきます。

  1. https://dev.fitbit.com/appsにアクセス

  2. 「Register new app」タブを選択 f:id:ykarakita:20170319194958p:plain

  3. 設定内容はこんな感じにしました。

    • Application Name * : my3rdFBapp
    • Description * : my3rdFBapp
    • Application Website * : このブログのURL
    • Organization * : none
    • Organization Website * : このブログのURL
    • OAuth 2.0 Application Type * : Personal
    • Callback URL * : http://127.0.0.1:8080/
    • Default Access Type * : Read & Write
  4. agreeしてRegister

登録が完了するとトークンを発行するための情報が表示される。OAuth 2.0 Client IDClient Secretをメモしておく。

トークンの発行

続いて先ほどの作成したアプリのページ下部にある「OAuth 2.0 tutorial page」を選択します。(https://dev.fitbit.com/apps/oauthinteractivetutorial
ここからの手順が結構重要&複雑です。

  1. Authorize

    • Flow type : Authorization Code Flow
    • Fitbit URL : www.fitbit.com(デフォルト)
    • OAuth 2.0 Client ID : メモしたOAuth 2.0 Client ID
    • Client Secret : メモしたClient Secret
    • Redirect URI : http://127.0.0.1:8080/

    Select ScopesExpires In(ms)はデフォルトでOK。(Expires In(ms)は値を変えても反映されなかったのでよくわからないまま)

  2. これらをすべて入力すると、その下に長いURLが生成されている。それをクリックする。 こんな画面が表示されるので許可を選択。 f:id:ykarakita:20170319201317p:plain

    そうするとアクセスできませんの画面が表示されるけど、これでOK。 f:id:ykarakita:20170319201356p:plain

  3. ブラウザのURLのバーをみると、 http://127.0.0.1:8080/?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#_=_ 的な感じにリダイレクトされていると思う。 これのcode=の後から#の手前までのコードをコピーする。

  4. コピーしたコードを先ほどのチュートリアルページのCode:の後の入力ボックスにペーストする。 そうするとcurlコマンドが生成されて表示されるのでそれをコピーしてターミナルで実行する。

  5. 成功するとこんな感じのトークンとか他の情報が発行されるのでメモしておく。

{"access_token":"xxxx","expires_in":28800,"refresh_token":"xxxx",
"scope":"settings location sleep social heartrate activity nutrition weight profile",
"token_type":"Bearer","user_id":"XXXXXX"}

エラーが出たらここまでの手順でなにかミスっているのでやり直す。

ここでようやっとPython

以下の環境でやりました。

fitbitモジュールを使います。

$ pip install fitbit
$ vi token.txt

さっきの項目5でコピーしたトークンをまるごと貼り付ける
$ vi app.py

下のコードを貼る。CLIENT_IDCLIENT_SECRETを入れてください。

# -*- coding: utf-8 -*-

import fitbit
import datetime
from ast import literal_eval

CLIENT_ID     = "XXXXXX" # 自分のやつを入れる。
CLIENT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 自分のやつを入れる。
TOKEN_FILE    = "token.txt"

tokens = open(TOKEN_FILE).read()
token_dict = literal_eval(tokens)
access_token = token_dict['access_token']
refresh_token = token_dict['refresh_token']

def updateToken(token):
    f = open(TOKEN_FILE, 'w')
    f.write(str(token))
    f.close()
    return

def pound_to_kg(pound):
    kg = pound * 0.454
    return kg

client = fitbit.Fitbit(CLIENT_ID, CLIENT_SECRET,
    access_token = access_token, refresh_token = refresh_token, refresh_cb = updateToken)

#TODAY = datetime.date.today()
TODAY = "2017-02-02"
bodyweight = client.get_bodyweight(base_date=TODAY)
weight = bodyweight["weight"][0]["weight"]
print(pound_to_kg(weight), "kg")

実行する

$ python app.py
xx.xx kg

ポイント

コードの下手さは勘弁してください。ポイントになるのは、fitbit.Fitbit()を呼び出すときのrefresh_cb引数です。

Fitbit APIが採用しているOAuth2は、一定時間を超えるとアクセストークンが失効してしまいます(デフォルトでは8時間)。リフレッシュトークンを使えばアクセストークンを更新することができます。その際にリフレッシュトークンも更新されます。 python-fitbitモジュールではAPI実行時にトークンが失効していた場合はリフレッシュするところまでやってくれます。 しかし保存するところまではやってくれません。先程token.txtに書いたトークンを随時更新していく必要があるのでそこは自分で実装する必要があります。

そこで、refresh_cbに関数を渡しておくと、もしfitbit.Fitbit()を呼び出したときにトークンが失効していた場合は自動的にリフレッシュした後、指定した関数に新しいトークンを渡してくれます。 なのでupdateToken()では新しいトークンを受け取ってファイルに上書きするということをしています。

ちなみにトークン更新後にファイルに上書きするのに失敗した場合、再度トークンを使用するためにはトークン新規発行以外に方法はないので注意しましょう。

このコードではupdateToken()の中でファイルに上書きをしていますが、DBに入れるとかするともっといいと思います。

clientを認証した後は好きにやってあげてください。データの参照も更新もできるはずです。今回コード中でweightを参照していて、Fitbitなのに体重?と思うかもしれませんが、私の場合、体重計はWithingsを使っていてIFTTTを使ってFitbitにも反映されるようにしているので体重が見れます。

ハマったところ

  • CLIENT_IDとかCLIENT_SECRETとかcodeとかaccess_tokenとかrefresh_tokenとかもうよくわからない。
  • OAuth2が全然わからない。
    • アクセストークンとかの発行の仕方がよくわからなかった。
    • access_tokenとrefresh_tokenが発行できたあと、refresh_tokenの使い方がわからなかった。
    • refresh_tokenの使い方はわかったけど、リフレッシュした後の情報をどうやって保存すればいいのかわからなかった。
      • めっちゃ調べた&ソースコードを読んだ。つらかった。時間かかった。
  • トークンが失効するまでに8時間かかるので失効→リフレッシュ→トークンファイル更新を頻繁に試せない。
    • 最後までいいテスト方法がわからず。(だれか教えてください…)

まとめ

これで継続的にデータがとれるようになりました。 今回しんどいこと多かったですがPythonとOAuth2と英語のいい勉強になりました。
やっとAPIを使う準備ができたので今度はFitbit APIを使って何か作ってみようと思います。