LINE Things development boardをMotionBoardに繋げてみる

# 1. はじめに
`LINE Things development board`を入手して、今現在できることをいろいろ試した（つもり）なので、他システム連携から利活用含めて考えてみました。

# 2. LINE Thingsとは

公式によると、
>LINE Thingsは、LINEを介して、チャネルとBluetooth® Low Energy対応デバイスを連携し、操作を可能にするIoTプラットフォームです。

とあり、LINEをインターフェースとしたIoTセンサーの利活用だったり、IoTデバイスの制御ができる仕組みを有しています。
誰もが使っているLINEをインターフェースとして活用できるのは大きな利点で、アイデア次第で無限の可能性を秘めていると言えます。

これから開発を考えているのであれば、`Messaging API`と`LINE Front-end Framework(LIFF)`については頻繁にでてくる用語なので合わせて知っておきましょう。

- [Messaging APIの概要](https://developers.line.biz/ja/docs/messaging-api/overview/)
- [LINE Front-end Frameworkの概要](https://developers.line.biz/ja/docs/liff/overview/)

## LINE Things developer board
温度センサー、3軸加速度センサー、ボタン、有機ELなどを搭載しているLINE Thingsの開発用ボードです。
Bluetooth通信用としてNordic社のnRF52モジュールが搭載されており、高速にプロト開発ができます。
デフォルトでファームウェアが入っていたため、開発用ボード->LINE->LINEプラットホームの疎通確認はすぐにできました。
もっていなくとも、市販の対応ボードで同様のことはできますが、センサーなどは自前で用意する必要があります。

https://github.com/line/line-things-starter/blob/master/README.ja.md
のREADMEによると、

* `LINE Things dev board`
* `Espressif ESP32-DevKitC`
* `M5Stack （ESP32)`
* `Adafruit Feather nRF52 Bluefruit LE - nRF52832`
* `Adafruit Feather nRF52840 Express`
* `BBC micro:bit`
* `Obniz`

が現在対応しています。

## 自動通信機能
`LINE Things`には`自動通信機能`という機能があります。
事前に、`シナリオセット`と呼ばれるものをLINEプラットホームに登録しておくことで、LINEがIoTデバイスから受け取ったデータを`LIFF`を経由することなく、Webhookで指定したURLに流してくれる機能です。
他システムと連携するときに、自前で`LIFF`上で通信機能を実装する必要がありませんし、LINEアプリを開いておかなくても通信ができます。
また`LIFF`はCookieやLocalStorageの永続性が保証されておらず、将来的に削除の可能性もあるとのことなので、その点も考慮する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/9d301fc0-6943-de8c-f6f5-b2d73542a222.png)


# 3. MotionBoardとは

データベースやセンサーデータなどを可視化できるソフトウェアです。
商用ソフトウェアですが、各種データベースとの接続や高度な可視化・分析機能が使用できます。

- [MotionBoard](http://www.wingarc.com/product/motionboard/)


# 4. 連携のまえに
デフォルトファームウェアをいろいろ試しておくと、何ができるかなんとなく見えてきます。

* [LINE Things dev board使用ハンズオン - Qiita](https://qiita.com/cpulabs/items/3a37a70cfa41129cb024)
* [LINE Things 自動通信機能 ハンズオン - Qiita](https://qiita.com/hktechno/items/12781e38b09e10c20da2)


# 5. MotionBoardと連携してみる



## 事前準備

* [LINE Things dev board使用ハンズオン - Qiita](https://qiita.com/cpulabs/items/3a37a70cfa41129cb024)を参考に`LINE Developers にてプロダクトの登録とService UUIDの取得`まで
* `Node-RED`などwebhookの受け先となるサーバー
    * 一番簡単に試すなら[IBM Cloud](https://www.ibm.com/jp-ja/cloud)のライトアカウント（無償）で`Node-RED`を建てる
* LINEの最新版が動作しているAndroidまたはiPhone
* [MotionBoard](http://www.wingarc.com/product/motionboard/)の`IoTエディション`
    * MQTTを使用してデータを送信するのであればAWS IoTなどの`MQTTブローカー`
    * [リアルタイム連携用テンプレートの作成](http://navi.wingarc.com/motionboardiot/webapi.html)


## シナリオセットの作成と登録
`シナリオセット`の登録には、`プロダクトID`、`Service UUID`、`Characteristic UUID`、`チャンネルアクセストークン`が必要です。

### 準備
* プロダクトID：プロダクト登録時のプロダクトID。
    参考：[LINE Things 自動通信機能 ハンズオン - LINE Things プロダクトの作成](https://qiita.com/hktechno/items/12781e38b09e10c20da2#)

* Service UUID
    * `アドバタイズ用UUID`と`GATT通信用UUID`を分けて考える必要があります。
    * LINE Things developer boardであれば個体によらず`f2b742dc-35e3-4e55-9def-0ce4a209c552`で固定。
* Characteristic UUID：https://github.com/line/line-things-dev-board のthings-dev-board.js　あたりに書いてます。
    * `LINE Things dev board`のセンサーとボタン情報をひととおり送信したければ`e90b4b4e-f18a-44f0-8691-b041c7fe57f2`とします。
* `チャンネルアクセストークン`：`LINE Developersコンソール`の[チャネル基本設定]から取得することができます。

### シナリオセットの作成
LINE Things dev board`のセンサーとボタン情報をひととおりを定期的に自動送信したければ以下のjsonファイルのようになります。

```json
{
    "autoClose": false, 
    "suppressionInterval": 0,  
    "scenarios": [
        {
            "trigger": {
                "type": "BLE_NOTIFICATION",
                "serviceUuid": "f2b742dc-35e3-4e55-9def-0ce4a209c552",
                "characteristicUuid": "e90b4b4e-f18a-44f0-8691-b041c7fe57f2"
            },
            "actions": [
            ]
        }
    ]
}
```

###シナリオセットの登録
LinuxやMacの場合

```console
$ curl -X PUT https://api.line.me/things/v1/products/<YOUR PRODUCT ID>/scenario-set \
-H 'Authorization: Bearer <YOUR CHANNEL ACCESS TOKEN>' \
-H 'Content-Type:application/json' \
-d '
{
    "autoClose": false, 
    "suppressionInterval": 0,  
    "scenarios": [
        {
            "trigger": {
                "type": "BLE_NOTIFICATION",
                "serviceUuid": "f2b742dc-35e3-4e55-9def-0ce4a209c552",
                "characteristicUuid": "e90b4b4e-f18a-44f0-8691-b041c7fe57f2"
            },
            "actions": [
            ]
        }
    ]
}'
```

<br>

Windowsの場合
curl.exeでもいいのですが、私はchrome拡張の[Restlet Client - REST API Testing
](https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm?hl=ja&a8=PlQKLlknX1T5BcZY0wzBDQ_TRQ7LVcTdjByY1_y_6YuAoJQ4CJQTU1QdU56JTVQP6lC_XDQSKDIqxs00000003166005)を使っています。リクエスト・レスポンス内容も保存出来て便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/e6c0b678-2efe-3de3-61b8-ccb4476477d1.png)

一回登録してしまえば、`シナリオセット`の変更がない限り、繰り返す必要はありません。
`シナリオセット`を更新する際は、同じURLに対して再度リクエストを行ってください。

## Node-REDの設定
Webhookの受信用＋[MQTTブローカー]の送信先として、Node-REDのフローを作成します。
Node-REDはフロー（ロジックシナリオ）のインポート機能がありますので、[メニュー]->[読み込み]->[クリップボード]の順でクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/7f48a866-44c5-200e-d3aa-40569ce5c0bf.png)

下をまるごとコピーして、貼り付けます。

```json
[{"id":"f53f6be6.d8d288","type":"http in","z":"3c49d6d6.16594a","name":"","url":"/line","method":"post","upload":false,"swaggerDoc":"","x":100,"y":220,"wires":[["acd8c0cb.af5a3","c526bc83.d739e"]]},{"id":"acd8c0cb.af5a3","type":"http response","z":"3c49d6d6.16594a","name":"","statusCode":"","headers":{},"x":270,"y":160,"wires":[]},{"id":"3c5f01ec.3d92be","type":"function","z":"3c49d6d6.16594a","name":"MB対応形式に変換","func":"let buffer = msg.payload.events[0].things.result.bleNotificationPayload;\nbuffer = Buffer.from(buffer,'base64');\n\n\nlet temperature = buffer[1].toString(16) + buffer[0].toString(16);\nmsg.temperature = parseInt(temperature, 16) / 100;\n\nif (buffer[3] >= 128) {\n    let accelX = buffer[3].toString(16) + buffer[2].toString(16);\n    msg.accelX = (parseInt(accelX, 16) - 65535) / 1000.0;\n} else {\n    let accelX = buffer[3].toString(16) + buffer[2].toString(16);\n    msg.accelX = parseInt(accelX, 16) / 1000.0;\n}\n\nif (buffer[5] >= 128) {\n    let accelY = buffer[5].toString(16) + buffer[4].toString(16);\n    msg.accelY = (parseInt(accelY, 16) - 65535) / 1000.0;\n} else {\n    let accelY = buffer[5].toString(16) + buffer[4].toString(16);\n    msg.accelY = parseInt(accelY, 16) / 1000.0;\n}\n\nif (buffer[7] >= 128) {\n    let accelZ = buffer[7].toString(16) + buffer[6].toString(16);\n    msg.accelZ = (parseInt(accelZ, 16) - 65535) / 1000.0;\n} else {\n    let accelZ = buffer[7].toString(16) + buffer[6].toString(16);\n    msg.accelZ = parseInt(accelZ, 16) / 1000.0;\n}\n\nlet btn1 = buffer[9].toString(16) + buffer[8].toString(16);\nmsg.btn1 = parseInt(btn1, 16);\n\nlet btn2 = buffer[11].toString(16) + buffer[10].toString(16);\nmsg.btn2 = parseInt(btn2, 16);\n\nmsg.payload = '{\"protocol\": \"1.0\",\"loginId\": \"2\",\"template\": \"iot\",\"tenant\": \"cdl002mb\",\"status\":[{\"time\":' + msg.payload.events[0].timestamp + ',\"enabled\": \"true\",\"values\":[{\"name\":\"temp\",\"type\":\"3\",\"value\":' + msg.temperature + '},{\"name\":\"hash\",\"type\":\"2\",\"value\":\"'  + msg.md5 + '\"},{\"name\":\"accelX\",\"type\":\"3\",\"value\":' + msg.accelX + '},{\"name\":\"accelY\",\"type\":\"3\",\"value\":' + msg.accelY + '},{\"name\":\"accelZ\",\"type\":\"3\",\"value\":' + msg.accelZ + '},{\"name\":\"sw1\",\"type\":\"3\",\"value\":' + msg.btn1 + '},{\"name\":\"sw2\",\"type\":\"3\",\"value\":' + msg.btn2 + '}]}]}';\n\nreturn msg;\n","outputs":1,"noerr":0,"x":450,"y":220,"wires":[["7b1087df.ad6828","f8f67a08.883318"]]},{"id":"c526bc83.d739e","type":"md5","z":"3c49d6d6.16594a","name":"","fieldToHash":"payload","fieldTypeToHash":"msg","hashField":"md5","hashFieldType":"msg","x":270,"y":220,"wires":[["3c5f01ec.3d92be"]]},{"id":"7b1087df.ad6828","type":"debug","z":"3c49d6d6.16594a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":650,"y":160,"wires":[]},{"id":"f8f67a08.883318","type":"mqtt out","z":"3c49d6d6.16594a","name":"","topic":"","qos":"","retain":"","broker":"9cb4f2c2.97e06","x":650,"y":220,"wires":[]},{"id":"9cb4f2c2.97e06","type":"mqtt-broker","z":"","name":"","broker":"","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
```

[mqttノード]をダブルクリックし、編集します。[鉛筆アイコン]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/c80d43fb-751b-eecb-4237-a122f2ef12eb.png)


予め準備した[MQTTブローカー]のサーバーアドレスを入力します。また、[セキュリティ]タブから[MQTTブローカー]の認証情報を入力し、最後に[更新]をクリックし、保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/fdf71c97-25f7-c55b-491b-ac59f1358deb.png)

設定が終わったら[デプロイ]ボタンを押下して、変更を適用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/d8104394-cfd4-86a1-1e66-e851ddaedf2a.png)

## Webhookの設定
LINE Developersにアクセスし、[プロバイダーリスト]から事前に作成したものをクリックします。
[基本設定]タブから、[Webhook送信]を編集して、[利用する]に変更します。
また、[Webhook URL]を編集して、作成したサーバーのURLを指定します。
設定した後、[接続確認]をクリックすることで疎通確認ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/8d8ccd90-f1be-4596-008f-fcc4077887be.png)



## ボードの作成
MotionBoardで可視化ボードを作成します。
ステップバイステップで書いてしまうと長くなるので、大まかに書くと、

- ボードの新規作成
- [データソース]を指定します。
    - WebAPIやMQTTでデータを送信すると、[system datasources]という専用の領域に格納されますので作成したテンプレート名を指定します。
- `リアルタイム分析`機能をオンにします。
    - [リアルタイム分析を設定する](http://navi.wingarc.com/motionboardiot/post-67.html)
- チャートデザインを選択する（折れ線、棒グラフ、画像、シグナル、シングル数値などから選択）
- アイテムを画面に配置する。（漫画のコマ割りの要領で）

のような手順になります。
<br>

- 参考： [ボード作成ガイド](https://mbc-manual.motionboard.jp/motionboard/manual/mbcloud/#111608369.html%3FTocPath%3D%25E3%2583%259C%25E3%2583%25BC%25E3%2583%2589%25E4%25BD%259C%25E6%2588%2590%25E8%2580%2585%7C%25E3%2583%259C%25E3%2583%25BC%25E3%2583%2589%25E4%25BD%259C%25E6%2588%2590%25E3%2582%25AC%25E3%2582%25A4%25E3%2583%2589%7C_____0)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70061/c9f285c1-f1a3-ff28-0c7e-2850ad051a2a.png)



# 6. シナリオを考える
- トラック運転手の毎日の脈拍や体温などの健康状態を登録してもらい、支援部門が全体を管理。
    - 未入力の運転手には入力を促すメッセージ送信（Messaging API）
- 学校のプール授業前に生徒に体温測定を義務付け
- イベント運営がボランティアスタッフの位置情報把握と最適タスク分配

# 7. 参考
* [LINE Things dev board使用ハンズオン - Qiita](https://qiita.com/cpulabs/items/3a37a70cfa41129cb024)
* [LINE Things 自動通信機能 ハンズオン - Qiita](https://qiita.com/hktechno/items/12781e38b09e10c20da2)
* [MotionBoard Cloudマニュアル@リアルタイム連携のWeb API](https://mbc-manual.motionboard.jp/motionboard/manual/mbcloud/#111609715.html%3FTocPath%3D%25E7%25AE%25A1%25E7%2590%2586%25E8%2580%2585%7CWeb%2520API%25E3%2583%25AA%25E3%2583%2595%25E3%2582%25A1%25E3%2583%25AC%25E3%2583%25B3%25E3%2582%25B9%7C%25E7%25AC%25AC3%25E7%25AB%25A0%25E3%2580%2580Web%2520API%25E3%2581%25AE%25E4%25B8%2580%25E8%25A6%25A7%7C_____6)
* [MotionBoard リアルタイム連携用テンプレート作成方法](http://navi.wingarc.com/motionboardiot/webapi.html)