#Eagle Eye Networks APIで簡単ビデオ連携 その1 静止画取得
昨今色々と話題の豊富なクラウド型の**動画管理システム(VMS)**ですが、あまり連携事例も情報量が無く、Qiitaでもあまり例が無さそうですので、ここで「Eagle Eye Networks」(以下EEN)という完全クラウド型のVMSを使用した連携例を紹介してみたいと思います。
##EENで静止画を取得できると何かいいのか??
今までのオンプレ型のVMSでも、静止画のエクスポートを行えばシステム間連携はできるし、保存もできます。じゃあなんでEENで取得して連携するのか?それは端的に言えば「元々クラウドにデータがあれば、クラウドシステムとの連携って簡単でしょ?」ということです。しかもリトリーブするために新たにシステムを組むなど、非生産的なことをせずに簡単にRESTで取得できる、ということで一番のメリットだと思います。
この仕組を使えば、
- 入退室管理のタイムスタンプだけで、簡単にその時の静止画が取れる
- 動画だと回線的に厳しい環境でも、静止画の連続gifならなんとかなるようなもの
にも簡単にシステムが適用できます。
##環境
EENはIPカメラとそれに電源を供給しながら通信を行うPoEスイッチ、そしてEENのクラウドデータセンターと接続するためのブリッジという構成で実装します。
EENが実際にどのように動作するかは以下のサイトを御覧ください。
EEN製品サイト(日本語)
またEENのAPIについてはネットで公開されているので、こちらもご参照下さい(全然途中までですが)。
EEN APIサイト(日本語)
必要なもの
- EENのアカウント
- EENに対応しているIPカメラ
- PoEスイッチ
- IBM Bluemixのアカウント
- Bluemix上のnode.jsのランタイム
- 上記ランタイム上で動くNode-RED
- node-red-contrib-json(package.jsonで事前に読み込みすること)
前提条件
- EENのサブスクリプション契約があること
試用版については上記サイトを参照 - Bluemixのサブスクリプション契約があること
試用版についてはBluemixのサイトを参照
##事前確認
実際に接続を行う前に、2点確認事項があります。
まずは、
- API連携を行うユーザーID、パスワードを確認
- EENのWeb画面でカメラが認識されていることを確認
##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"]]}]
インポートが完了すると、それぞれ下記のように表示されるはずです。
##ノードのカスタマイズ
このままではユーザーID、パスワードが使えませんので、それぞれの内容を定義しているノードに記載します。
先ほどインポートした「Get Auth Head」というノードを開いて下さい。
Functionの編集モードになるので、、の部分をそれぞれご自身のユーザーID、パスワードに変更し下さい。
##繋げてみよう
これで準備は完了です。
実際にURLにアクセスして確認してみましょう。
接続先のURLは、
です。
この仕組はカメラ名が必要ですので、
?camName=カメラ名
というパラメータを上記のURLに付加します。最終的には、
となります。
このURLでアクセスを行うと、以下のような静止画が表示されます。
##最後に
いかがでしたか?かなり簡単に静止画を取得できたかと思います。
今回はビジュアル的にわかりやすくするためにBluemix + Node-REDを使っているので多少冗長なところがあったかと思いますが、素直にnode.jsで書いてしまえばもっと行数も少なくいけたかと思います。
EENはこんな感じで静止画や動画を簡単に取得できるので、色々と連携可能な仕組みができると思います。
次回は動画の取得!です。その後は動画の保存なんかも書いてみたいと思います(OKであれば)。
##ライセンス、著作権等
本スクリプト、ノード構成、エクスポート内容についてはMIT Licenseを適用します。
Eagle Eye Networksの商標およびロゴについてはEagle Eye Networks, Inc.に帰属します。
IBMおよびIBM Bluemix、Node-Redの商標およびロゴについてはInternational Business Machines Corporationに帰属します。
node.jsの商標およびロゴについてはNode.js contributorsに帰属します。