IoTでペットヘルスケア[実装編：AWSサービス間の連携]

#本記事で取り扱うこと
[IoTでペットヘルスケア[構想編]](http://qiita.com/second113/items/bf350e362e74a4fc6140)
[IoTでペットヘルスケア[実装編：センサデータの取得～AWS IoT連携]](http://qiita.com/second113/items/6219bf99f448f40be378)
の続編です。

[IoTでペットヘルスケア[構想編]](http://qiita.com/second113/items/bf350e362e74a4fc6140)で紹介したアーキテクチャのうち、下記の<font color="Red">赤枠内の実装</font>(AWS IoTからその他のAWSのサービスへの連携)について、順を追って説明します。手順は現時点（2017/5）のものであるため、時間が経過している場合には、より便利なものが出ている可能性もありますので最新の情報をご確認ください。
![AWS Design_pets_healthcare_実践編02.png](https://qiita-image-store.s3.amazonaws.com/0/179480/56a789c9-92cd-741c-c4c5-3c0ffa101185.png)


また、当初は複雑な処理について、Step FunctionsやDynamoDBも活用するところまで本記事でご紹介しようと考えていましたが、長くなりすぎるため、一旦は個別に手動で実装する方法をご紹介したいと思います。可能であれば、応用編として自動化、サービス化を視野に入れた実装も別記事として作成したいと思います。


#AWS IoTからのデータ連携
デバイスからAWS IoTへ送られてきたデータは**Ruleで条件付けを行い**、**条件に該当したデータに対してActionで定義された動作を行う**事ができます。Ruleは送られてきたデータ一つに対して複数作成できます。Actionは、以下の図の通り、一つのルールに複数個定義することができます。
![base_01.png](https://qiita-image-store.s3.amazonaws.com/0/179480/d4c70ed8-e469-5b56-1ad6-293215480e18.png)

## Ruleの作成
Ruleでは、SQLの構文に似た形で条件を定義する事できます。今回は、以下の内容のルールを１つ作成し、ここから複数のActionへ分岐させます。対象データは前回の記事でデバイスから送信してきたデータです。

- 取得対象の項目
    - 全て（countPerMin、timestamp、deviceId、clientId）
- 対象のトピック
    - topic/y_raspberrypi_01/sensor/motion_sensor
- 条件
    - countPerMinが10より大きい場合
        - 1分間の検知回数。0～120の値となる。

では、AMCでの手順を見ていきます。まずはRulesのページを開きます。
<img width="700" alt="rule_01.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/9ecadf5e-b22c-df75-0a3c-78c95b6f121b.jpeg">
Create a ruleを選択を選択して作成を開始します。



<img width="700" alt="rule_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/3fd5748c-2cfb-f3b8-60dc-8cd9dc861a6a.jpeg">
以下の項目を設定します。設定中も画面中ほどにSQL文だとこうなる、というのが表示されますので参考にしながら設定することができます。

- NameとDescription
    - ルールの名前と説明について任意のものを入力します。
- SQLバージョン
    - 使える構文に差がありますので、基本的にはGAの最新版（現在は2016-03-23）を利用します。[詳細はこちら](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-rule-sql-version.html)を参照ください。
- Attribute
    - 送信されてきたデータから利用したい値を指定します。
    - countPerMinなどの項目を直接指定したり、”*”のようにワイルドカード指定も可能です。
- Topic filter
    - 条件の対象となるトピック名を指定します。いわゆるFROM句です。こちらが設定項目となってることからも分かりますが、Ruleの概念はリージョン内の全てのTopicを対象にしています。
    - ワイルドカードとして”#”（前方一致）や、”+”（部分一致）も利用する事ができます。
- Condition
    - データ取得の条件を入力します。いわゆるWHERE句です。
    - 空欄の場合、指定したトピックの全てのデータが該当します。

上記の項目について、図のように設定し、Ruleを作成します。
＃Add actionを選択すれば、Ruleと一緒にactionも作成可能です。本記事では次項で作成します。




## Actionの作成
連携先のAWSサービス毎にActionを定義します。利用可能なAction一覧が表示されますので、必要なものを選択して作成していきます。が、それに先立ち、IAM Roleの作成を行いましょう。

<img width="500" alt="Action_01.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/40725a88-13cb-294b-fbf8-2176e1b977e4.jpeg">


###IAM Roleの作成
さて、Actionを作成する前にAWS IoTのサービスに対して、他のサービスを操作する権限を与える必要があります。そのためのIAM Roleを事前に作成しておきましょう。Action作成画面からも作成することができますが、Action作成のたびに都度作成していると管理が煩雑になりますので、必要な権限を設計して作成したほうが良いでしょう。

今回は、以下の[マネージドポリシー（管理ポリシー）](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_managed-vs-inline.html)をアタッチしたAWS IoTサービス用Roleを作成します。

- AWSIoTRuleActions
- CloudWatchFullAccess
- AWSIoTRuleActions



### CloudWatch連携
今回はトイレの利用履歴を管理したいため、”Sends message data to CloudWatch"を選択します。こちらでは、Ruleにて該当するデータが送られてきた場合に、CloudWatchのメトリクスへデータを送信できるActionです。

<img width="600" alt="action_cw01.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/5fc63721-95dd-4b79-5ceb-7823ad8e1e8f.jpeg">


- Metric name
    - 任意のメトリック名を指定。今回はRestroom Using。
- Metric namespaces
    - メトリックをまとめる名前空間を指定。今回はPersonal/Karin。
- Unit
    - メトリックの単位を指定。今回はCount。
- Value
    - メトリックとして送る値を指定。~~今回は1。~~
    - ~~本当はCountPerMinを指定したかったのですが、指定してもうまくCloudWatchへ送信できず、残念ながらAWS IoTのAction機能ではできない様子。~~
    - ~~どれくらい検知されたのかも記録してグラフ化したければ、Lambdaへ連携してカスタムメトリクスへPushするか、Elasticsearchへ連携する必要がありそうです。~~今回はメールやLINEでの通知でカバーできるので深追いせず。
    - 【訂正】\${Attribute名}で参照することができました。今回の場合、\${countPerMin}とすることで値を取得する事ができました。
- Timestamp
    - メトリクスのタイムスタンプを指定。今回は空欄（Action実行時の日時を利用）。
- IAM Role name
    - 先程作成したRoleを指定します。

以上の設定を行うと、トイレの利用を検知すると、CloudWatchのメトリクスに登録され、以下のようにグラフ化して見ることができます。

<img width="600" alt="rule_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/287e023d-0d68-bb6a-95ec-a2ca241aa2a0.jpeg">


点がある部分が利用しているところで、1分間ごとに検知されると出ます。複数回or長時間入っていれば、複数の点として記録されます。これでいつ、どれくらい入ったのか（逆に入っていないのか）、間隔に変動があるか、視覚的に分かるようになりました。

### SNS連携
次に、トイレの利用を検知した際にPushで通知してくれるようにします。

- まずは、SNSのトピックを作成します。
SNSのTopicsページで"Create new topic"を選択すると、以下のウインドウが開きますので、任意の名前を入れて"Create topic"を選択します。

<img width="700" alt="sns_01.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/88e98c5d-37ab-369e-0c16-ff47a3d854a0.jpeg">


- 次に、トピックをサブスクライブするメールアドレスを登録します。
    - Topic ARNに上記で作成したトピックのARNを入力します。
    - ProtocolにEmailを選択します。（※後述のAWS IoTの出力設定とあわせる必要があります。）
    - Endpointへ通知したいメールアドレスを入力します。
    - "Create subscription"を選択すると、メールアドレスにConfirmationのメールが送信されますので、リンクをクリックします。

<img width="700" alt="sns_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/13df6236-cfa6-e00a-9f87-6fe8de5ab546.jpeg">



- 最後にAWS IoT側から連携の設定を行います。
    - SNS target
        - 作成したSNSトピック名を指定します。
    - Message format
        - RAWを選択します。
            - JSON形式を利用したい場合は、SNSのsubscriptionもSNSにして作成する必要があります。
    - IAM Role name
        - 先程作成したRoleを指定します。

<img width="600" alt="sns_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/77e9f9c3-27b0-ca39-b64f-9b28973a62c3.jpeg">



上記の設定を行うと、トイレ利用を検知すると以下のようなメール通知が行われます。AWS IoTへ送信されてきた生データをそのままメール通知しているため味気ないですが、どのくらいトイレにいたのか（countPerMin)も知ることができるようになりました。より複雑な条件付けは必要ですが、SNSはSMSにも対応していますので、緊急時は会社携帯にだって連絡させることも可能です。

<img width="600" alt="sns_mailsample.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/5e813348-7515-a38e-dbcd-d298c8ab7510.jpeg">



### Lambda連携
今回は、LINEへメッセージを送信するためのファンクションをLmabdaで実装します。ただし、必要な入力データとしてメッセージ送信先の情報やLINE botアカウント用の認証情報が必要になるため、デプロイする前にLINE botの作業へ移りますのでご注意ください。

- Serverless Frameworkの準備

メッセージ送信用のServerless Frameworkのプロジェクトを作成します。
([Serverless Frameworkの使い方はこちら](https://serverless.com/)を参照ください)  

```console
#Install via npm:
npm install -g serverless
#グローバルインストールである必要はないので、任意で。
#Set-up your Provider Credentials. Watch the video on setting up credentials
# Create a new Serverless Service/Project
serverless create --template aws-nodejs --path pushMessage
# Change into the newly created directory
cd pushMessage
```

- 連携先となるLambdaファンクションを作成

Lambda用のコードとしては、以下のようなものをServerless Frameworkを利用してデプロイするために準備します。テンプレートとしてhandler.jsが作成されているため、内容を以下に置き換えます。

```js:handler.js
// load modules
var https = require('https');

//load environment variable
const toToken = process.env.toToken;
const channelAccessToken = process.env.channelAccessToken;

module.exports.pushMessage = (event, context, callback) => {

    console.log('EVENT:', JSON.stringify(event, null, 2));
    var userate = "(" + event.countPerMin + "/120)";

    // Request Body
    var request_body = JSON.stringify({
        to: toToken,
        messages: [
            {
                "type": "text",
                "text": "トイレ使ったにゃん♪ 早くかたづけるにゃん♪" + "\r\n" + userate
                }
            ]
    });

    // Request Headers
    var send_options = {
        host: 'api.line.me',
        path: '/v2/bot/message/push',
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Authorization": " Bearer " + channelAccessToken
        },
        method: 'POST'
    };

    // API request
    var req = https.request(send_options, function (res) {
        res.on('request_body', function (chunk) {
            console.log(res.statusCode + chunk.toString());
        });
        req.on('error', function (err) {
            console.log('ERROR: ' + err.message);
            callback(err);
        });
    });
    req.write(request_body);
    req.end();
    callback(null, "function executed successfully");
};
```

- serverless.ymlの準備

serverless.ymlは以下のような感じです。こちらもテンプレートを置き換えます。先程のLambdaファンクションに加えて、実行のためのRoleを作成する内容となっています。
※環境変数（environment）に記載している**[Placeholder_toToken]**と**[Placeholder_channelAccessToken]**は、LINEの情報を入手後に置き換えていただくためのプレースホルダです。

```yaml:serverless.yml
service: pushMessage

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev  #default stage, overwrite by --stage option
  region: ap-northeast-1  #利用するリージョンの設定
  memorySize: 128 # Overwrite the default memory size. Default is 1024
  timeout: 5
  environment:
    region: ${opt:region,self:provider.region}
  iamRoleStatements:
    - Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s)
      Action:
        - logs:CreateLogGroup
        - logs:CreateLogStream
        - logs:PutLogEvents
      Resource: "*"

functions:
  pushMessage:
    handler: handler.pushMessage
    environment:
      toToken: [Placeholder_toToken]
      channelAccessToken: [Placeholder_channelAccessToken]
      
```



- 後述の手順（[LINE bot連携](##LINE bot連携)）を実施し、プレースホルダ部分を置き換え、serverless.ymlを更新します。


##LINE bot連携
LINEでは、独自サービスというLINEユーザの双方向のコミュニケーション用に[APIを公開](https://business.line.me/ja/companies/1422379/)してます。今回はそちらで利用可能なPush APIを利用します。その際に必要な情報の取得が少々面倒（というか分かりにくい）ため、手順を記載します。なお、こちらの利用には通常の（BOT用ではない）LINEアカウントがある前提となっていますので、お持ちでない方はLINEのアカウント登録から実施ください。


###Botアカウントの作成
- まずは[LINE BUSINESS CENTER](https://business.line.me/ja/)にアクセスし、ログインします。
    - サイト右上にログインボタンが有りますので選択します。
    - アカウントは既存のLINEアカウント（で登録しているメールアドレス）です。

- ログイン後の上記ページで、Messaging APIを選択します。
<img width="600" alt="LINE_04.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/0ac03eda-2f25-c8b2-f8a2-30ddfc95cc84.jpeg">




- "Developer Trialを始める"を選択します。
    - [このページ](https://business.line.me/ja/services/bot)の最下部のプランに紹介されています通り、Push APIが使えるのはDeveloper Trialもしくはプロ（API）です。Trialは友達数に制限（50人まで）があるものの、ご自宅のワンちゃんニャンちゃんの情報をPushしてほしい人数と比べると十分だと思いますので、こちらを選択します。（プロ、高いんです・・・。）

<img width="500" alt="LINE_05.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/4cf9a891-23ef-70bb-8107-0f07d7bb6688.jpeg">

- 会社/事業者情報の登録を行います。
    - 基本的に、任意に内容を埋めてください。
    - 今回のような用途の場合、個人を選択することになると思いますが、"会社/事業者名"のところは、LINE Botアカウントを友達登録する際に表示され、かつ、LINE Botアカウントが有効な状態（サービス中）では変更できませんので、公開範囲なども踏まえて入力ください。

<img width="500" alt="LINE_06.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/c103ffd9-7ec2-5c1e-3103-f8fb36763407.jpeg">


- このBussinessアカウントを選択した状態で、改めてMessaging APIの利用を選択すると、Botアカウントの設定を開始できます。
    - アカウント名、業種を入力して"確認する"に進みます。
    - Botアカウント用のアイコン写真もここでアップロードできますが、後からも変更可能です。 

<img width="500" alt="LINE_07.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/12769aef-f6d0-2a03-8387-21abaa7f3756.jpeg">



- 申し込みが完了すると、以下の画面に遷移しますので、"LINE@MANAGERへ"を選択します。
<img width="500" alt="LINE_08.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/50e4f76c-d033-6e91-8dd2-b0a7f77db398.jpeg">


- Bot設定から"APIを利用する"を選択します。
    - 通常の1対1のトークなどが不可になり、Bot用のアカウントとしてのみ利用可能な状態になります。
<img width="500" alt="LINE_09.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/720f5f46-caad-17cf-5d9b-4b108acf3cc2.jpeg">


- 再度、Bot設定を開き、リクエストや詳細な挙動の設定を行います。
    - Webhookの送信：利用する
        - 後述のLINEユーザID取得に利用します。
    - Botのグループトーク参加：利用する
        - こちらは任意ですが、グループトークに対してメッセージを贈りたい場合は"利用する"を選択します。
    - 自動応答メッセージ：利用しない
        - 話しかけられた際の挙動として、応答できない旨のメッセージがデフォルト設定されていますが、そちらを無効にします。
    - 友達登録追加時の挨拶：どちらでもOK

<img width="500" alt="LINE_10.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/ee54cfb2-b954-74a4-92d7-296049e3de89.jpeg">


###channelAccessTokenの取得
- LINE BUSINESS CENTERのアカウントリストを開き、Messaging API の"LINE Developers"を選択します。


<img width="500" alt="LINE_11.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/b96b6321-10d9-a12f-5c68-d4539884a7e0.jpeg">

-  Channel Access Tokenの"ISSUE"ボタンを選択肢、トークンを発行します。
    -  こちらの値をserverless.ymlの環境変数として**[Placeholder_channelAccessToken]**を置き換えます。

<img width="500" alt="LINE_12.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/6cfb724e-2684-a49f-7a29-efb89a3891ac.jpeg">


###LINEユーザIDの取得
Push APIは[リファレンス](https://devdocs.line.me/ja/)にもある通り、送信先のLINE ID（グループトークのIDなども含む）を指定しなければなりません。（※ID指定無しに友達登録している人全員に送る、ということができません。）
そこで、今回は簡易的にそのIDを取得する手順を記載します。本格的なBOTサービスを運用する場合は、IDを取得してからデータベースに保存するところまで自動化するべきだとおもいますが、今回は仕組みのご紹介ということで。

####Webhook受け先の作成
LINEのBotアカウントに対してイベントが発生した際、内容をWebhookで飛ばす事ができます。LINEユーザのIDもその中に情報として含まれており、その他の取得方法がない・・・と思います。※もっと簡単な取得方法があったら申し訳ないです。
というわけで、Webhookを受け、内容を確認するための準備を行います。Webサーバをお持ちの方は、そちらで受け取れば良いのですが、ない方はわざわざサーバを立てるのも面倒だと思いますので、API Gateway + Lambda Functionで代用してしまいます。

- Lambda Functionの作成
送られてきたeventの中身を確認できればなんでも良いのですが、今回は以下のファンクションを利用します。渡されたeventをログ出力するだけの内容です。ファンクションの詳細な設定は、デフォルトでOKです。

```js:dumpEvent.js
'use strict';
exports.handler = (event, context, callback) => {
    console.log('event =  ', JSON.stringify(event));
    callback(null, event);  // Echo back the first key value
};
```

- トリガーの設定

ファンクションの"トリガー設定を追加"からAPI Gatewayを選択します。

<img width="500" alt="LINE_13.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/45f75b1a-24ed-b733-e85e-817202180eaf.jpeg">

API名、デプロイされるステージは任意でOKです。セキュリティはオープンを選択します。（※認証無しになるため、IDの確認後は、APIを削除します）
<img width="500" alt="LINE_14.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/520a8f37-8400-23d0-1aeb-d52ecfb306e1.jpeg">


- API Gatewayの設定

メソッドの"統合リクエスト"の設定を開き、"Lambdaプロキシ統合の仕様"のチェックを外します。

<img width="500" alt="LINE_15.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/03e592b2-69ab-8544-2391-9913c2040d18.jpeg">

- Webhook URLの登録

Lambdaのトリガー設定からURLをコピーします。
<img width="500" alt="LINE_16.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/3c270669-5e64-ae57-5429-f46f51c99624.jpeg">

LINE developerから"EDIT"を選択し、上記のURLを設定します。
<img width="500" alt="rule_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/0edd63b5-c0e0-1698-0f89-14dc516cac74.jpeg">




- イベントの送信

登録したいIDが送信されるイベントを発生させます。例えば、Botアカウントのユーザ登録や、トークへの招待ですが、なんでもOKです。友達登録はdeveloperのページにQRコードが有りますので、そちらを利用してください。


- ログの確認

dumpEventファンクションのログを表示（CloudWatch Logsを確認）すると、以下のようなログが出ているはずです。userIdやroomId(トークルームの場合）の部分がtoTokenに指定するIDとなりますので、こちらで**[Placeholder_toToken]**を置き換えます。。ユーザの場合はUから始まる文字列、トークルームの場合はRから始まる文字列です。

<img width="500" alt="rule_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/4a1534c2-269d-1a34-18aa-5fdb60780907.jpeg">


###LambdaのFunctionのデプロイ

- serverless.ymlの編集が完了したら、以下のコマンドでデプロイします。
    - ※作成するリソースも多くないため、手動で作成しても問題ありません。
 
```console
serverless deploy -v
```

デプロイが完了すると、AWS IoTのRuleにマッチした際、Lambda Functionへ連携されてLINE botが通知してくれるようになりました。

<img width="400" alt="LINE_20.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/9e21f128-6f69-14fa-17a4-dbb15920b512.png">

#おわりに
今回は、比較的シンプルにAWS IoTから他のサービスへ連携する部分をご紹介しました。簡易な内容であれば少し設定をするだけで利用できますし、凝ったことをしたければLambdaへeventを飛ばしてしまえばある意味なんでもできてしまいます。これで召使いの外出中の不安が少し減りますが、、、できれば一緒にいたいですね。

<img width="500" alt="LINE_20.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/179480/b652f8c7-80de-4f7a-ec53-fc49591c7b4e.jpeg">