Eagle Eye Networks APIで簡単ビデオ連携 その1 静止画取得

#Eagle Eye Networks APIで簡単ビデオ連携 その1 静止画取得

昨今色々と話題の豊富なクラウド型の**動画管理システム（VMS）**ですが、あまり連携事例も情報量が無く、Qiitaでもあまり例が無さそうですので、ここで[「Eagle Eye Networks」](http://www.eagleeyenetworks.com/)(以下EEN)という完全クラウド型のVMSを使用した連携例を紹介してみたいと思います。

##EENで静止画を取得できると何かいいのか？？
今までのオンプレ型のVMSでも、静止画のエクスポートを行えばシステム間連携はできるし、保存もできます。じゃあなんでEENで取得して連携するのか？それは端的に言えば「元々クラウドにデータがあれば、クラウドシステムとの連携って簡単でしょ？」ということです。しかもリトリーブするために新たにシステムを組むなど、非生産的なことをせずに簡単にRESTで取得できる、ということで一番のメリットだと思います。
この仕組を使えば、

- 入退室管理のタイムスタンプだけで、簡単にその時の静止画が取れる
- 動画だと回線的に厳しい環境でも、静止画の連続gifならなんとかなるようなもの

にも簡単にシステムが適用できます。

##環境
EENはIPカメラとそれに電源を供給しながら通信を行うPoEスイッチ、そしてEENのクラウドデータセンターと接続するためのブリッジという構成で実装します。
EENが実際にどのように動作するかは以下のサイトを御覧ください。
[EEN製品サイト（日本語）](http://www.iguazu-eagleeye.jp/)

またEENのAPIについてはネットで公開されているので、こちらもご参照下さい（全然途中までですが）。
[EEN APIサイト（日本語）](http://een-api-doc-jp.mybluemix.net/)

**必要なもの**
- EENのアカウント
- EENに対応しているIPカメラ
- PoEスイッチ
- IBM Bluemixのアカウント
- Bluemix上のnode.jsのランタイム
- 上記ランタイム上で動くNode-RED
- node-red-contrib-json（package.jsonで事前に読み込みすること）

**前提条件**
- EENのサブスクリプション契約があること
　試用版については上記サイトを参照
- Bluemixのサブスクリプション契約があること
  　試用版についてはBluemixのサイトを参照

##事前確認
実際に接続を行う前に、2点確認事項があります。
まずは、

1. API連携を行うユーザーID、パスワードを確認
2. EENのWeb画面でカメラが認識されていることを確認

以下のような感じでWeb画面に表示されていればOKです。
![Layout.png](https://qiita-image-store.s3.amazonaws.com/0/106468/78791837-b388-ae63-bd17-e43cecbb0535.png)


##Node-REDでインポート

下記のJSON DocをデプロイしたNode-RED上にインポートしてみてください。

- 認証、認可用ノード

```
[{"id":"977ab801.29e488","type":"inject","z":"ba43e00b.410be","name":"","topic":"","payload":"","payloadType":"str","repeat":"3600","crontab":"","once":true,"x":110,"y":400,"wires":[["dc2123d7.9639d"]]},{"id":"dc2123d7.9639d","type":"function","z":"ba43e00b.410be","name":"Get Auth Head","func":"var user = \"<User ID>\";\nvar password = \"<Password>\";\nmsg.payload = 'username=' + user + '&password=' + password;\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":400,"wires":[["a175b66d.8cb478"]]},{"id":"a175b66d.8cb478","type":"http request","z":"ba43e00b.410be","name":"","method":"POST","ret":"obj","url":"https://login.eagleeyenetworks.com/g/aaa/authenticate","x":330,"y":460,"wires":[["df64b846.5dc978"]]},{"id":"df64b846.5dc978","type":"http request","z":"ba43e00b.410be","name":"","method":"POST","ret":"obj","url":"https://login.eagleeyenetworks.com/g/aaa/authorize","x":330,"y":520,"wires":[["73b7e889.bc36f8"]]},{"id":"73b7e889.bc36f8","type":"contrib-json","z":"ba43e00b.410be","engine":"JSONPath","command":"jq","expr":"$.headers.set-cookie[1]","complete":"complete","prop":"payload","name":"Put Cookie","x":650,"y":400,"wires":[["e994bf64.d51cb"]]},{"id":"e994bf64.d51cb","type":"function","z":"ba43e00b.410be","name":"Form Auth Key2","func":"var cookies = msg.payload[0].split(\"; \");\nfor (var i = 0; i < cookies.length; i++) {\n\tvar str = cookies[i].split(\"=\");\n\tif (str[0] === \"auth_key\") {\n\t\tglobal.set(\"auth_key2\", unescape(str[1]));\n\t\tbreak;\n\t}\n}\nmsg.payload = global.get(\"auth_key2\");\nreturn msg;","outputs":1,"noerr":0,"x":660,"y":460,"wires":[["cf84a0d5.fefc5"]]},{"id":"173b1941.36c997","type":"comment","z":"ba43e00b.410be","name":"Generate EEN Auth Key loop #2","info":"","x":190,"y":340,"wires":[]},{"id":"cf84a0d5.fefc5","type":"debug","z":"ba43e00b.410be","name":"","active":false,"console":"false","complete":"false","x":650,"y":520,"wires":[]}]
```


- 静止画表示用ノード

```
[{"id":"7bddddbf.fd52a4","type":"http in","z":"13af9bb0.48d9b4","name":"GetLivePreview","url":"/eengetprev","method":"get","swaggerDoc":"","x":140,"y":620,"wires":[["ecd24ef4.d8883"]]},{"id":"fbe621e.5f684e","type":"change","z":"13af9bb0.48d9b4","name":"Restore Header","rules":[{"t":"delete","p":"headers","pt":"msg"},{"t":"set","p":"headers","pt":"msg","to":"orgheaders","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":740,"wires":[["9b4dd731.f54048"]]},{"id":"9b4dd731.f54048","type":"http response","z":"13af9bb0.48d9b4","name":"","x":890,"y":740,"wires":[]},{"id":"6df3c0.309e5c4","type":"comment","z":"13af9bb0.48d9b4","name":"Get EEN Live Preview","info":"### Usage\nhttp://<My Bluemix Site>.mybluemix.net/eengetprev?camName=<Camera Name>&test=<0 or 1>\n\n- camName\n    - Camera Display Name\n\n- test\n  If 'test' parameter specify to '1', Live video is not shown and return only 'Return code'.\n  This parameter is option .\n\n### Example\nhttp://<My Bluemix Site>.mybluemix.net/eengetprev?camName=EN-CDUM-002a&test=1\n","x":160,"y":560,"wires":[]},{"id":"ecd24ef4.d8883","type":"function","z":"13af9bb0.48d9b4","name":"Get Auth & Set EENTS","func":"msg.orgheaders=msg.req.headers;\n\nif (msg.payload.auth_key) {\n    msg.auth_key = msg.payload.auth_key;\n} else {\n    msg.auth_key = global.get(\"auth_key2\");\n}\n\nif (msg.payload.camName) {\n    msg.camName = msg.payload.camName;\n}\n\nif (msg.payload.test) {\n    msg.test = msg.payload.test;\n}\n\nvar st_UTC_do = new Date();\nmsg.st_UTC = [st_UTC_do.getFullYear(),\n                ( '0' + (st_UTC_do.getMonth() + 1)).slice(-2),\n                ( '0' + st_UTC_do.getDate() ).slice(-2),\n                ( '0' + st_UTC_do.getHours() ).slice( -2 ),\n                ( '0' + st_UTC_do.getMinutes() ).slice( -2 ),\n                ( '0' + st_UTC_do.getSeconds() ).slice( -2 )\n                ].join(\"\");\n\nvar st_JST_do = new Date(st_UTC_do.getTime() + 32400000);\nmsg.st_JST = [st_JST_do.getFullYear(),\n                ( '0' + (st_JST_do.getMonth() + 1)).slice(-2),\n                ( '0' + st_JST_do.getDate() ).slice(-2),\n                ( '0' + st_JST_do.getHours() ).slice( -2 ),\n                ( '0' + st_JST_do.getMinutes() ).slice( -2 ),\n                ( '0' + st_JST_do.getSeconds() ).slice( -2 )\n                ].join(\"\");\n\nreturn msg;","outputs":1,"noerr":0,"x":390,"y":620,"wires":[["c9075a91.0664e8"]]},{"id":"c9075a91.0664e8","type":"http request","z":"13af9bb0.48d9b4","name":"Get Camera ID","method":"GET","ret":"obj","url":"https://login.eagleeyenetworks.com/g/device/list?A={{auth_key}}&t=camera","tls":"","x":360,"y":680,"wires":[["29278254.814d5e"]]},{"id":"29278254.814d5e","type":"function","z":"13af9bb0.48d9b4","name":"Pull CamID","func":"var camLength = msg.payload.length;\n\nfor (itr = 0; itr < camLength; itr++){\n    var camIdx = msg.payload[itr].indexOf(msg.camName);\n    if ( camIdx > 0 ) {\n        msg.camID = msg.payload[itr][camIdx - 1];\n    }\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":350,"y":740,"wires":[["e016f2f3.68a15"]]},{"id":"e016f2f3.68a15","type":"http request","z":"13af9bb0.48d9b4","name":"Get Imge","method":"GET","ret":"bin","url":"https://login.eagleeyenetworks.com/asset/after/image.jpeg?timestamp={{st_UTC}}.000;id={{camID}};A={{auth_key}};asset_class=pre","x":660,"y":620,"wires":[["d875e65e.8fbf48"]]},{"id":"d875e65e.8fbf48","type":"function","z":"13af9bb0.48d9b4","name":"Set Img File Name","func":"msg.jpgfilename = msg.camID + \"_\" + msg.st_JST + \".jpg\";\nmsg.orgheaders['Content-Disposition'] = \"inline; filename=\" + msg.jpgfilename;\nmsg.orgheaders['Content-Type'] = \"image/jpeg\";\n\nreturn msg;","outputs":1,"noerr":0,"x":690,"y":680,"wires":[["fbe621e.5f684e"]]}]
```

インポートが完了すると、それぞれ下記のように表示されるはずです。
![auth.png](https://qiita-image-store.s3.amazonaws.com/0/106468/0276843b-3bf8-dd15-27b7-1509d7a37843.png)
![prev.png](https://qiita-image-store.s3.amazonaws.com/0/106468/974c7991-0e94-c6e6-e904-9c43eb919e54.png)


##ノードのカスタマイズ
このままではユーザーID、パスワードが使えませんので、それぞれの内容を定義しているノードに記載します。

先ほどインポートした「Get Auth Head」というノードを開いて下さい。
![auth2.png](https://qiita-image-store.s3.amazonaws.com/0/106468/a44ad1cd-c6b0-3ff7-90ef-dbc7524f829c.png)

Functionの編集モードになるので、<User ID>、<Password>の部分をそれぞれご自身のユーザーID、パスワードに変更し下さい。
![editfunc.png](https://qiita-image-store.s3.amazonaws.com/0/106468/6811a9dc-569d-08c8-1749-2a9a16775447.png)

##繋げてみよう
これで準備は完了です。
実際にURLにアクセスして確認してみましょう。
接続先のURLは、

http://Bluemixアプリ名+myblemix.net等/eengetprev

です。

この仕組はカメラ名が必要ですので、
?camName=カメラ名
というパラメータを上記のURLに付加します。最終的には、

http://Bluemixアプリ名+myblemix.net等/eengetprev?camName=カメラ名

となります。
このURLでアクセスを行うと、以下のような静止画が表示されます。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/106468/e1bfbb75-0493-c2c8-0d62-5d19f6647905.png)

##最後に

いかがでしたか？かなり簡単に静止画を取得できたかと思います。
今回はビジュアル的にわかりやすくするためにBluemix + Node-REDを使っているので多少冗長なところがあったかと思いますが、素直にnode.jsで書いてしまえばもっと行数も少なくいけたかと思います。
EENはこんな感じで静止画や動画を簡単に取得できるので、色々と連携可能な仕組みができると思います。
[次回](http://qiita.com/c-nova/items/16cff2e933be9ff9adbe)は動画の取得！です。その後は動画の保存なんかも書いてみたいと思います（OKであれば）。

##ライセンス、著作権等

本スクリプト、ノード構成、エクスポート内容についてはMIT Licenseを適用します。
Eagle Eye Networksの商標およびロゴについてはEagle Eye Networks, Inc.に帰属します。
IBMおよびIBM Bluemix、Node-Redの商標およびロゴについてはInternational Business Machines Corporationに帰属します。
node.jsの商標およびロゴについてはNode.js contributorsに帰属します。