miyarappoの試行錯誤

とあるエンジニアの試行錯誤

Oura Ring API を使って毎日の自分のコンディションをSlackに通知する

はじめに

会社のSlackの自分のtimesに自分のコンディションスコアを通知しています。

Slackへの通知
Slackへの実際の通知

アプリケーションでメトリクスを取得するのと同様に、人間の活動のメトリクスも取得したい!体調が悪くなる予兆をキャッチしたい!無理が効く体調か知りたい!そんな試みです。

やっていること

やっていることはシンプルですが、結構おもしろいです。

  1. Oura Ringを付けてコンディションや睡眠を記録
  2. Google App ScriptでOura APIからスコアを取得
  3. そのスコアに応じてSlackのプロフィールアイコンを変える & そのスコアを自分のtimesに通知する

Oura Ring とは?

自分はウェアラブルバイスが大好きでして、ここ数年はOura Ringというデバイスを愛用しています。

指に着用するだけで睡眠、心拍数、体温、行動量を取得して体調をスコアリングしてくれます。

Ouraアプリの画面
Ouraアプリの画面。取得したデータは全てアプリで確認する。

Oura Ringが出す数値は非常に納得感があり、スコアが悪いとちゃんと体調が悪くなります。笑

今までjawbone => Apple Watch => Fitbit => MiBandを試してきましたが、付け心地、充電の持ち、同期の速さ、数値の納得感が群を抜いており、とても気に入っています。

またOura RingはAPIも公開してくれていて、自分の生体データは自由にGETできます。

How

実際に自分が使っているコードはこちらになります。

以下では要点を絞って説明します。

Oura API実行

Oura Ringを購入するとアカウントの登録が必須なのですが、そのアカウトの管理画面からアクセストークンを取得します。

Oura API ドキュメント

const ouraBaseUrl = 'https://api.ouraring.com/v1/'
const ouraAccessToken = 'xxxx'

const ouraApiOptions = {
    "method": "get",
    "headers": {
    "Authorization": "Bearer "+ ouraAccessToken
    }
  }
const ouraReadinessUrl = `${ouraBaseUrl}readiness?start=${previousDate}&end=${currentDate}`

const response = UrlFetchApp.fetch(ouraReadinessUrl, ouraApiOptions)

const responseData = JSON.parse(response)
const readinessScore = responseData.readiness[0].score

Oura APIはちょっとイケてなくて、今日のスコアを取得するためには前日と今日の日付をクエリパラメータに入れる必要があります。

(アクセストークンは平文にしちゃっています。漏れても自分の体調が分かるくらいなのですがGASでクレデンシャルを安全に使う方法ってあるのでしょうか。。)

スコアに応じてアイコンとメッセージを決定

スコアに応じたアイコンとメッセージを定義しておき、愚直に分岐で決定させました。

function decideStatus(readinessScore) {
  const statusObj = {
    "great": {
      "emoji": ":star-struck:",
      "text": "絶好調!今日の俺は神"
    },
    "good": {
      "emoji": ":smile:",
      "text": "良い感じ"
    },
    "normal": {
      "emoji": ":simple_smile:",
      "text": "まぁまぁ"
    },
    "delicate": {
      "emoji": ":tired_face:",
      "text": "微妙。今日は早めに帰る"
    },
    "bad": {
      "emoji": ":scream:",
      "text": "無理ぽよ"
    },
    "sleeping": {
      "emoji": ":sleeping:",
      "text": "寝てます"
    }
  }

  let statusEmoji
  let statusText

  if (readinessScore >= 90) {
    statusEmoji = statusObj.great.emoji
    statusText = statusObj.great.text
  } else if (readinessScore >= 80 && readinessScore < 90 ) {
    statusEmoji = statusObj.good.emoji
    statusText = statusObj.good.text 
  } else if (readinessScore >= 70 && readinessScore < 80 ) {
    statusEmoji = statusObj.normal.emoji
    statusText = statusObj.normal.text
  } else if (readinessScore >= 60 && readinessScore < 69 ) {
    statusEmoji = statusObj.delicate.emoji
    statusText = statusObj.delicate.text
  } else if (readinessScore == 0) {
    statusEmoji = statusObj.sleeping.emoji
    statusText = statusObj.sleeping.text
  }else {
    statusEmoji = statusObj.bad.emoji
    statusText = statusObj.bad.text
  }

  return {statusEmoji: statusEmoji, statusText: statusText}
}

Slackに通知

上記で決定したアイコンとメッセージをSlackに通知します。

#genaralとかに通知されるとうざいので個人が好き勝手にできるチャンネルに通知しましょう。自分はtimesに通知しています。

事前準備としてSlack Botを作成する必要があります。

参考にした記事

↓ Slackのプロフィールのステータスを変更

function updateSlackProfile(statusEmoji, statusText) {
  const slackProsileOpstions = {
    "method": "post",
    "headers": {
    "Authorization": "Bearer "+ slackAccessToken
    },
    "contentType": "application/json",
    "payload": JSON.stringify({
      "profile": {
        "status_emoji": statusEmoji,
        "status_text": statusText
      }
    })
  }
  UrlFetchApp.fetch(slackProfileUrl, slackProsileOpstions)
}

↓ Slackのチャンネルに通知

function postSlackChannel(readinessScore, sleepScore, activityScore, statusEmoji) {
  const slackApiOptions = {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify({
      "username": "Oura Score Notification",
      "icon_emoji": ":green_heart:",
      "text":  text
    })
  }
  UrlFetchApp.fetch(slackHooksUrl, slackApiOptions)
}

実行結果

GASのスクリプトが書けたら定期実行します。

自分はtimesに以下のように通知しています。

timesへの通知
timesへの通知

timesに入っていない人にアピールするためにプロフィールアイコンがコンディションと連動するようにもしました。

プロフィールアイコン
プロフィールアイコン

調子が良い時は体調が良さそうなアイコンになりますし、調子が悪い時は体調が悪そうなアイコンになります。

体調が悪い時は労ってもらうことが目的です。

やってみた感想

かれこれ3ヶ月ほど運用してみましたが、自分の体調を周りに公開するのは新鮮な体験でした。

同僚の反応

特に最初の頃は面白がって多くの反応をもらえました。会社の同僚は皆優しい。

最初の頃はコンディションスコアのみを通知していましたが、現在は睡眠とアクティビティのスコアも通知しています。

Slackの通知
はじめたての頃。何人かは興味を持って反応してくれた

目論見通り、スコアが低い時は「大丈夫かな?」みたいな反応をもらえました。

↓の時は転職したばかりでテーブル構造の理解に苦しんでいた時でした。

Slack通知
スコアが低いと心配の声をもらえた

APIでデータを取得するためには、起床後にOura Ringに記録されたデータをアプリに同期することが必要です。

GASのスクリプトは9時から10時の間で定期実行をしているのですが、実行時にその日のデータを同期していないとスコアが0で通知されてしまい死亡説が流れました。

Slackの通知
バグでスコアが0になってしまった時は死亡説が流れた

心理的な障壁

「体調どう?」と幾度となく上司や同僚に問われ、また自分も質問してきました。

ですが、中々正直に言うことも言われることも少ないのではないでしょうか?

この課題を解決すべく本記事のような試みをしました。

自分は情報は基本的にオープンにする性質です。日々の体調を公開することくらいどうってことないと思っていましたし、実際に公開しています。

そんな自分でも「スコアが良い日が続くと頑張ってないと思われるのではないか?」「スコアが低いと不必要に心配されるのでは?」と思うときがありました。

生体データはある意味究極のプライバシーデータだと思います。全員が公開してお互いのデータを確認できたら便利だと思っていますが、個人情報保護の観点をクリアにするのが難しいだろうなと改めて感じました。

これってパルスサーベイの究極系なのでは?

コロナ禍で従業員の健康を質問するパルスサーベイを行う企業が増えたと聞きます。

自分も前職では1週間に1度、健康度を5段階で回答するサーベイが人事から届いていました。

でも自分は正直に回答することが出来ませんでした。しんどい理由があったとしても事情を第三者に説明するのってしんどくないですか?しんどい時なら尚更そうだと思います。

生体データなら手間もかからないし、偽ることができないです。

実現のハードルはありますが、企業が全ての従業員に実施したいパルスサーベイの究極系は生体データを取得することだと思います。

業務システムのデータとの関連性

勤務時間や仕事中の変化(異動とか)と照らし合わせて見ると面白いデータがとれると思います。

自分は転職して最初の1ヶ月は明らかにスコアが悪くなりました。笑

個人情報の観点から実施が難しいと予想しますが、タレントマネジメントの文脈で使えたら相当面白いんじゃないかと予想しています。

誰と誰が一緒に働くと睡眠のスコアが上がるとか可視化できたら楽しそうです。

自分は従業員のエンゲージメントを測定するサーベイを提供するSaaSの開発をしているのですが、生体データを活用できたら楽しいだろうなと想像しています。

おわりに

簡単なスクリプトでの実験でしたがやってみると色々な知見を得られました。

FitbitにもAPIはあるので同じようなことはできると思います。

テクノロジーウェルビーイングの掛け合わせは面白い分野だな〜。