FitbitのAPIを手っ取り早く試してみる方法

[Fitbit](http://www.fitbit.com/)は、腕時計型の活動量計（心拍数計）です。測定データを取得できるみたいだけど、どこから手をつけていいか分からない！という方向けに、まずはプログラムを書かなくても自分のFitbitの登録・測定データを取得できる方法を紹介します。

**2017.3.4追記: Postmanをインストール可能な環境であれば、この記事に書かれている方法よりも、[FitbitのOAuth 2.0 APIをPostmanで試してみる方法](http://qiita.com/makopo/items/7f5a47a7c58929b6fb93)の方が、簡単で、扱いやすいかと思います。**

## 1. OAuth認証情報を作成

https://dev.fitbit.com/ にアクセスします。

![F08E99F7-C0F5-4EC7-A148-ACE6D387EFAF.png](https://qiita-image-store.s3.amazonaws.com/0/10290/b3e5090d-7a55-c5ad-cbee-aa536aaef226.png)

画面右上の```REGISTER AN APP```ボタンをクリックすると、登録フォームが表示されます。

![3.png](https://qiita-image-store.s3.amazonaws.com/0/10290/3a77fd29-2432-1316-601e-362557cd0169.png)

URLを入力する欄がありますが、本記事に基づく作業では、サーバやクラウドを用意する必要はありません。基本的に、何か埋まっていれば良いです。ただし、以下の点に気をつけてください。

* ```Desctiption```は10文字以上
* ```OAuth 2.0 Application Type```には```Client```または```Personal```を選択
* ```Callback URL```には**リダイレクトが発生しない**URLを入力（```http://localhost/```推奨）

> ```Callback URL```は、最後のスラッシュの有無まで含めて正確にメモっておきましょう。

最後にある```Register```ボタンをクリックすると、登録完了画面が表示されます。

![4.png](https://qiita-image-store.s3.amazonaws.com/0/10290/55dbce74-35ed-cfbb-0196-f24abda8e90d.png)

```OAuth 2.0 Client ID```と```Client Secret```が、次のステップで必要となります。

## 2. 認証リクエストURIの生成

OAuth 2.0 tutorial page にアクセスします。
https://dev.fitbit.com/apps/oauthinteractivetutorial

![5.png](https://qiita-image-store.s3.amazonaws.com/0/10290/7f5286be-0b55-f2a3-e610-d2d4341da205.png)

* ```Flow type:```に、```Implicit Grant Flow```を選択（デフォルト）
* ```OAuth 2.0 Client ID:```と```Client Secret:```に、登録完了画面に表示された値を入力
* ```Redirect URI:```に、```Callback URL```で指定したURLを**一字一句違えずに**入力

入力し終わったら、入力欄の下にある長いリンクをクリックします。

## 3. 自分のアカウントと対応付け

別ウィンドウが開き、アクセス許可を求められます。

![6.png](https://qiita-image-store.s3.amazonaws.com/0/10290/bf7463a5-e175-2930-5e37-af32840d0e47.png)

> この画面でなく、「接続しようとしているアプリは Fitbit に有効なデータを提供しませんでした。この問題を報告してください。」と表示される場合は、```Redirect URI:```で指定したURLが正しいか、余分な空白が含まれていないかを確認してみましょう。

「許可」をクリックすると、存在しないURLであれば、エラーページが表示されます。

![7.png](https://qiita-image-store.s3.amazonaws.com/0/10290/5f6d679e-9cf2-dc55-de0c-97ac9810d557.png)

**まだ閉じないでください！**アドレスバーに表示されているURLをどこかに控えておきます。

> 先ほど「リダイレクトが発生しないURL」と説明したのは、ここでリダイレクトしてしまうとアドレスが変わってしまい、次のステップで利用することができなくなってしまうためでした。

## 4. 認証URLを解析

OAuth 2.0 tutorial page に戻ります。

![8.png](https://qiita-image-store.s3.amazonaws.com/0/10290/b3c75adc-9368-201b-69af-af23f051b985.png)

アドレスバーに表示されていたURLを、```2 Parse response```のテキストフィールドに貼り付けます。説明文には```#scope```から後の部分を貼り付ける旨書かれていますが、この例では```#access_token```で始まるため、正しくありません。現在は、URL全体を貼り付けても問題なく解析されます。```scopes```、```user id```、```time to live```、```token```が表示されていれば、解析に成功しています。これらのデータが今後必要になることはありません。

> 解析に失敗すると赤い文字でエラーメッセージが表示されます。URLの形式が上記の例と異なっている場合は、リダイレクトしてしまっている可能性が高いですので、```Callback URL```に別のURLを指定して最初からやり直してみましょう。

## 5. クエリの実行

下に進むと、curlのコマンドが表示されています。

![9.png](https://qiita-image-store.s3.amazonaws.com/0/10290/fa092989-7d3c-c6c9-9c78-8253dfdbf85c.png)

curlがインストールされていれば、これをプロンプトやターミナルにコピペして実行できます。

#### Hurl.itを使ったクエリ実行

コマンド表示欄の下の小さい```Send to Hurl.it```リンクをクリックすると、ブラウザ上でRESTクエリを実行できます。

![FC929E22-6740-4D7F-8F7E-2D3009647605.png](https://qiita-image-store.s3.amazonaws.com/0/10290/80ab7d1b-47ef-55ef-0d20-5f4da7fca6fb.png)

```私はロボットではありません```にチェックを入れて、クイズを解き、```Launch Request```をクリックします。

![fa17ca0a-2b9f-6668-f08f-caffe12d85fd.png](https://qiita-image-store.s3.amazonaws.com/0/10290/ff434083-951a-25ae-ee79-a1d906bae14f.png)

整形されたJSONで結果が返ってきます。画面右端の```view raw```をクリックすると、生データが表示されます。

## 6. APIの探検

API Document
https://dev.fitbit.com/docs/

**Resource URLs** に書かれている書式を参考に、```API endpoint URL: ```を書き換え、クエリを再実行します。**Example Request** をそのままコピペして実行してみて、少しずつ変更していくのも良いでしょう。

#### 例１：直近1週間の心拍数を[ゾーン別](https://help.fitbit.com/articles/ja/Help_article/1565#zones)に取得

```
https://api.fitbit.com/1/user/-/activities/heart/date/today/7d.json
```
> 参考：Get Heart Rate Time Seriesの仕様
> https://dev.fitbit.com/docs/heart-rate/#get-heart-rate-time-series

このような結果が取得できます。(11月26日に取得)

```json
{
  "activities-heart": [
    {
      "dateTime": "2016-11-20",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "caloriesOut": 154.56368,
            "max": 93,
            "min": 30,
            "minutes": 88,
            "name": "Out of Range"
          },
          {
            "caloriesOut": 322.1564,
            "max": 130,
            "min": 93,
            "minutes": 92,
            "name": "Fat Burn"
          },
          {
            "caloriesOut": 151.52952,
            "max": 158,
            "min": 130,
            "minutes": 27,
            "name": "Cardio"
          },
          {
            "caloriesOut": 16.33092,
            "max": 220,
            "min": 158,
            "minutes": 2,
            "name": "Peak"
          }
        ],
        "restingHeartRate": 72
      }
    },
    {
      "dateTime": "2016-11-21",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "max": 93,
            "min": 30,
            "name": "Out of Range"
          },
          {
            "max": 130,
            "min": 93,
            "name": "Fat Burn"
          },
          {
            "max": 158,
            "min": 130,
            "name": "Cardio"
          },
          {
            "max": 220,
            "min": 158,
            "name": "Peak"
          }
        ]
      }
    },
    {
      "dateTime": "2016-11-22",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "max": 93,
            "min": 30,
            "name": "Out of Range"
          },
          {
            "max": 130,
            "min": 93,
            "name": "Fat Burn"
          },
          {
            "max": 158,
            "min": 130,
            "name": "Cardio"
          },
          {
            "max": 220,
            "min": 158,
            "name": "Peak"
          }
        ]
      }
    },
    {
      "dateTime": "2016-11-23",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "caloriesOut": 13.74296,
            "max": 93,
            "min": 30,
            "minutes": 10,
            "name": "Out of Range"
          },
          {
            "caloriesOut": 146.3536,
            "max": 130,
            "min": 93,
            "minutes": 43,
            "name": "Fat Burn"
          },
          {
            "caloriesOut": 68.62556,
            "max": 158,
            "min": 130,
            "minutes": 11,
            "name": "Cardio"
          },
          {
            "caloriesOut": 73.89072,
            "max": 220,
            "min": 158,
            "minutes": 8,
            "name": "Peak"
          }
        ]
      }
    },
    {
      "dateTime": "2016-11-24",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "max": 93,
            "min": 30,
            "name": "Out of Range"
          },
          {
            "max": 130,
            "min": 93,
            "name": "Fat Burn"
          },
          {
            "max": 158,
            "min": 130,
            "name": "Cardio"
          },
          {
            "max": 220,
            "min": 158,
            "name": "Peak"
          }
        ]
      }
    },
    {
      "dateTime": "2016-11-25",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "max": 93,
            "min": 30,
            "name": "Out of Range"
          },
          {
            "max": 130,
            "min": 93,
            "name": "Fat Burn"
          },
          {
            "max": 158,
            "min": 130,
            "name": "Cardio"
          },
          {
            "max": 220,
            "min": 158,
            "name": "Peak"
          }
        ]
      }
    },
    {
      "dateTime": "2016-11-26",
      "value": {
        "customHeartRateZones": [],
        "heartRateZones": [
          {
            "max": 93,
            "min": 30,
            "name": "Out of Range"
          },
          {
            "max": 130,
            "min": 93,
            "name": "Fat Burn"
          },
          {
            "max": 158,
            "min": 130,
            "name": "Cardio"
          },
          {
            "max": 220,
            "min": 158,
            "name": "Peak"
          }
        ]
      }
    }
  ]
}
```

上記の場合は、11月20日にピーク(158以上)が2分、有酸素運動(130以上158未満)が27分、脂肪燃焼(93以上130未満)が92分、ゾーン圏外が88分あったことになります。Fitbitを装着している日だけ、minutes属性が含まれます。

#### 例２：2016年11月20日16時15分の心拍数を取得

手順１にて、```OAuth 2.0 Application Type```で```Personal```を指定して登録してある必要があります。

```
https://api.fitbit.com/1/user/-/activities/heart/date/2016-11-20/1d/1sec/time/16:15/16:16.json
```

> 参考：Get Heart Rate Intraday Time Seriesの仕様
> https://dev.fitbit.com/docs/heart-rate/#get-heart-rate-intraday-time-series

このような結果が取得できます。

```json
{
  "activities-heart": [
    {
      "customHeartRateZones": [],
      "dateTime": "2016-11-20",
      "heartRateZones": [
        {
          "caloriesOut": 0,
          "max": 93,
          "min": 30,
          "minutes": 0,
          "name": "Out of Range"
        },
        {
          "caloriesOut": 5.62212,
          "max": 130,
          "min": 93,
          "minutes": 1,
          "name": "Fat Burn"
        },
        {
          "caloriesOut": 7.49616,
          "max": 158,
          "min": 130,
          "minutes": 1,
          "name": "Cardio"
        },
        {
          "caloriesOut": 0,
          "max": 220,
          "min": 158,
          "minutes": 0,
          "name": "Peak"
        }
      ],
      "value": "139"
    }
  ],
  "activities-heart-intraday": {
    "dataset": [
      {
        "time": "16:15:00",
        "value": 154
      },
      {
        "time": "16:15:05",
        "value": 149
      },
      {
        "time": "16:15:10",
        "value": 145
      },
      {
        "time": "16:15:15",
        "value": 143
      },
      {
        "time": "16:15:20",
        "value": 136
      },
      {
        "time": "16:15:25",
        "value": 133
      },
      {
        "time": "16:15:30",
        "value": 132
      },
      {
        "time": "16:15:40",
        "value": 130
      },
      {
        "time": "16:15:45",
        "value": 129
      }
    ],
    "datasetInterval": 1,
    "datasetType": "second"
  }
}
```



## 7. 実際のアプリ作成

自分の意図するデータが取得できそうであれば、アプリ制作に取り掛かります。Fitbit APIの短いサンプルやチュートリアルをいくつか紹介します。

Fitbit APIをPHPでOAuth認証してWebサイトに心拍数などを出力
https://www.sho-yamane.me/entry/2016/07/16/163712

Fitbit Charge HRの心拍数データを取得する(Python)
http://shima-nigoro.hatenablog.jp/entry/2016/05/29/181309

Fitbit APIにOAuth 2.0でアクセスして心拍データを取得する方法(Ruby)
http://kaorumori.hatenadiary.com/entry/2015/09/23/011006

Using OAuth 2.0 - Accessing the Fitbit API
https://dev.fitbit.com/docs/oauth2/

API開発者によるPoCサンプル(Node.js)
https://github.com/jeremiahlee/fitbit-hapi-demo

psFitb1t: The original Powershell module to retrieve YOUR heartrate data
https://github.com/CollinChaffin/psFitb1t

ここまで来たら、もうOAuth 2.0プログラミングの世界ですので、普通のOAuthの記事を参考に作っていけばいいと思います。

ちなみに、[Passport](http://passportjs.org) の Strategy があります。

Passport strategy for Fitbit OAuth 2.0
https://github.com/thegameofcode/passport-fitbit-oauth2