IFTTTのwebhooksとWatson IoT PlatformでNode.jsにプッシュ通知

# はじめに
GoogleHomeとラズパイ3の自作赤外線モジュールを使って、リビングから2階寝室のエアコンを付けられるようにしようと思ったときに構成を悩んだのでメモ。

構成を考えるときの条件は、

* GoogleHomeとの連携はIFTTTのWebhooksを使う
* クラウド上にサーバーは立てたくない
* DDNSなどのラズパイNAT越えもイヤ
* もちろん無料でできること

で、Publish-Subscribe型のサービスで無料のMQTTブローカーとかないかと探したところ良さそうなのがあった。
[CloudMQTT](https://www.cloudmqtt.com/)
[Watson IoT Platform](https://www.ibm.com/internet-of-things/jp-ja/iot-solutions/watson-iot-platform/)
Watson IoT PlatformにはWeb APIでイベントをPublishできる口があったのでこちらを使うことにしました。（CloudMQTTは管理APIしか見当たらなかった）

## 必要なもの

* GoogleHome
* IFTTTアカウント
* IBM Cloudアカウント（たぶんライトアカウントでもOK）
* Raspberry Pi 3 または zero w　（両方動作確認済み）

ラズパイはなくてもNode.jsが動く環境があればいいです。

## 動作環境
Node.js 8.9.4

# IBM Cloudライト・アカウント作成
この辺からアカウントを作成します。
[IBM Cloudライト・アカウントで使える 
”ライト・プラン”サービスのご紹介](https://www.ibm.com/cloud-computing/jp/ja/bluemix/developerslounge/liteservice/)

たぶん名前とメアドで作成できたと思います。クレカ情報不要。
自分はWatson画像認識を無料枠で使っているのですが、こちらはクレカ情報を取られてしまいました。
ライトアカウントでのWatson IoT Platformの無料枠はこのぐらいあります。

* 500個までのデバイス接続
* 月間200MBまでのデータ通信

個人で使う分には十分だと思います。

# IoT Platformの作成
IBM Cloudダッシュボードの左メニューからIoTを選択してCreate Resourceボタンを押します。
![リソース作成.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/6a52db03-af0a-3810-5f16-4c1b002956d8.png)

カタログからInternet of Things Platformを選びます。
![カタログ.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/5aeeccda-3bb3-3c01-a369-63f0e2a5ef70.png)

内容を確認します。
![リソース作成.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/3415f867-cc3d-8672-3d20-93ebeb2edf8c.png)
問題がなければ右下の作成ボタンを押します。しばらくするとダッシュボードにInternet of Things Platform-xxが表示されるのでクリックします。
![IoT起動.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/96447b4c-4aaf-1303-66b1-366045284799.png)
起動ボタンを押すと別画面でIoT Platformのダッシュボードが立ち上がります。
![ポータル.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/ae12e45f-6590-3ae3-500c-873140e1dcc0.png)

## 組織ID
このダッシュボード画面の左下の「設定（歯車アイコン）」を選択して「組織ID」をメモしておきます。後でラズパイから接続するときに必要になります。組織IDはIoT PlatformダッシュボードのURL　**xxxxxx**.internetofthings.ibmcloud.comの**xxxxxx**部分と同じです。
![組織ID.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/360e1439-b72d-4d29-de5f-725db6647119.png)

## APIキーと認証トークン
同様にダッシュボード画面左の「アプリ（コンパスアイコン）」を選択してAPIキーを作成します。内容はデフォルトのままで問題ありません。
![APIキー.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/c30de3d9-fe20-2100-6211-c518fb3bf976.png)
APIキーと認証トークンをメモっておきます。メモったら「生成」ボタンを押します。

これでIoT Platformの準備は終わりです。

# ラズパイのNode.jsでクライアントアプリを書く
まずWatson IoT Platformクライアントをインストールします。

```
npm install ibmiotf
```

## Watson IoT Platformのイベントについて
IoT Platformのイベントは次のような属性を持ちます。内容は全部自分で勝手に決めていいです。一つのPlatformで複数のイベントを発行でき、選択的にサブスクライブできます。

| 属性 | 説明 | メモ | 今回使う値 |
|:--------:|:--------------:|:-------------------:|:--------:|
|typeId    |デバイスタイプID |イベントのカテゴリー   |ifttt     |
|deviceId  |デバイス識別ID   |イベント発行元の識別子 |ghome     |
|eventName |イベント名称     |イベント種別          |heating/cooling |
|eventData |イベントデータ   |データ本体            |なし      |

今回はGoogle HomeでIFTTTを経由してエアコンの暖房／冷房を通知したいので上のようなイベント内容にしてみました。

### Watson IoT Platform HTTP Messaging API
ついでにイベントを発行する方法について書いておきます。
Watson IoT Platform HTTP Messaging APIを使ってイベントを発行することができます。これはクライアント側ではなくイベントを発行する側（今回はIFTTTのWebhooks）で呼び出します。
URLは以下の形式になります。メソッドはPOSTです。ボディはJSONデータなど自由に入れることができます。

```
POST https://{組織ID}.messaging.internetofthings.ibmcloud.com/api/v0002/application/types/{typeId}/devices/{deviceId}/events/{eventName}
```
今回のイベントなら

```
https://xxxxxx.messaging.internetofthings.ibmcloud.com/api/v0002/application/types/ifttt/devices/ghome/events/heating
```
とかになります。

認証はBasic認証で行います。ヘッダに
`Authorization: Basic {APIキー:認証トークン}をBase64エンコードしたもの`
を付ける必要があります。

## Node.jsクライアント
IoT Platformにイベントをサブスクライブするコードを書いていきます。

```javascript:subscriber.js
var client = require('ibmiotf');

var appClientConfig = {
  "org": 'xxxxxx',       // 組織ID
  "id": 'your-app-name', // 任意のアプリ識別子
  "auth-key": 'a-xxxxxx-yyyyyyyyyy',
  "auth-token": 'zzzzzzzzzzzzzzzzzz'
};

var appClient = new client.IotfApplication(appClientConfig);

appClient.connect();

// setting the log level to 'debug'
appClient.log.setLevel('debug');

appClient.on("connect", function() {
  console.log("Connect to IoTF.");
  // とりあえず全てのイベントを購読
  appClient.subscribeToDeviceEvents();
});

// イベントを受信したときのハンドラ
appClient.on("deviceEvent", function(deviceType, deviceId, eventType, format, eventData) {

  console.log("Device Event from :: "+deviceType+" : "+deviceId+" of event "+eventType+" with payload : "+eventData);

  // 今回はイベント名称だけで処理を切り分ける
  if (eventType === 'heat') {
    // 暖房の時の処理（省略）
  } else if (eventType === 'cool') {
    // 冷房の時の処理（省略）
  } else {
    console.log('unknown event ' + eventType);
  }
});
```

今回はイベントは2種類しかないので、手っ取り早く全てのイベントをサブスクライブしてイベント名称によって処理を切り分けています。デバイス種別やイベント別にサブスクライブすることができます。詳しくは[こちら](https://console.bluemix.net/docs/services/IoT/applications/libraries/nodejs.html#nodejs)で。
自分の環境ではイベントハンドラ内でラズパイのGPIOで赤外線LEDを制御するスクリプトを呼び出しています。この辺は書籍やググればたくさん出てくるので省略します。

クライアントアプリの設定を見ていきます。

```javascript
var appClientConfig = {
  "org": 'xxxxxx',       // 組織ID
  "id": 'your-app-name', // 任意のアプリ識別子
  "auth-key": 'a-xxxxxx-yyyyyyyyyy',
  "auth-token": 'zzzzzzzzzzzzzzzzzz'
};
```

`"org"`, `"auth-key"`, `"auth-token"`は先の手順でメモっておいた組織IDとAPIキー、認証トークンです。
`"id"`はクライアントの一意識別子です。これは適当に決めてかまいませんが、クライアントアプリごとに変える必要があります。同じIDで接続しようとすると「すでにサブスクライブ済みです」的なエラーがおきます。

## 起動
nodeコマンドで起動します。

```bash
pi@raspberrypi:~/work/node $ node subscriber.js
[ApplicationClient:connnect] ApplicationClient Connected
Connect to IoTF.
[ApplicationClient:subscribeToDeviceEvents] Calling subscribe with QoS 0
[ApplicationClient:subscribe] Subscribing to topic iot-2/type/+/id/+/evt/+/fmt/+ with QoS 0
[ApplicationClient:subscribe] Subscribed to topic iot-2/type/+/id/+/evt/+/fmt/+ with QoS 0
```

こんな感じのログがでたらOK。

# IFTTTの設定
IFTTTの設定をしていきます。ThisをGoogle Assistant、thatにWebhooksを選択します。
## Google Assistantの設定
ThisでGoogle Assistantを選択し、このような感じでGoogle Homeに話しかけるキーワードを入力します。"Say a simple phrase"でよいと思います。応答フレーズも適当にいれます。
![ifttt1.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/b36fcd6a-06a3-d64c-fd45-92a16eb3fc64.png)

## Webhooksの設定
ThatでWebhooksを選択し、Google Homeでフレーズがヒットしたら呼ばれるHTTP APIを設定します。ここでWatson IoT PlatformのHTTP Messaging APIを設定します。しかし上述の通り、Basic認証があるためURLを以下のように設定します。

```
https://APIキー:認証トークン@組織ID.messaging.internetofthings.ibmcloud.com/api/v0002/...
```
うん。もうIFTTTを信用するしかないですね…:sweat:
（ヘッダ挿入に対応してくれないかな…）

![ifttt2.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/799d1185-33d8-e5ec-a540-2a6261d70de5.png)
![ifttt3.PNG](https://qiita-image-store.s3.amazonaws.com/0/238395/e03bafe0-6db3-6884-dca1-06386c7dcc1a.png)
上のような感じでBodyを送ることもできます。（今回は送っているだけで使っていません）Google Assistantのingredientを使って「暖房」「冷房」を引数にしてBodyに埋め込めばIFTTTのアプレットを一つにできます。

設定できたら`Save`ボタンを押して保存します。以上で完成です。
リビングのGoogle Homeに「OK Google, 2階暖房」と話しかけるとラズパイ上のNode.jsアプリにイベントが通知されるようになりました。

# まとめ
IFTTTを使ってWebhooksでWatson IoT Platformでプッシュ通知する方法をまとめました。とりあえず無料でできるということと宅内側でサーバーを立てなくてよいという方針で考えてみました。他にもDDNSやngrokなどの方法もあると思いますがセキュリティ周りを気にしたくない人はWatson IoT Platformの利用も検討してはいかがでしょうか。無料枠も結構ありますし。