俺のアウトプット

調べたこと、試したことを書きます

Fitbit API から睡眠データを取得してみた

Fitbit Charge2 を装着したまま洗顔をしたら、水がかかりお亡くなりになりました。。。

www.fitbit.com

ちょうど、Fitbit Charge3 が出たので購入。 今度は防水なので大丈夫でしょう!!

せっかくなので、睡眠データを取得して何かをしたいと思います。

睡眠レベル

一言に睡眠と言っても、状態によっていくつかレベルがあります。

  • 覚醒状態 (wake)
    • 毎晩10-30回ほど発生
    • 期間が非常に短い
  • レム睡眠 (rem)
    • 浅い眠り、体の眠り
    • 夢を見ることがある
    • 心拍数が上がったり、呼吸が早くなったり
  • ノンレム睡眠
    • 深い眠り、脳の眠り
    • 第1段階〜第4段階に分かれる

眠りの深さは

覚醒状態 -> レム睡眠 -> ノンレム睡眠 第1段階 -> ノンレム睡眠 第4段階

の順に深くなります。

Fitbitでは、ノンレム睡眠を浅い睡眠と深い睡眠の2つに分け、4つの睡眠レベルに分けています。

  1. 目覚めた状態 (wake)
  2. レム睡眠 (rem)
  3. 浅い睡眠 (light)
    • ノンレム睡眠 第1段階
    • ノンレム睡眠 第2段階
  4. 深い睡眠 (deep)
    • ノンレム睡眠 第3段階
    • ノンレム睡眠 第4段階

俺の睡眠データ

基本、早寝早起きです。たまに小学生の子供達より早く寝ます。

手っ取り早く、curlで叩いて睡眠データを取得します。
Fitbit APIをcurlで叩く方法は、下記のエントリを参照してください。

《2018-11-16》

ver1.1以前(classic) と 1.2(stages) ではフォーマットが異なるので注意です。

ver1.1の場合

curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.1/user/-/sleep/date/2018-11-16.json | jq . > 2018-11-16-v1.1.json

2018-11-16-v1.1.json

{
  "sleep": [
    {
      "awakeCount": 0,
      "awakeDuration": 0,
      "awakeningsCount": 17,
      "dateOfSleep": "2018-11-16",
      "duration": 30000000,
      "efficiency": 95,
      "endTime": "2018-11-16T05:39:00.000",
      "isMainSleep": true,
      "logId": 20170274799,
      "minuteData": [
        {
          "dateTime": "21:18:30",
          "value": "1"
        },
        {
          "dateTime": "21:19:30",
          "value": "1"
        },
        略
        {
          "dateTime": "05:36:30",
          "value": "1"
        },
        {
          "dateTime": "05:37:30",
          "value": "1"
        }
      ],
      "minutesAfterWakeup": 0,
      "minutesAsleep": 473,
      "minutesAwake": 27,
      "minutesToFallAsleep": 0,
      "restlessCount": 17,
      "restlessDuration": 27,
      "startTime": "2018-11-15T21:18:30.000",
      "timeInBed": 500
    }
  ],
  "summary": {
    "stages": {
      "deep": 71,
      "light": 215,
      "rem": 85,
      "wake": 41
    },
    "totalMinutesAsleep": 473,
    "totalSleepRecords": 1,
    "totalTimeInBed": 500
  }
}

v1.2の場合

curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-16.json | jq . > 2018-11-16-v1.2.json

2018-11-16-v1.2.json

{
  "sleep": [
    {
      "dateOfSleep": "2018-11-16",
      "duration": 30000000,
      "efficiency": 95,
      "endTime": "2018-11-16T05:39:00.000",
      "infoCode": 0,
      "isMainSleep": true,
      "levels": {
        "data": [
          {
            "dateTime": "2018-11-15T21:18:30.000",
            "level": "wake",
            "seconds": 30
          },
          {
            "dateTime": "2018-11-15T21:19:00.000",
            "level": "light",
            "seconds": 300
          },
          略
          {
            "dateTime": "2018-11-16T05:23:00.000",
            "level": "wake",
            "seconds": 510
          },
          {
            "dateTime": "2018-11-16T05:31:30.000",
            "level": "light",
            "seconds": 450
          }
        ],
        "shortData": [
          {
            "dateTime": "2018-11-15T21:18:30.000",
            "level": "wake",
            "seconds": 150
          },
          {
            "dateTime": "2018-11-15T21:59:00.000",
            "level": "wake",
            "seconds": 60
          },
          略
          {
            "dateTime": "2018-11-16T05:10:30.000",
            "level": "wake",
            "seconds": 30
          },
          {
            "dateTime": "2018-11-16T05:33:30.000",
            "level": "wake",
            "seconds": 30
          }
        ],
        "summary": {
          "deep": {
            "count": 6,
            "minutes": 79,
            "thirtyDayAvgMinutes": 72
          },
          "light": {
            "count": 35,
            "minutes": 264,
            "thirtyDayAvgMinutes": 196
          },
          "rem": {
            "count": 10,
            "minutes": 92,
            "thirtyDayAvgMinutes": 64
          },
          "wake": {
            "count": 38,
            "minutes": 65,
            "thirtyDayAvgMinutes": 43
          }
        }
      },
      "logId": 20170274799,
      "minutesAfterWakeup": 0,
      "minutesAsleep": 435,
      "minutesAwake": 65,
      "minutesToFallAsleep": 0,
      "startTime": "2018-11-15T21:18:30.000",
      "timeInBed": 500,
      "type": "stages"
    }
  ],
  "summary": {
    "stages": {
      "deep": 71,
      "light": 215,
      "rem": 85,
      "wake": 41
    },
    "totalMinutesAsleep": 435,
    "totalSleepRecords": 1,
    "totalTimeInBed": 500
  }
}

ver1.2の方が睡眠レベル単位でまとめているため、データ容量が少なくなる傾向になります。
以後、ver1.2形式で取得します。

f:id:kitsugi:20181118161035p:plain

《2018-11-17》

Fitbitを外したまま寝てしまった。。。
しかし、エラーにならず結果が取得できることがわかりました。

curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-17.json | jq . > 2018-11-17-v1.2.json

2018-11-17-v1.2.json

{
  "sleep": [],
  "summary": {
    "totalMinutesAsleep": 0,
    "totalSleepRecords": 0,
    "totalTimeInBed": 0
  }
}

《2018-11-18》

curl -H "Authorization: Bearer **********.**********" https://api.fitbit.com/1.2/user/-/sleep/date/2018-11-18.json | jq . > 2018-11-18-v1.2.json

2018-11-18-v1.2.json

{
  "sleep": [
    {
      "dateOfSleep": "2018-11-18",
      "duration": 24840000,
      "efficiency": 89,
      "endTime": "2018-11-18T05:30:00.000",
      "infoCode": 0,
      "isMainSleep": true,
      "levels": {
        "data": [
          {
            "dateTime": "2018-11-17T22:36:00.000",
            "level": "wake",
            "seconds": 30
          },
          {
            "dateTime": "2018-11-17T22:36:30.000",
            "level": "light",
            "seconds": 1020
          },
          略
          {
            "dateTime": "2018-11-18T05:18:00.000",
            "level": "light",
            "seconds": 510
          },
          {
            "dateTime": "2018-11-18T05:26:30.000",
            "level": "wake",
            "seconds": 210
          }
        ],
        "shortData": [
          {
            "dateTime": "2018-11-17T22:36:00.000",
            "level": "wake",
            "seconds": 120
          },
          {
            "dateTime": "2018-11-17T22:45:00.000",
            "level": "wake",
            "seconds": 30
          },
          略
          {
            "dateTime": "2018-11-18T04:32:00.000",
            "level": "wake",
            "seconds": 30
          },
          {
            "dateTime": "2018-11-18T04:35:00.000",
            "level": "wake",
            "seconds": 120
          }
        ],
        "summary": {
          "deep": {
            "count": 4,
            "minutes": 77,
            "thirtyDayAvgMinutes": 73
          },
          "light": {
            "count": 23,
            "minutes": 203,
            "thirtyDayAvgMinutes": 209
          },
          "rem": {
            "count": 8,
            "minutes": 75,
            "thirtyDayAvgMinutes": 70
          },
          "wake": {
            "count": 26,
            "minutes": 59,
            "thirtyDayAvgMinutes": 47
          }
        }
      },
      "logId": 20192347613,
      "minutesAfterWakeup": 0,
      "minutesAsleep": 355,
      "minutesAwake": 59,
      "minutesToFallAsleep": 0,
      "startTime": "2018-11-17T22:36:00.000",
      "timeInBed": 414,
      "type": "stages"
    }
  ],
  "summary": {
    "stages": {
      "deep": 71,
      "light": 215,
      "rem": 85,
      "wake": 41
    },
    "totalMinutesAsleep": 355,
    "totalSleepRecords": 1,
    "totalTimeInBed": 414
  }
}

f:id:kitsugi:20181118161054p:plain

睡眠データの項目

公式ドキュメントを見ても項目についての詳細な説明がなく、項目名と実データから推測。間違っていたらコメントください。

睡眠データ

睡眠データ(sleep)は配列形式です。
昼寝をして夜寝た場合は2件登録されます。複数件ある場合は、Fitbit側が判定して1件だけisMainSleepフラグをtrueにします。

項目 サンプル 説明
dateOfSleep '2018-09-18' 睡眠日
duration 21960000 ベッド(布団)にいた合計時間(ミリ秒)
efficiency 97 睡眠効率(%)。のび太は100。計算方法はここを参考
endTime '2018-09-18T05:06:00.000' 起床時間
infoCode 0
isMainSleep true メインの睡眠かどうか
levels 明細データのレベル
data {"dateTime": "2018-09-18T02:27:00.000", "level": "rem", "seconds": 840} 睡眠レベル単位のデータ配列。wake, rem, light, deepの4種類。睡眠レベル、開始時間、持続時間(秒)
shortData {"dateTime": "2018-09-17T23:04:00.000", "level": "wake", "seconds": 150} 覚醒データ配列。目覚めた状態が3分以内のデータ
summary "deep": {"count": 4, "minutes": 78, "thirtyDayAvgMinutes": 57} 睡眠レベル単位の要約データ。合計回数、合計時間(分)、30日間の平均時間(分)
logId 19548152255 ログID
minutesAfterWakeup 5 起床した後の時間(分)
minutesAsleep 334 睡眠時間(分)
minutesAwake 32 起きていた時間(分)
minutesToFallAsleep 0 最初の眠るまでの時間(分)
startTime '2018-09-17T23:00:00.000' 就寝時間
timeInBed 366 ベッド(布団)にいた時間(分)。minutesAsleep + minutesAwake
type 'stages' 睡眠データの種類

timeInBed = minutesToFallAsleep + minutesAsleep + minutesAwake

要約データ

項目 サンプル 説明
stages 睡眠レベル
deep 38 深い睡眠(分)
light 163 浅い睡眠(分)
rem 82 レム睡眠(分)
wake 31 目覚めた状態(分)
totalMinutesAsleep 334 睡眠時間の合(分)
totalSleepRecords 1 合計睡眠回数(sleep配列の件数)
totalTimeInBed 366 ベッド(布団)にいた合計時間(分)。totalTimeInBed - totalMinutesAsleep が起きている時間

Python3の場合

Fitbit用のモジュールを利用すると簡単に取得できます。

クライアントID (OAuth 2.0 Client ID)、クライアントシークレット、アクセストークン、リフレッシュトークンは https://dev.fitbit.com/apps で登録したアプリから取得した値を設定します。

API_VERSIONに1.2を指定しています。(デフォルト1.0)

import datetime
import fitbit

def main():
    client = fitbit.Fitbit(CLIENT_ID,
                           CLIENT_SECRET,
                           access_token=ACCESS_TOKEN,
                           refresh_token=REFRESH_TOKEN)
    client.API_VERSION = 1.2
    
    dt = datetime.date(2018, 11, 18)
    sleep = client.get_sleep(dt)
    print(sleep)


if __name__ == "__main__":
    main()

参考