Eagle Eye Networks APIで簡単ビデオ連携 その3 URLによる動画連携

#Eagle Eye Networks APIで簡単ビデオ連携 その3 URLによる動画連携

　[前回の動画取得](http://qiita.com/c-nova/items/16cff2e933be9ff9adbe)と[前々回の静止画取得](http://qiita.com/c-nova/items/6da5482019b9ca7b7f62)では「静止画や動画を、要求するアプリまで持ってくる」事が目的でしたので、手元にファイルそのものをダウンロードするということがゴールになっていました。
　しかし、現実的な動画連携の仕組みではダウンロードももちろん重要ですが、実はエンドユーザーから見ると「動画の閲覧」自体が重要であり目的となるため、ストリーミングやその他のライトな手段で目的を達成できることが望まれます。
　そこで今回はEagle Eyeでは最も簡単な動画、静止画連携手段である「URL文字列操作による動画連携」について紹介したいと思います。この方法はEagle Eyeの中でもモーション検知によるアラートEメール送信時にも使用される方法ですので、最も使用されている連携機能と言っても過言ではないでしょう。本機能の詳細については、以下のURLをご参照ください。
[Links for Historic Video](https://www.eagleeyenetworks.com/links-historic-video/)

##EENで動画連携できると何かいいのか？？

　動画連携は[動画取得](http://qiita.com/c-nova/items/16cff2e933be9ff9adbe)とは異なり動画そのものを取得するわけではありませんので、例えば画像解析や長期保存などには向きません。そのため、よりリアルタイム性が求められ物や、利用者が多い（Eagle Eyeの仕組みを使うため外部公開は難しくても、大人数がいる社内とかではOKでしょう）利用ケースに向いていると考えられます。
　また、使用するURIはカメラのESNとタイムスタンプという極めてシンプルなものですので、動画連携を想定していなかったアプリなどで試しに連携してみるのもありです。

- 入退室管理でアラートが発生した際に、すぐに何があったのか、今何が起こっているのかを見ることができる
- 装置の異常状態をセンサーから受け取ったときに、センサーのタイムスタンプを利用して動画をすぐに見ることができる
- 動画取得と異なり、Eagle Eyeの仕組みがそのまま使えるため、動画閲覧のための閲覧権限をアプリで作り込むことが不要

など、用途を限定すれば素晴らしい効果を発揮します。


##環境、事前確認
大元の環境条件や事前確認項目は[前々回](http://qiita.com/c-nova/items/6da5482019b9ca7b7f62#%E7%92%B0%E5%A2%83)と同様ですので、<-のリンク先を参照して下さい。

今回はCloudantに格納されたIPカメラからの人数カウントデータを使用する想定になっておりますので、BOSCHの7000番以降のIPカメラをお持ちでない場合には、以下のようなダミーデータを作成し、countというデータベースに格納してください。

```
{ "timestamp": "2016-11-08T03:50:18Z", "count": 1 }
```
時刻はUTCで、カウントデータは1で固定です。
上記データを作成後、Search Indexとして「**onecount**」というDesign Docに「**newest**」という名前でIndexを作成します。

```
function (doc) {
  index("timestamp", doc.timestamp, { "store": true });
}
```

BOSCHの7000番台以降のカメラによる人数カウントは、次回ご紹介する予定です！

EENの機能の詳細、APIについても前回同様以下のサイトを御覧ください。
[EEN製品サイト（日本語）](http://www.iguazu-eagleeye.jp/)
[EEN APIサイト（日本語）](http://een-api-doc-jp.mybluemix.net/)


##Node-REDでインポート

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

- 認証、認可用ノード
今回はEagle Eyeの認証認可はBluemix内では使用せず、実際にはWeb画面を開く時に手動でユーザーID、パスワードを入力します。

- 人数カウント用ノード
これはBOSCHのカメラで人数カウントのデータを収集するための仕組みです。
BOSCHのIPカメラをお持ちでない方は無視して頂いて問題ありません。

```
[{"id":"1140d0f2.fed24f","type":"http in","z":"9e258e12.c6e77","name":"Put Count","url":"/pcount","method":"get","swaggerDoc":"","x":120,"y":740,"wires":[["cee68b39.747e68","cb773983.1f10a8"]]},{"id":"cee68b39.747e68","type":"debug","z":"9e258e12.c6e77","name":"","active":true,"console":"false","complete":"payload","x":330,"y":800,"wires":[]},{"id":"4f74e92c.388878","type":"http response","z":"9e258e12.c6e77","name":"","x":830,"y":740,"wires":[]},{"id":"cb773983.1f10a8","type":"function","z":"9e258e12.c6e77","name":"Set Number","func":"msg.payload = 1;\n\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":740,"wires":[["8483eb0f.fe0888","5fc0a9cf.794c68"]]},{"id":"c8e04426.05d018","type":"cloudant out","z":"9e258e12.c6e77","name":"","cloudant":"","database":"count","service":"XXXXX-cloudantNoSQLDB","payonly":true,"operation":"insert","x":830,"y":800,"wires":[]},{"id":"8483eb0f.fe0888","type":"function","z":"9e258e12.c6e77","name":"Set time and count","func":"var dtObj = new Date();\n\nvar datetime = [dtObj.getFullYear() + '-',\n    ( '0' + (dtObj.getMonth() + 1)).slice(-2) + '-',\n    ( '0' + dtObj.getDate() ).slice(-2) + 'T',\n    ( '0' + (dtObj.getHours() + 1)).slice( -2 ) + ':',\n    ( '0' + (dtObj.getMinutes() + 1)).slice( -2 ) + ':',\n    ( '0' + (dtObj.getSeconds() + 1)).slice( -2 ) + 'Z'\n    ].join(\"\");\n\nmsg.payload = {\n    timestamp: datetime,\n    count: msg.payload\n};\nreturn msg;","outputs":1,"noerr":0,"x":610,"y":740,"wires":[["1cb2834c.16c96d","c8e04426.05d018","4f74e92c.388878"]]},{"id":"1cb2834c.16c96d","type":"debug","z":"9e258e12.c6e77","name":"","active":true,"console":"false","complete":"false","x":850,"y":860,"wires":[]},{"id":"5fc0a9cf.794c68","type":"ui_chart","z":"9e258e12.c6e77","group":"789f5031.5fdec","order":0,"width":0,"height":0,"label":"chart","name":"","interpolate":"step-after","nodata":"","ymin":"","ymax":"","removeOlder":1,"removeOlderUnit":"3600","x":570,"y":800,"wires":[[],[]]},{"id":"789f5031.5fdec","type":"ui_group","z":"9e258e12.c6e77","name":"Status","tab":"cdf49191.764b4","disp":true,"width":"6"},{"id":"cdf49191.764b4","type":"ui_tab","z":"9e258e12.c6e77","name":"Home","icon":"dashboard"}]
```

- 動画連携URL生成用ノード
動画連携のURLはここで生成されます。Cloudantに格納されたカウントデータを取得し、タイムスタンプでソートし、最新の1件のみを抽出し、タイムスタンプをテンプレートノードに埋め込んでリンクURLに変換します。

```
[{"id":"f5c568cd.fc3768","type":"http in","z":"9e258e12.c6e77","name":"Get Count","url":"/gcount","method":"get","swaggerDoc":"","x":120,"y":980,"wires":[["1629391b.0df247"]]},{"id":"99df8499.d6ae48","type":"cloudant in","z":"9e258e12.c6e77","name":"","cloudant":"","database":"count","service":"XXXXX-cloudantNoSQLDB","search":"_idx_","design":"onecount","index":"newest","x":570,"y":980,"wires":[["9e41ada5.3eada","c5959292.53739"]]},{"id":"1629391b.0df247","type":"function","z":"9e258e12.c6e77","name":"Set Var query","func":"var dtObj = new Date();\nvar dtObjU = new Date(dtObj.getTime() - 32400000);\n\nvar dateUTC = [dtObjU.getFullYear() + '-',\n    ( '0' + (dtObjU.getMonth() + 1)).slice(-2) + '-',\n    ( '0' + dtObjU.getDate() ).slice(-2) + 'T',\n    ( '0' + (dtObjU.getHours() + 1)).slice( -2 ) + ':',\n    ( '0' + (dtObjU.getMinutes() + 1)).slice( -2 ) + ':',\n    ( '0' + (dtObjU.getSeconds() + 1)).slice( -2 ) + 'Z'\n    ].join(\"\");\n\nmsg.payload = {\n    \"query\": \"timestamp:[ 1970-01-01T00:00:00Z TO \" + dateUTC + \"]\",\n    \"sort\": \"timestamp<string>\",\n    \"limit\": 1\n};\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":980,"wires":[["99df8499.d6ae48"]]},{"id":"1df5b142.466d9f","type":"http response","z":"9e258e12.c6e77","name":"","x":830,"y":1100,"wires":[]},{"id":"9e41ada5.3eada","type":"debug","z":"9e258e12.c6e77","name":"","active":true,"console":"false","complete":"false","x":850,"y":980,"wires":[]},{"id":"c5959292.53739","type":"function","z":"9e258e12.c6e77","name":"TS2EENTS","func":"var UTC_do = new Date(msg.payload[0].timestamp);\nmsg.tsUTC = [UTC_do.getFullYear(),\n                ( '0' + (UTC_do.getMonth() + 1)).slice(-2),\n                ( '0' + UTC_do.getDate() ).slice(-2),\n                ( '0' + UTC_do.getHours() ).slice( -2 ),\n                ( '0' + UTC_do.getMinutes() ).slice( -2 ),\n                ( '0' + UTC_do.getSeconds() ).slice( -2 ) + '.000'\n                ].join(\"\");\n\nmsg.camID = \"<Set Cam ID>\";\n\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":1040,"wires":[["4626e3c8.7f61ac","9e41ada5.3eada"]]},{"id":"4626e3c8.7f61ac","type":"template","z":"9e258e12.c6e77","name":"Web Const","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<html>\n    <head>\n    </head>\n    <body>\n        <a href=\"https://c006.eagleeyenetworks.com/#/camera_history/{{camID}}/{{tsUTC}}\">Eagle Eye Webコンソールで開く</a><br><br>\n    </body>\n</html>\n","x":590,"y":1100,"wires":[["1df5b142.466d9f"]]}]
```

インポートが完了すると、それぞれ下記のように表示されるはずです。
![node.png](https://qiita-image-store.s3.amazonaws.com/0/106468/2005522a-c96e-8ef5-8765-ddadabf65ccf.png)

##ノードのカスタマイズ
今回はインポートしたノード内に定数的に埋め込んだ部分がほとんどありませんので、3ノードのみ編集します。

1つ目の編集箇所はCloudantノード x2です。ここは各々が定義しているCloudantの構成内容に変更します。
Search Indexは準備フェーズで作成したものを指定しましょう。

2つ目の編集箇所は「**TS2EENTS**」ノード内の「**Set Cam ID**」の部分です。
ここを対象のIPカメラのESNに書き換えましょう。

BOSCHの7000番台以降のIPにカメラをお持ちの方は、次回の設定内容をお待ち下さい・・・。申し訳ありません。

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

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

です。
このURLにアクセスを行うと、次のようなWebページが表示されます。
（地味で申し訳ないです・・・）
![URL.png](https://qiita-image-store.s3.amazonaws.com/0/106468/d3b2447d-f593-880c-172a-e06314a3ab49.png)

ちゃんとURIが生成されていれば、正常にリンクをクリックできるはずです。
ここでクリックしてみましょう。クリックするとEagle Eyeへのログインページが表示されます。
![EEN.png](https://qiita-image-store.s3.amazonaws.com/0/106468/c3de697f-10da-d3e5-8e2d-50eb8ad03683.png)


ご自身のユーザーID、パスワードでログインします。
ログインが完了すると通常のユーザーであればレイアウトが表示されますが、この生成したURLにアクセスした場合、直接ヒストリビューアに画面に移動し、指定された日時に移動します。
![Hist.png](https://qiita-image-store.s3.amazonaws.com/0/106468/02d9e6c6-f8f8-ca34-4ab3-a8103bb0e34b.png)


もし実際のカウントデータを使用している場合には、BOSCHのIPカメラ上で人数カウントがUpした時の動画に日時が合っているはずです。この画面まできてしまえば、動画の再生はもちろん、ダウンロードやギャラリービューアによる前後時間の確認も簡単にできるはずです！


##最後に

いかがでしたか？
[前回](http://qiita.com/c-nova/items/16cff2e933be9ff9adbe)の動画取得と比べると、連携に必要な準備が格段に少ないことがわかって頂けたと思います。特にEagle Eyeによる認証認可などは組み込み上はまったく不要で、カメラのESNさえ分かっていればタイムスタンプのみで実現可能というのは、非常に汎用性が高いのではと思います。
Eagle EyeのタイムスタンプはYYYYMMDDHHMMSS.mmmという形式ですが、UTCで格納されているのがデメリットです（そうしないと世界中にカメラがある場合に大変ですからね・・・）。しかしCloudantに格納する際にUTCでタイムスタンプを格納しておけば、時刻調整のロジックを作らなくて済みますので、慣れるしかなさそうです。

次回は動画の保存を予定していましたが、多分BOSCHのカメラによる人数カウントの話を書きたいと思います。


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

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