OuraRingは活動トラッカーとして非常に優秀なデバイスです。
アプリもしっかりと作りこまれており、それだけも使い勝手はいいのですが、Oura APIとして各種データを取得できることもメリットの一つです。
今回は、OuraRingの睡眠記録から就寝時間と起床時間を抜き出し、それを普段から利用しているGoogleカレンダーに予定として登録するというものを作成しました。
Ouraアプリを利用していて、Googleアカウントさえあれば、多少のプログラミング知識があれば実装可能です。
今回、私が作成したのも簡単に取得できる睡眠時間のデータなので、もっとデータを加工したりすることで睡眠効率の分析なども行うことができるでしょう。
今回のゴール
まずは、今回どのようなことをしたのか簡単に紹介します。
Ouraは日々の活動記録を以下のようにアプリで確認が可能です。また、これらのアプリ内のデータのほとんどがAPIで取得が可能です。
ここから、GASを使用して、睡眠記録を取得して、Googleカレンダーに連携(イベントとして書き込む)させました。
こうすることで一週間の睡眠記録が俯瞰してみることができ、日々の生活改善に役立てることを期待しています。
Oura APIを使う準備
さて、ここからは早速OuraAPIの使ってみましょう。
まず、Oura APIを使用するには開発者用Personal Access Tokenを作成する必要があります。
開発者用と言ってもOuraのスマホアプリを利用している方であれば、すでにOuraのアカウントはあるので、Personal Access Tokenの作成は可能です。
ちなみに、Personalとあるように個人使用のAPIです。 APIを使ったアプリ開発には、OAuth2でユーザー認証という別の方法もありますが、今回は割愛します。
Oura Personal Access Tokenの作成
Oura Personal Access Tokenは、Ouraのサイト(こちら)から作成することができます。
すでにOura Ringが手元にあり、Ouraアプリを利用している方はアカウント登録済みですので、サインインしましょう。
ログイン後は、サイドメニューからPersonal Access Tokenに移り、右上の「Create New Personal Access Token」をクリックして、新規トークンを作成します。
画面遷移して、トークンの使用目的をNoteの箇所に記載します。
私は、Token作るのにOuraに対してアピールが必要なのかと思い、まじめに「For self analysis.」とか書いていました。
ただ、実際は複数トークンを使い分ける場合のメモのレベルなので自分が分かれば何でもOKです。
これで、Tokenが発行されました。このTokenはメモしておくようにしましょう。
このトークンが流出すると、他のユーザーに自分の活動ログだけでなく登録してるメールアドレスなんかもAPI取得できるので、管理厳守です。
Oura API
さて、トークンが発行できたので、まずはOura APIからデータを取得できるか試してみます。
OuraAPIは公式ドキュメントにどのようなAPIの叩き方(URLの記載)が必要か、どんな記録が取れるのかなどは記載されています。
OuraAPIにはv1とv2が公開されています。
それぞれURLを書くお作法や取れるデータが若干異なりますので、今回は最新版のv2でデータを取るようにします。
公式のQ&Aにも記載ありますが、v1のサポート期間は少なくとも1年間(2023年1月ころ)と記載ありますので、これからはv2でやっていくべきでしょう。
GASでOuraAPIを叩く
今回の最終目的は睡眠時間を取得してGoogleカレンダーに書き込むことなので、GAS(GoogleAppScript)でOuraAPIを叩いてみることにします。
トークンはHeaderにBearer認証で記載します。
Authorizationの部分にトークンを記載しますが、トークンの前のBearerは必須ですので、コピペするときは、上書きしてしまわないように注意してください。
ひとまず一番、GETのresponseが短いpersonal_info(個人情報)を取得するAPIを叩いてみました。
function main() {
let myHeaders ={
'Authorization': 'Bearer <<Your Token>>'
}
let requestOptions = {
muteHttpExceptions: true,
method: 'GET',
headers: myHeaders
}
let res = UrlFetchApp.fetch('<https://api.ouraring.com/v2/usercollection/personal_info?start_date=2021-11-01&end_date=2021-12-01>', requestOptions)
console.log(res.getContentText())
}
得られた結果は、json形式で以下のようになります。 Ouraに登録している年齢や体重、身長、性別といった情報が取得できました。
{
"age": 32,
"weight": 65.7,
"height": 1.7,
"biological_sex": "male",
"email": "<<メールアドレス>>"
}
これで、OuraAPIを取得する準備ができましたので、本題である睡眠時間を取得していきます。
Oura APIから睡眠記録をGoogleカレンダーに登録する
Oura APIの使い方は把握できたので、今回の目的である睡眠の記録として、睡眠スコアと睡眠時間を取得していくことにします。
また、取得した睡眠記録と睡眠時間をGoogleカレンダーに登録します。
ドキュメントを確認したとところ、睡眠スコアと睡眠の記録はAPIがそれぞれAPIがあるようです。
睡眠スコアは対象とする日のトータルの睡眠スコアなので、1日に1スコアが記録されます。
一方、睡眠記録は一日のうちに夜の睡眠のほかに昼寝など別途記録があった場合はそれぞれ記録されていきます。
1.Oura API GETメソッドの関数化
まずは、先ほど作成したOura APIのGETする関数を使いまわしできるように関数としてまとめます。
ConnectOuraAPI()として作成しました。
引数として、APIの種類とパラメータ(基本的には日付)とトークンを渡すようにしました。
function ConnectOuraAPI(api, parameters, token) {
const baseUrl = '<https://api.ouraring.com/v2/usercollection/>'
let url = `${baseUrl}${api}?`
for (let parameter in parameters){
url += `${parameter}=${parameters[parameter]}&`
if(Object.keys(parameters).slice(-1)[0] === parameter){
url = url.slice(0, -1)
}
}
let requestOptions = {
muteHttpExceptions: true,
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
}
let res = UrlFetchApp.fetch(url, requestOptions).getContentText()
return JSON.parse(res)
}
これで、OuraAPIのGETメソッドをJSON形式でreturnしてくれる関数の完成です。
2.睡眠スコアの取得
まずは、睡眠スコアを取得します。
apiは公式ドキュメントから確認ください。
当日の睡眠スコアなどを取得するAPIはdairy_sleepですので、このAPIを取得します。
また、パラメータとして、取得する期間が必要です。
今日時点(昨日の就寝後)のデータを取得したいので、start_date、end_date両方ともにに今日の日付(today)を付与しています。
なお、OuraAPIでは日付型ではなく、YYYY-MM-DDの文字列型になるので、Utilities.formatDate(日付 , タイムゾーン, 型)で変換してから付与しています。
function main(){
let token = '<<Your Token>>' //ご自身のTokenを入力
let api = 'daily_sleep'
let today = new Date()
let parameters = {
start_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd'),
end_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
}
let dairySleep = ConnectOuraAPI(api, parameters, token)
console.log(dairySleep)
}
得られた結果は、以下のようになります。
{ data:
[ { contributors: [Object],
day: '2022-12-08',
score: 78,
timestamp: '2022-12-08T00:00:00+00:00' } ],
next_token: null }
必要なのは、scoreだけですので、最終的には
dairySleep.data[0].score
として、スコアの部分だけを抜き出しました。
3.睡眠時間の取得
次に睡眠記録のデータを取得していきます。
睡眠記録は、apiがsleepとなります。
パラメータは睡眠スコアとは異なり、昨日の就寝から今日の起床までを取得する必要があります。
start_dateを昨日(yesterday)、end_dateを今日(today)とします。
また、昨日昼寝をしているとその記録も取得されることになるので、そのあとの処理がdeiry_sleep
と異なることには注意が必要です。
function main(){
let token = '<<Your Token>>' //ご自身のTokenを入力
let api = 'sleep'
let today = new Date()
let yesterday = new Date()
yesterday.setDate(yesterday.getDate()-1)
let parameters = {
start_date: Utilities.formatDate(yesterday, 'JST', 'yyyy-MM-dd'),
end_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
}
let sleep = ConnectOuraAPI(api, parameters, token)
console.log(sleep)
}
sleepで得られる結果は以下のようになります。 今回は、昼寝があった日の睡眠記録を取得してみました。
# 一部省略します。
{ data: [
{
average_breath: 13.75,
average_heart_rate: 61.5,
bedtime_end: '2022-12-05T12:57:29+09:00',
bedtime_start: '2022-12-05T12:36:29+09:00',
day: '2022-12-05',
sleep_phase_5_min: '44211',
time_in_bed: 1260,
total_sleep_duration: 330,
type: 'sleep'
},
{ average_breath: 14.125,
average_heart_rate: 65.25,
bedtime_end: '2022-12-06T09:04:01+09:00',
bedtime_start: '2022-12-06T02:59:01+09:00',
day: '2022-12-06',
sleep_phase_5_min: '44111121211111331....322442212222311444444',
time_in_bed: 21900,
total_sleep_duration: 17640,
type: 'long_sleep'
}
],
next_token: null
}
ちょっと、自分の睡眠記録が出るのは恥ずかしいですね。
取得した記録<data>が配列になっていて、その中にオブジェクト形式で2つの睡眠記録が入っています。
上が12時頃に昼寝した記録があります。 下は3時ごろに寝て9時ごろに起きたとんでもない睡眠記録ですね。(この日はワールドカップの日本戦の日でした、、、、、)
今回は、この中で就寝時間であるbedtime_startと起床時間のbedtime_endを使用します。
なお、この二つの時間はベットの中かでゴロゴロしていた時間も含まれています。
Ouraアプリで表示されている合計睡眠時間に該当します。
では、実際の睡眠時間だけを取得したい場合はどうするかというと、得られた結果のsleep_phase_5_minを利用することで、睡眠開始から5分ごとの睡眠の状態を取得することができるようです。
4が覚醒状態で1が深い睡眠状態を示しています。
ただの数字の羅列なので少し加工が必要ですが、覚醒時間(=4)の数を取得して、5minをかけてあげればざっくりとした実際には起きてた時間というのも取得が可能です。
今回は、就寝時間と起床時間だけでいいものとするので、このあたりの整形しないものとします。
4.Googleカレンダーに登録する
睡眠記録と睡眠時間が取得できたので、これをGoogleカレンダーに登録します。
今回は、昼寝の記録もGoogleカレンダーに登録することにします。
従って、睡眠記録がリスト形式でゲットできるので、for文をまわして、googleカレンダーに登録します。
また、先ほど説明した睡眠スコアを取得するAPIと睡眠記録を取得するAPIを叩く関数は、それぞれ引数が異なるapiとparametersを書き換えて、同じConnectOuraAPI()で呼び出ししています。
特に、start_dateの日付変更を忘れには注意してください。
一部省略しますが、先ほどOuraAPIで取得した睡眠記録からfor文でGoogleカレンダーにイベント(予定)を追加する関数「AddEventGoogleCalender()」を呼び出します(コードの全体は記事の最後に載せます)。
function main(){
let token = '<<Your Token>>' //ご自身のTokenを入力
let api = 'sleep'
let today = new Date()
let yesterday = new Date()
yesterday.setDate(yesterday.getDate()-1)
let parameters = {
start_date: Utilities.formatDate(yesterday, 'JST', 'yyyy-MM-dd'),
end_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
}
let sleep = ConnectOuraAPI(api, parameters, token)
api = 'daily_sleep'
parameters.start_date = Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
let dairySleep = ConnectOuraAPI(api, parameters, token)
let dairySleepScore = dairySleep.data[0].score
for (let s of sleep.data){
// sleep記録ごとにカレンダーイベントを作成する
AddEventGoogleCalendar(dairySleepScore, s.bedtime_start, s.bedtime_end)
}
}
AddEventGoogleCalendar()は、GASのドキュメント通りですが、引数として、睡眠スコアと就寝・起床時間を受け取っています。
なお、OuraAPIで取得してる起床時間などは文字列型です。
先ほど、OuraAPI叩くために文字列に変換しましたが、今度はその逆で、文字の日付・時刻をDate型に変換する必要があります。
Googleカレンダーに登録するには取得した日付の文字列をnew Date()
を使用してDate型に変換しています
function AddEventGoogleCalendar(score, bedtime, awakeTime){
const CALENDAR_ID = '<<Your CalenderID>>; //カレンダーIDを入力
const calendar = CalendarApp.getCalendarById(CALENDAR_ID)
calendar.createEvent(`睡眠スコア:${score}`, new Date(bedtime), new Date(awakeTime))
}
今回は、普段作ってるカレンダーとは別にOuraの睡眠記録を表示する専用のGoogleアカウントを一つ作成して、普段使用しているアカウントと連携しました。
ただし、睡眠スコアは今日の分しか取得してませんので、昨日の昼寝においても睡眠スコアは本日分が付与されることにご注意下さい。
これを避けるためには、昼寝の記録は睡眠スコアを記載しないようにしたり、昨日分を取得したりする必要があります。
必ず昼寝するわけでもないので、今回は本日分をそのまま記載するようにしました。
5.GASを定期実行する
さて、ここまでコードはかけましたが、これでは昨日から今日の睡眠記録を取得するだけです。
今回作成したGASコードを毎日、定期実行することで自動化していきます。
実際には、起床時間にバラツキがあったりすると定期実行では不都合がある場合もありますので、適宜スケジュールは調整してください。
定期実行は、GASのサイドメニューのトリガーから作成することが可能です。 右下のトリガーを追加から新規トリガーを作成します。
今回は、実行する関数は大元となる「main()」を選択して、時間主導型で毎日午前9時から10時の間に実行されるようにしました。
あくまで、9時には起きている想定です。
ただ、9時に起きていてもOura RingからOuraアプリに記録が伝達されていないとAPIをたたいても結果が取得できません。
時間に少し余裕を持たせるか、朝起きたらまずOuraアプリを開いて情報を取り込む癖をつけてるようにしておきましょう。
なお、今回のコードは重複した睡眠記録はGoogleカレンダーへの登録をスキップするといったコードは記載していません。
ただ、日付を受け取り、イベントを追加するだけです。 重複の入力を避けたい場合などは、コードを修正してみてください(コード全体は本記事の末尾に記載しています)。
おわりに
Googleカレンダーへの連携を毎日行うと、冒頭で表示したように週間の睡眠時間を並べて俯瞰してみることができます。
これを日常的に眺めること、睡眠意識の改善をやっていきます!!
OuraRingは指輪型で睡眠の邪魔にもなりにくく非常に気に入っています。
ただ、値段が少し張りますし、OuraRing3になってからサブスクリプションとなり月額6.99ドル(900円~1000円)の課金制となってしまいました。
トラッカーとして、月額およそ1000円は少し割高に感じないでしょうか。
ただ、メンバーシップになったことでAPI機能が利用できることになったのは大きなメリットです。
Ouraアカウントと少しのプログラミング知識さえあれば、自分の活動記録はAPIで取得することができます。
自分だけのためのプログラミングとなると今回のようにGASを使用して、Tokenも直接書き込んでいるようなコードでも問題ありません。
これが、アプリ化して他のユーザーのTokenを管理するようにしたり、ネット公開などしてしまうとセキュリティをいろいろと考える必要があります。
せっかくの便利なデバイスなので、月額課金分はぜひとも使い倒してください。
Oura Ringの購入までの流れや関連記事は別途紹介してますので、Oura Ringに興味がある方は是非ご覧ください。
コード全文
最後に今回、作成したGASのコードを記載します。 TokenやGoogleIDなど適宜書き換えていただき、使ってみてください。 実行する関数はmain()です。その設定もお忘れなく。
function ConnectOuraAPI(api, parameters, token) {
const baseUrl = 'https://api.ouraring.com/v2/usercollection/'
let url = `${baseUrl}${api}?`
for (let parameter in parameters){
url += `${parameter}=${parameters[parameter]}&`
if(Object.keys(parameters).slice(-1)[0] === parameter){
url = url.slice(0, -1)
}
}
let requestOptions = {
muteHttpExceptions: true,
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
}
let res = UrlFetchApp.fetch(url, requestOptions).getContentText()
return JSON.parse(res)
}
function AddEventGoogleCalendar(score, bedtime, awakeTime){
const CALENDAR_ID = '<<Your Calendar ID>>' //ここにGoogleカレンダーIDを記載
const calendar = CalendarApp.getCalendarById(CALENDAR_ID)
calendar.createEvent(`睡眠スコア:${score}`, new Date(bedtime), new Date(awakeTime))
}
function main(){
let token = '<<Your Personal Access Token>>' //ここにOuraAPI Tokenを記載※コード管理も注意
let api = 'sleep'
let today = new Date()
let yesterday = new Date()
yesterday.setDate(yesterday.getDate()-1)
let parameters = {
start_date: Utilities.formatDate(yesterday, 'JST', 'yyyy-MM-dd'),
end_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
}
let sleep = ConnectOuraAPI(api, parameters, token)
api = 'daily_sleep'
parameters.start_date = Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
let dairySleep = ConnectOuraAPI(api, parameters, token)
let dairySleepScore = dairySleep.data[0].score
for (let s of sleep.data){
AddEventGoogleCalendar(dairySleepScore, s.bedtime_start, s.bedtime_end)
}
}
コメント
コメント一覧 (2件)
Ouraを連携させたいと思い、参考にさせていただきました!
– カレンダーを参照しすでに予定が登録されている場合はスキップする処理
– ouraへデータが連携されていない日はスキップ
– 過去10日分を登録
上記処理を追加し、メモとして以下にupさせていただいておりますが、コード利用が不適切でしたらお申し付けください。
https://gist.github.com/tai-sho/32ea89f0be8b595f2e616542e39b6d69
コメント、また、当コード参考にしていただきありがとうございます。
参照も記載いただいてますし、問題ありません。
むしろ、不足していた同一日のスキップや過去分登録等至らぬ点を補足いただきありがとうございます!