はじめに
enebularのNode-Redから文字を入力しBOCCOを発話させてみました。
BOCCOとは、ユカイ工学から開発した家族をつなぐコミュニケーションロボットです。
BOCCOに話しかけると、それが音声メッセージとして送信され、スマートフォンからはテキストか音声でメッセージを送信することができます。
子供、両親、祖父母などのスマートフォンの操作ができない家族とも簡単にコミュニケーションをとることができる、そんなコミュケーションロボットです。また、温度センサや振動センサ、鍵センサとの連携もできます。
またBOCCOには開発者向けに、BOCCOに自由な言葉を発話させたり、BOCCOに話しかけられた発話データを取得したりすることができるようにAPIが公開されています。
そのAPIを利用することNode-RedからBOCCOを発話させることができるので、その方法について説明します。
意外と、APIをたたくときにcurlコマンドからNode-Redへの置き換えに詰まってしまいました。
なので、本記事はBOCCO API以外のAPIをNode-Redから叩くときに参考になるかと思います。
本記事執筆にあたって[@1ft_seabass](https://qiita.com/tseigo/lgtms)さんにとてもよく助けていただきました、ありがとうございます!!
BOCCO APIをつかう前準備
BOCCO APIを使うためには下記の準備が必要です。
- BOCCOを買う
- BOCCOアプリのユーザーアカウントをつくる
- BOCCO APIの利用申請を行い、APIキーを取得しておく
本記事のスコープ
本記事では、BOCCO APIでメッセージを送信する部分の実装を書きます。なので、APIのアクセストークンの取得やチャットルームの取得(発話させたいアプリ上のルームのuuid取得)は事前にcurlコマンドを通して行われているものとします。
curlコマンドからNode-Redの置き換えを考える
BOCCOにメッセージを送信するためには、curlコマンドで下記のようにします。
※access_toke-xxxxxxxxxには、「アクセストークンの取得」を参考にし取得したもの、room-id-xxxxxxxxxには、「チャットルームの取得」を参考にした文字列が入ります。
curl -i "https://api.bocco.me/alpha/rooms/room-id-xxxxxxxxx" \
-d "access_token=access_toke-xxxxxxxxx" \
-d "unique_id=`uuidgen`" \
-d "media=text" \
-d "text=こんにちはBOCCO!" \
-H "Accept-Language: ja"
ぱっとみて、NOde-Redに置き換える上で疑問に思った点は下記の通りです
- (おそらくPOSTだと考えていたが、)POSTなのかどうかよくわからん
- uuidgenにはいるUUID version 4のNode-Red上での生成方法
- -dや-HのペイロードをNode-Red上でどう表記するか
よくよく調べると、curlのオプションから
- -d : POSTリクエストとしてフォームを送信
- -H : HEADER HTTPヘッダにHEADERを追加もしくは変更
を意味するので、 上記のcurlはPOSTで良さそうだということがわかりました。
メッセージを送信するためのNode-Redのフロー
結果として、メッセージを送信するためのNode-Redのフローは下記のようになりました。
[{"id":"a511b8e0.9cce78","type":"tab","label":"フロー 2","disabled":false,"info":""},{"id":"a1bd5804.e816f8","type":"inject","z":"a511b8e0.9cce78","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":200,"wires":[["5d0ef47f.4601fc"]]},{"id":"5d0ef47f.4601fc","type":"change","z":"a511b8e0.9cce78","name":"curl -d 部分","rules":[{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.access_token","pt":"msg","to":"x2c4sjxacw4tq72q5h9vrsgjbcz3p55ixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","tot":"str"},{"t":"set","p":"payload.media","pt":"msg","to":"text","tot":"str"},{"t":"set","p":"payload.text","pt":"msg","to":"こんにちはBOCCO!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":200,"wires":[["fd594459.862968"]]},{"id":"1945a3cc.d39e4c","type":"change","z":"a511b8e0.9cce78","name":"curl -H 部分(ヘッダー)","rules":[{"t":"set","p":"headers.Accept-Language","pt":"msg","to":"ja","tot":"str"},{"t":"set","p":"headers.Content-Type","pt":"msg","to":"application/x-www-form-urlencoded","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":300,"wires":[["a41d5dc1.08334","e1875ceb.2a6d1"]]},{"id":"a41d5dc1.08334","type":"http request","z":"a511b8e0.9cce78","name":"","method":"POST","ret":"txt","paytoqs":true,"url":"https://api.bocco.me/alpha/rooms/E7607BA3-2AA0-4DEB-8959-XXXXXXXXXXXX/messages","tls":"","persist":false,"proxy":"","authType":"","x":550,"y":300,"wires":[["301fb11b.5e036e"]]},{"id":"301fb11b.5e036e","type":"debug","z":"a511b8e0.9cce78","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":300,"wires":[]},{"id":"e1875ceb.2a6d1","type":"debug","z":"a511b8e0.9cce78","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":380,"wires":[]},{"id":"fd594459.862968","type":"uuid","z":"a511b8e0.9cce78","uuidVersion":"v4","namespaceType":"","namespace":"","namespaceCustom":"","name":"payload.unique_id に uuid を付与","field":"payload.unique_id","fieldType":"msg","x":640,"y":200,"wires":[["1945a3cc.d39e4c"]]}]
一つ一つみていきます。
injectノード(タイムスタンプ)
injectノード(タイムスタンプ)が、このフローの開始ボタンとなっています。
この左四角ボタンをおすとフローが開始されます。
注意する点としては、タイムスタンプは日時のペイロードをもち、次のノードへと流れていきます。
changeノード(curl -d 部分)
次のchangeノードではcurlコマンドでいう-dの部分を記述する役割を持っています。
changeノードは上から下へ処理が降りていきます。
なので最初の。msg.payloadに対して{}と代入しているのは、先ほどのinjectノード(タイムスタンプ)の日時のペイロードを初期化するためです。それ以下のpayloadはcurlコマンドの要素をそれぞれ記載しています。
「msg.payload.~~~」と記述します。
uuidノード(payload.unique_idにuuidを付与)
これはuuidを生成するノードです。
「パレットの管理」→「ノードを追加」で「uuid」と検索すると、「node-red-contrib-uuid」と出てくるので追加して使います。
こちらはcurlコマンドでいう「"unique_id=uuidgen
"」に対応している部分となります。
changeノード(curl -H)
こちらのノードにはcurlコマンドで「"Accept-Language: ja"」に対応します。
そして、このノードにはもう一つ重要なmsg.headerが入ります。
それが画像に記載のある「msg.headers.Content-Type」とその値「application/x-www-form-urlencoded」です。
これは盲点というか、全く未知のことでしたが、[@1ft_seabass](https://qiita.com/tseigo/lgtms)さんにご指導いただき理解しました。
Node-Redではもともとjsonで送るようContent-Typeが「application/json」で指定されているようです。
なので、どのContent-Typeでおくるのか意識して記述する必要があります。
※調べていたら同じようなことで、詰まっていたかがいらっしゃいました。Node-REDでA3RT APIを使うときのハマりどころ
http requestノード
こちらのノードでは「POST」を指定してURL部分はcurlコマンドで記述したURLを入れます。
動作確認
先ほどのノードを動かしてみると...喋った!!!!!!!BOCCCOがenebularのNode-Redから喋った!!!!!!!!
curlコマンド上のtextを変更すれば送信するメッセージを自由にかえることができます。
enebularのNode-RedからBOCCOを発話させてみました🤔
— たくろーどん (@takudooon) December 2, 2020
http requestのContentTypeの設定が肝だった、なるほどなぁ。@1ft_seabass さんにめちゃくちゃ教えてもらいました🙏🙏🙏#enebular #NodeRed pic.twitter.com/vmjQR4iq1n
応用編:UIからテキストを入力してBOCCOを発話させる
全体のフローは下記の通りです。
先ほどのinjectノード(タイムスタンプ)部分がtext inputノードに置きえています。
主に変更が必要な3点のノードについて下記で解説します。
[{"id":"fbe90451.f204e8","type":"tab","label":"フロー 2","disabled":false,"info":""},{"id":"f22ca949.cbcff8","type":"comment","z":"fbe90451.f204e8","name":"メッセージ送信","info":"","x":100,"y":80,"wires":[]},{"id":"c50ee121.d83cf","type":"change","z":"fbe90451.f204e8","name":"curl -H 部分","rules":[{"t":"set","p":"headers.Accept-Language","pt":"msg","to":"ja","tot":"str"},{"t":"set","p":"headers.Content-Type","pt":"msg","to":"application/x-www-form-urlencoded","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":280,"wires":[["1646e643.b3242a","7d781da3.478604"]]},{"id":"1646e643.b3242a","type":"http request","z":"fbe90451.f204e8","name":"","method":"POST","ret":"txt","paytoqs":true,"url":"https://api.bocco.me/alpha/rooms/xxxxxxxx/messages","tls":"","persist":false,"proxy":"","authType":"","x":310,"y":340,"wires":[["ba5cd2d2.6f1b5"]]},{"id":"ba5cd2d2.6f1b5","type":"debug","z":"fbe90451.f204e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":510,"y":340,"wires":[]},{"id":"7d781da3.478604","type":"debug","z":"fbe90451.f204e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":690,"y":280,"wires":[]},{"id":"d85b093f.57a428","type":"ui_text_input","z":"fbe90451.f204e8","name":"","label":"","tooltip":"","group":"f11ebb11.fd1b88","order":4,"width":0,"height":0,"passthru":true,"mode":"text","delay":"300","topic":"","x":80,"y":220,"wires":[["db7bd901.751608","99c90f9c.edaba"]]},{"id":"c21ab3a0.6b448","type":"debug","z":"fbe90451.f204e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.text","targetType":"msg","x":490,"y":140,"wires":[]},{"id":"99c90f9c.edaba","type":"change","z":"fbe90451.f204e8","name":"msg.payload → msg.payload.text","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.text","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":220,"wires":[["c21ab3a0.6b448","18258676.518dea"]]},{"id":"db7bd901.751608","type":"debug","z":"fbe90451.f204e8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":270,"y":140,"wires":[]},{"id":"320fc2e2.485a5e","type":"change","z":"fbe90451.f204e8","name":"curl -d 部分","rules":[{"t":"set","p":"payload.access_token","pt":"msg","to":"xxxxxxxxxxx","tot":"str"},{"t":"set","p":"payload.media","pt":"msg","to":"text","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":270,"y":280,"wires":[["c50ee121.d83cf"]]},{"id":"18258676.518dea","type":"uuid","z":"fbe90451.f204e8","uuidVersion":"v4","namespaceType":"","namespace":"","namespaceCustom":"","name":"payload.unique_id に uuid を付与","field":"payload.unique_id","fieldType":"msg","x":620,"y":220,"wires":[["320fc2e2.485a5e"]]},{"id":"f11ebb11.fd1b88","type":"ui_group","z":"","name":"text","tab":"a7ecc75a.8f0de8","order":1,"disp":true,"width":"6","collapse":false},{"id":"a7ecc75a.8f0de8","type":"ui_tab","z":"","name":"home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
text inputノード
こちらにはテキストを入力するためのノードになります。
入力されたテキストはmsg.payloadに格納されます。
またこちらノードはダッシュボードに追加しておきます。
text inputノードのプロパティから「Group」→「新規にui_groupに追加」でぽちぽち進めていきましょう。
そうすると、右のダッシュボード画面の「タブ&リンク」にui_groupが追加されます。
changeノード(msg.payload → msg.payload.text)
先ほどのtext inputノードで入力されたテキストはmsg.payloadに格納されます。
なので、curlコマンドで渡されるように、msg.payload.textに変換して次のノードに渡します。
この時、「値の移動」を使って変換します。
changeノードの使い方はこちらの記事がわかりやすかったです。
changeノード(curl -d 部分)
こちらのノードは下記のように設定します。
今回は初期化する必要はないのでmsg.payloadに{}は代入しません。
また、msg.payload.textはinput textノードから流れてくるので、ここには記述しません。
動かしてみる
デプロイし、UIを表示させ、テキストを入力するとBOCCOが発話してくれます。
おわりに
今回はenebularのNode-Redを使ってBOCCOを発話させてみました。
curlコマンドで記述されたAPIのPOSTをNode-Redに置き換える方法を理解することができました。
Node-Redのノードごとに流れていくmsg.payloadの動きを、ちゃんとdebugノードでデバッグメッセージで表示させて、確認することが重要だと感じました。
これで他のAPIもNode-Redから叩くことができそうです。