IoT機器(センサーや家電機器、エネルギー機器など)は様々な通信規約(プロトコル)で通信を行っていますが、PicoGWを用いることで一括してWebAPIから制御できるようになります。PicoGWが提供するWebAPIは以下の三種類です。
- REST
昔からある伝統的なWeb APIで、ブラウザがWebサーバからHPの情報をロードする場合にも基本的にはこの方式でアクセスします。「リソース」と呼ばれる情報のかたまりに対して、「メソッド」と呼ばれるアクセス方式でリクエストを投げるとサーバーは応答し、処理が終われば接続は切断されます。リソースにはIDが割り振られています。WebサーバへのアクセスであればリソースIDはURLの中のパス名であり、メソッドはGETやPOSTなどのいわゆるHTTPメソッド名となります。GETメソッドであればブラウザのURLフィールドにパスを書けばアクセスできますし、それ以外のメソッドも通常はブラウザ内で動作するJavaScriptで簡単に使えるのでよく使われています。また、curlというよくできたツールがあり、テストやデバッグも簡単にできるので、最も普及した使いやすいWebAPIスタイルと言ってよいでしょう。PicoGW APIもRESTに範をとり、リソース名+アクセスメソッドという形でアクセスします。
ただ、HTTP RESTは呼び出し元からリクエストがあった時のみ応答し毎回接続が切れるので、機器側からタイミングよく通知を送るようなことはできません。例えばエアコンのリモコンアプリを作ったとして、電源が入ったらそのタイミングで表示を変えたかったとしても、RESTの場合はリモコンアプリから問い合わせしなければ現在の電源状態はわかりませんので、頻繁に問い合わせをしなければなりません。簡単さと引き換えに、パフォーマンスを犠牲にしているといっていいでしょう。
- WebSocket
リアルタイムの通知を扱うにはサーバーとクライアントを常に接続しておけばよいので、RESTからかなり遅れてWebSocketというものがブラウザに実装されました。ネット上で通信を行うにはよくソケットと呼ばれる方式を使いますが、WebSocketはそのブラウザ版です。通常のソケットの接続後にハンドシェイクという手続きが追加されているだけで、そのあとは通常のソケットと同じく、双方向に通信ができます。リアルタイム性が要求されるネットゲームなどでもWebSocketはよく使われています。流す情報の内容に制約はないので、相互に送りあう情報の表現に関する取り決めを行っておく必要があります。PicoGWでは、文字列化したJSONオブジェクトを送りあうことにしています。内容については後述します。
- MQTT
MQTTはIoTに適した軽量通信方式で、TCPをベースにしています。PubSubのみで通信を行うモデルになっています。
ブラウザから直接使うことはできませんが、WebSocketで接続可能なものもあります。
PicoGWでの3プロトコルの呼び出し
これら3プロトコルは全く異なるものではありますが、PicoGWでは情報の内容(ペイロード)が極力同じようになるようにしています。これには、使い心地やドキュメンテーションを改善させるだけでなく、プラグインの実装をAPIごとに切り替えるようなことは避けたいという理由もあります。
どのプロトコルであれ、基本的にはJSONオブジェクトをやり取りすると思ってください。このJSONオブジェクトのフォーマットは、クライアントからのサーバーへのリクエストに関しては統一化されています(後述:レスポンスはプラグインごとに自由に返されることになっています)。
{ "method": "GET, PUTなどメソッド名の文字列", "path": "アクセスする情報のパス文字列。REST的に言えばリソース名", "args": {呼び出しが必要とする引数のJSONオブジェクト。場合により省略可}, "timeout": 結果の返答がない場合にあきらめる時間(ミリ秒)を数値で。省略可。デフォルトは60000 (1分) }
例えば、ECHOENT Lite機器の電源をONにするリクエストは次のようになります。
{ "method": "PUT", "path": "/v1/echonet/airconditioner_1/operationstate", "args": {"value":"on"} }
これを、REST, WebSocket, MQTTでどのように送信するかを以下に示します。
- RESTの場合
HTTP RESTで送る場合は、methodとpathはリクエストラインという形でプロトコルに含まれます。
従って、pathに対してPUTし、その時のメッセージ本体にargs文字列として入れて送信してください。
このとき、bodyがJSONなので、リクエスト時のヘッダとして”Content-Type: application/json”を入れてください。
curl的に言えば次のようになります。(PicoGWはlocalhostで走っているものとします)
$ curl -X PUT -H "Content-Type: application/json" -d '{"value":"on"}' http://localhost:8080/v1/echonet/controller_2/operatingstate
- WebSocketの場合
WebSocketは、個々のAPIコールをする前にPicoGWへの接続を確立しておく必要があります。これには、http://[PicoGW IP]:8080 に普通にWebSocketで接続しに行けばよいです。
APIコールをするには上記JSONをそのまま文字列化(JavaScript的に言えばstringify())して送ればよいのですが、常に接続が維持されそのうえで複数のリクエストを処理するようになっているので、あるレスポンスが返ってきたときに、どのリクエストに対する返答かがわからなくなる可能性があります。
これを解決するために、リクエスト時に送信するJSONオブジェクトにtidというメンバーを加えることができます。これには任意の数値を与えることができます。これを与えておくと、レスポンスにもtidというメンバーが含まれ、対応するリクエストでの値がコピーされて返ってきます。リクエストを区別できるような値を指定して送ってください。レスポンスに関心がない場合は、指定しなくても構いません。
{ "method": "PUT", "path": "/v1/echonet/airconditioner_1/operationstate", "args": {"value":"on"}, "tid": 1234 }
- MQTTの場合
MQTTはブローカーを中心としたスター型のネットワーク構成となります。PicoGWにはブローカー機能はなくクライアントなので、あらかじめどこかにMQTTブローカーを立てておき、PicoGWから接続しに行く必要があります。接続方法は以下の通りです。
- ブラウザでPicoGWのフロントエンド(http://[PicoGW IP]:8080/というURL)を開く
- 表示されるツリー構造の中で /v1/adminの上で右クリック⇒Settingsで設定画面を出す
- 真ん中あたりの「MQTT」という項目の中のfalseをtrueにし、MQTT broker addressを指定し、必要に応じてQoSを設定したうえで、右下のAcceptボタンを押す。
これでPicoGWがブローカーに接続します。接続状況はPicoGWのコマンドラインに表示されるので確認してください。この時、PicoGWは固有のトピックをSubscribeします。このトピック名は、PicoGWのID (通常はMAC address)に-reqという文字列を足したものになります。ここに使われるMAC addressは /v1/admin/id/ にGETすることで取得できます。GETなのでPicoGWのフロントエンドからも簡単に得ることができます。もしこのIDが11:22:33:44:55:66であれば、PicoGWはブローカーの11:22:33:44:55:66-reqというトピックをSubscribeすることになるわけです。
次に、MQTT経由でPicoGWのAPIを呼び出したい側(クライアント)でどうするかについて述べます。こちらでは、ブローカーに接続したのちに、あらかじめPicoGWからの返答を受け取るためのトピックにSubscribeしておきます。このトピックはPicoGWのID名(MAC address)そのものになります。つまり、上記の例であれば、11:22:33:44:55:66という名前のトピックをSubscribeしておきます。
こうしておいて、PicoGWがSubscribeしている11:22:33:44:55:66-reqに対して、リクエストのJSONオブジェクトを文字列化して送る(Publishする)と、PicoGWが11:22:33:44:55:66に結果をPublishしてくるので、それを受け取ることでAPI呼び出しができるようになるわけです。
まとめると、MQTTの場合は、クライアントからPicoGWへのリクエストを送る場合は[PicoGW ID(MAC)]-reqという名前のトピックにJSON ObjectをPublishし、その結果は[PicoGW ID(MAC)]という名前のトピックで受け取れるというわけです。
メッセージの対応をとるためのtidというメンバーを追加しておくと、返答にもその情報を含めてくれるのは、WebSocket通信と同じです。
APIコールへの返答について
返答は各プラグインが自由に決めるので、JSONオブジェクトであるという以外は特に統一されていません。ただし、呼び出しが成功した場合は”success”というメンバー(値はtrue)を、失敗の場合は”errors”という、エラーオブジェクトの配列を値に持つものを極力含めることを心掛けているので、呼び出し失敗についてはこれで調べてください。