はじめに
ごんびぃーです。
先日おともだちの じゅんさん からこんなツイートをいただきました。
これはごんちゃんが喜ぶハック
— じゅん💉💉+💉@6/15XRミーティング北海道エリアのお世話 (@jun_mh4g) May 25, 2022
.@GONBEEE_project https://t.co/FIQxbqBLri
Tanaka Seigo さんによるOuraRing APIとHololens2による
身体データ表示アプリです。
Twitterで僕をご存知の方はOuraRingユーザーである事を知っていると思いますが、
せっかくのスマートデバイスなので何かしらハックしてみたいなとは考えていました。
そんな状況で紹介していただいたツイートで、
やらざるを得ない!ということで早速OuraRing APIを叩いてきました。
何故Node-RED・enebularを?
では、OuraRing API 自体は HTTP なのになんで enebular を使っているかというと、enebular はあくまで中継サーバーの役目をしている感じで、最新1件を掴むデータの再加工や、もしかすると他のデモや IoT デバイスとつなぎ込みたいときに、柔軟性を出したいのでそうしたというところです。
— Tanaka Seigo (@1ft_seabass) May 25, 2022
TanakaさんによるとOuraRingAPI自体はHTTPで簡単に取得できますが、
Unity以外の環境や他デバイスへのつなぎ込みを考えると
ゴリゴリ実装よりも一度サーバーを噛ませた方が汎用性が高くなるとのことでした。
せっかく先人が便利実装の発見をしているので、
これを機に新しいカテゴリの勉強もしようということでenebularの勉強に着手しました。
まずはcurlから
長年Unityを使っておきながらHTTP経由でのAPI発火というものを
まともにやったことがない人生でした。
そのためAPIの叩き方の初歩である(と思しき)curlからやってみました。
zenn.dev
参考としたリンクはこちらです。
コマンドラインでcurlを叩く際のコードは以下の通りです。
curl -i -X GET -H "authorization:Bearer XXXXXXXXXXXXXXX" "https://api.ouraring.com/v2/usercollection/personal_info"
全てを理解した後にこのコードを読むと-Hの所でHTTP requestの
ヘッダーを設定していたのかーなどぼんやりと分かってくる情報もありました。
XXXXXXXとなっている箇所はOuraRingの
https://cloud.ouraring.com/personal-access-tokens
から作成できるPersonal Access Tokenを入力します。
OuraRing APIはBearer認証となっていました。
このコードを叩いて帰ってくる結果がこちら
OuraRing API v2、とりあえずCurlで叩いたけど
— ごんびぃー𓊍 (@GONBEEE_project) May 25, 2022
マジで一瞬で取れますね
便利か? pic.twitter.com/l6lrWncboR
API発火、マジでシンプルにできたので、
逆になんで今までこういったことをやってこなかったんだ、、、?
enebularやってみよう
enebularを全く知らない所から始めた当初は
enebularという言語なりウェブサービスがあると思ってました。
ウェブサービスであることは間違いないですが、
本質的にはNode-REDというノードベースでJavaScriptコードを書ける、
各種APIやIoTデバイス向けの言語であるという認識で落ち着きました。
enebularはウェブ上でNode-REDを編集できるエディタがあり、
Node-RED入門としてはかなり好条件な環境だと思います。
Node-REDはflowとよばれるファイルを実行することで稼働するもので、
enebular上で書いたflowをローカルに.jsonでダウンロードする事もできます。
ローカルで実行していた時の様子はこちら。
Node-RED完全に理解した!
— ごんびぃー𓊍 (@GONBEEE_project) May 26, 2022
なるほど、HTTP inとHTTP responseはそういうことか!
今回とりあえずHTTP POSTを使ってるけど、
本来ならGETを使うべき場面だったっぽいな
レスポンスが帰ってくるのは分かったから、
Node-RED内でOuraAPIのJSONを丸めて任意のデータだけ帰ってくるようにすればいいのか! pic.twitter.com/9htlaYrAir
更にenebularの素晴らしい点として、
つい最近enebularクラウド実行環境というものがリリースされ、
flowの開発から実行までenebular環境内で完結することができるようになりました。
タイミング神すぎ。
blog.enebular.com
ということで、enebularという環境はNode-RED初学者向けとして
非常に良い条件の環境ではないかと思っています。
もしこれから始める人がいたらenebular強くおすすめします。
flowの完成形
個人的な備忘録も兼ねているので、
先に完成形を示して各挙動を解説していくという形を取りたいと思います。
(このブログの運用まだ定まってないのでおゆるし)
enebularには完成形flowを共有するDiscoveryシステムがありますが、
今回はBearerトークンを直書きしているので共有ナシです。お許し!
各ノード機能を解説していきます。
エントリーポイント(LCDP・[get]/unity-test・タイムスタンプ)
フローの左上に見える3つのノードが所謂エントリーポイントに相当します。
LCDPはenebularクラウド実行環境用のエントリーなので今回はスキップします。
[get]/unity-testがHTTP in ノードで、
URL経由でこのノードを発火して運用するのが主となります。
中身は超シンプルなURLです。
Unityから試しに発火する際に着けた名前がそのままですが、
実際の運用時はこのURLを「/get-heartrate」など
わかりやすい名前にすることを推奨します。
タイムスタンプノードはデバッグ用に用意した
何の機能もないInjectノードです。
デバッグ時に毎回HTTP叩くのは面倒なので、
エディタ上でボタンを用意したというだけです。
ヘッダー処理(Set Header)
どちらかというとOuraRing API側の仕様になりますが、
OuraAPIにアクセスするにはヘッダーにHostとAuthorizationを記述する必要があります。
https://cloud.ouraring.com/v2/docs#section/Authentication
Node-REDで他所にHTTPを飛ばす HTTP requestノードは
Bearer認証トークンを記すフィールドがありますが、
Host先を書くことができません。
そのためHTTP requestノードでAPIを叩く前に、
手動でヘッダーを編集する必要があります。
msg.headers = {}; msg.headers["host"] = "api.ouraring.com"; msg.headers["authorization"] = "Bearer XXXXXXXXX";
ミソはこの部分で、msgのヘッダーを宣言して
hostとauthをそれぞれ手動記述しています。
これで次に説明するHTTP requestが無事認証され、通るようになります。
ただ自分がまだNode-RED完全理解していないのもあり、
HTTP requestノード内にhostを書く方法もあるんじゃないかと睨んでいます。
有識者募集。
OuraAPI発火(Get OuraAPI)
多分今回のピークです。
HTTP requestノードでOuraRing APIのURLを叩き、JSONオブジェクトを受信しています。
今回はHeartRateのみですが、他APIも同様の叩き方で受信できます。
工夫ポイントとしては、
出力形式をJSONオブジェクトとしている所でしょうか。
HTTPの返送がデフォルトでは文字列で飛んできますが、
ある程度JSONのフォーマットに則っていると
受信と同時にJSONオブジェクトに自動変換されて後々の処理がやりやすくなります。
データ処理(Get Last heartrate bpm)
最終的にUnityからアクセスして最新の心拍数を取得することが目的だったため、
データ処理で飛んでくる配列の一番最後の要素を抽出し、BPMだけを取得しています。
data.pop().bpmで配列最後(Oura上で最新)の
要素からbpmのみを抽出しmsg.payload(メッセージ全体の中身)をbpmに置き換えています。
msg.payload = msg.payload.data.pop().bpm; return msg;
Node-REDには各種データの中身をいじるためのChangeノードというものがありますが、
デフォルトのJSONのままでは直接数式を書くことができないなど
汎用性がちょっと低いかな?というものです。
今回は配列末尾を取るという目的が存在したため、
JavaScriptで使えるpop()を直手書きしました。
JSONata
Node-REDで使えるフォーマットとしてJSONata式というものがあるようです。
$で関数を宣言して使うもののようで、こちらを活用すれば
Changeノードで色々な計算も仕込めるようになるとのこと。
今回は一旦手書きpop()にしましたが、
本来配列末尾を取る時に一番簡単な方法は
array[array.length-1]
のような書き方になるはずなので、
今後の課題としてJSONata式の活用もやっていきたいなとは思います。
あ!それ、change ノードで JSONata タイプ指定できるのですが、それだと柔軟に計算もしこめます!
— Tanaka Seigo (@1ft_seabass) May 27, 2022
出力(http(200)・msg.payload)
API取得やデータ処理が全て終わった後に完成したデータをどこかに出力します。
HTTP responseノードはHTTP inノードを叩いた元に返送するものなので、
フローエディタで実行している間は基本的に出力できません。
デバッグ確認が不便になるため、
UnityでいうDebug.Log的なmsg.payloadを用意しました。
Debugノードを設置して、コンソールにも出力するようにしただけなので、
特段解説することはありません。
Debug用ノード各種(Catch・Complete等)
フロー左下にはエラーをキャッチした際にDebug出力を行うノード群を配置しています。
冒頭でも記載しましたが、将来的にenebularクラウドで実行するため、
HTTP responseが完了したらクラウド実行を停止するためのノードを配置しました。
クラウド実行環境についてはまた別に記事を書こうと思っています。
挙動確認
このフローで一番左上にあるタイムスタンプノードのボタンをクリックすると
Set Headerノードから順番に実行され、各出力ノードにたどり着きます。
挙動としては上の画像のようになります。
Get Last heartrate bpmで最新のBPMのみを出力しようとしているため
デバッグコンソールには作業当時の自分の心拍数である86が出力されています。
これでenebular上でNode-REDからOuraRing APIを叩き、
各種人体データが取得できるようになりました。
やったぜ。
最後に
今回の記事はenebularやNode-REDに注目し、
初学者目線でOuraRing APIを叩けるようになるところまでを書きました。
本来の目的というかゴールは更にここからUnityC#からenebularにアクセスし、
BPMなどの数字をUnity環境に持っていくことが最終ゴールですが、
あまりグダグダ書くのもよろしくないので、
今回は一旦ここまでとし、enebularクラウドとUnity間の通信に関してはまた別の記事にする予定です。
久々に完全新規に得た知識なので結構興奮状態で色々調べておりました。
今後もしばらくNode-REDについては勉強していきたいと思っています。
enebularの勉強会などもあるようなので、そちらも参加していきたいですね。
もしどこかで遭遇したら対戦よろしくお願いします。
ご視聴ありがとうございました。