node-red でデータベースを用いた BroadLink RM mini3 の使い方

## できること
- Raspberry pi + Node-Red + RM mini3 だけで使用可。
- 別途アプリやRM Bridge, IFTTT等を必要としない。
- e-Control アプリで登録したリモコンデータを利用できる。
- e-Control アプリなしでもデータを構築できる。
- ファイルにデータベースを格納するので取り扱いが楽。
- dashboardでリモコン学習＆データベース登録を簡単にできる。
- 専用アプリなしでもdashboardでリモコン操作可能。
- alexa から利用可。

## 用意するもの
- BroadLink RM mini3
- Raspberry pi zero
- node-red
- node-red-dashboard
- node-red-contrib-broadlink

## 手順
### RM mini3 の初期設定
1. BroadLink e-Control 旧アプリ
1. BoradLink IHC 新アプリ
1. node-red-contrib-broadlink の`Setup`ノード

上記いづれかの方法で初期設定としてRM mini3に家のwifi情報を設定してネットワークにつなげる。具体的なやり方は省略。

### node-red のインストール
- node-red
- node-red-dashboard (パネルの管理よりインストール）
- node-red-contrib-broadlink (同上)

上記をインストールしておく。具体的なやり方は省略。

### データベースの新規作成
[https://github.com/VladimirSFilippov/node-red-contrib-broadlink/tree/master/examples](https://github.com/VladimirSFilippov/node-red-contrib-broadlink/tree/master/examples)
を参考にデータベースを使うサブフローを利用して赤外線信号の登録、呼出を行う。スマホのe-Controlアプリでリモコンを学習させて登録データを移す方法が紹介されているが、ここではアプリを使わずに新規でデータベースを構築していく。

Raspberry pi にデータベースファイルをおくフォルダを作成する。例えば、

```
/home/pi/SharedData
```

フォルダ内に3つのファイルを下記内容で作成する。

- jsonSubIr - リモート機器名登録ファイル
- jsonButton - ボタン名登録ファイル
- jsonIrCode - 赤外線コード登録ファイル

```json:jsonSubIr
[{"id":0,"name":""}]
```

```json:jsonButton
[{"id":0,"name":"","subIRId":0}]
```

```json:jsonIrCode
[{"buttonId":0,"id":0,"code":[]}]
```

### データベースを利用する為のサブフロー
サンプルのサブフローには修正の必要な個所があるため、修正して次の様なサブフローを用いる。

- **RM Add** - データベースへ登録するサブフロー
- **RM Get** - データベースから呼び出すサブフロー

```RM Add```

```json
[{"id":"7369859a.299a1c","type":"subflow","name":"RM Add","info":"データベースにボタンを追加します。\n入力値として下記の様に値を代入してください。\n```\nmsg.folder=\"/home/pi/SharedData/\";\nmsg.deviceName=\"リモート機器名\";\nmsg.buttonName=\"ボタン名\";\nmsg.code=[赤外線コード];\n```\n`deviceName`が見つからない場合は、新たにリモート機器名が追加されます。\n\n初めて使用する際にデータベースを新たに作成するには、指定フォルダに下記初期内容を書き込んだ `jsonSubIr`,`jsonButton`,`jsonIrCode` の３つのファイルを用意してから利用を開始してください。\n\n`jsonSubIr`\n```\n[{\"id\":0,\"name\":\"\"}]\n```\n`jsonButton`\n```\n[{\"id\":0,\"name\":\"\",\"subIRId\":0}]\n```\n`jsonIrCode`\n```\n[{\"buttonId\":0,\"id\":0,\"code\":[]}]```","category":"","in":[{"x":60,"y":60,"wires":[{"id":"8ee2869c.a83c38"}]}],"out":[{"x":680,"y":300,"wires":[{"id":"4dac3c29.d73df4","port":0}]}]},{"id":"21800b0a.1fb5e4","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":120,"wires":[["82cadfb.284a52"]]},{"id":"82cadfb.284a52","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":120,"wires":[["3ed8293f.15f226"]]},{"id":"3ed8293f.15f226","type":"function","z":"7369859a.299a1c","name":"","func":"msg.devices=msg.payload;\nvar devArr=msg.payload.filter(function(obj){if(obj.name==msg.deviceName) return true;});\n\nif(devArr.length>0){ \n    msg.deviceId=devArr[0].id;\n    return [msg,null];\n}\nelse{\n    msg.deviceId=Math.max.apply(Math,msg.devices.map(function(o){return o.id;}))+1;\n    msg.devices.push({id:msg.deviceId,name:msg.deviceName});\n    msg.payload=msg.devices;\n    return [null,msg];\n}","outputs":"2","noerr":0,"x":510,"y":120,"wires":[["140778dc.1df5a7"],["140778dc.1df5a7","7fe62471.ee206c"]]},{"id":"7fe62471.ee206c","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":60,"wires":[[]]},{"id":"b8451907.3ebff8","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":180,"wires":[["18a12738.8890a9"]]},{"id":"18a12738.8890a9","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":180,"wires":[["db8d02e3.8fd3f"]]},{"id":"db8d02e3.8fd3f","type":"function","z":"7369859a.299a1c","name":"","func":"msg.buttons=msg.payload;\nmsg.buttonsId=Math.max.apply(Math,msg.buttons.map(function(o){return o.id;}))+1;\nmsg.buttons.push({id:msg.buttonsId,name:msg.buttonName,subIRId:msg.deviceId});\nmsg.payload=msg.buttons;\nreturn msg;","outputs":"1","noerr":0,"x":510,"y":180,"wires":[["1f4bf2c9.c3737d","b4f23130.065f8"]]},{"id":"140778dc.1df5a7","type":"function","z":"7369859a.299a1c","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":650,"y":120,"wires":[["3e86e3d.2feb41c"]]},{"id":"1f4bf2c9.c3737d","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":180,"wires":[[]]},{"id":"30245555.bda11a","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":240,"wires":[["23ad759e.df224a"]]},{"id":"23ad759e.df224a","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":240,"wires":[["4dac3c29.d73df4"]]},{"id":"4dac3c29.d73df4","type":"function","z":"7369859a.299a1c","name":"","func":"msg.codes=msg.payload;\nmsg.codeId=Math.max.apply(Math,msg.codes.map(function(o){return o.id;}))+1;\nmsg.codes.push({buttonId:msg.buttonsId,id:msg.codeId,code:msg.code});\nmsg.payload=msg.codes;\nreturn msg;","outputs":"1","noerr":0,"x":510,"y":240,"wires":[["86b906c0.0c08d8"]]},{"id":"86b906c0.0c08d8","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":240,"wires":[[]]},{"id":"8ee2869c.a83c38","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonSubIr\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":120,"wires":[["21800b0a.1fb5e4"]]},{"id":"3e86e3d.2feb41c","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonButton\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":180,"wires":[["b8451907.3ebff8"]]},{"id":"b4f23130.065f8","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonIrCode\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":240,"wires":[["30245555.bda11a"]]}]
```
![RM Add.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/0b9b2def-c34c-ec57-5b2e-1f4690968eb0.jpeg)


```RM Get```

```json
[{"id":"c9b17f.ee6f4e8","type":"subflow","name":"RM Get","info":"データベースからボタンを探し出します。\n入力値として下記の様に学習済みの変数を代入してください。\n```\nmsg.folder=\"/home/pi/SharedData/\";\nmsg.deviceName=\"リモート機器名\";\nmsg.buttonName=\"ボタン名\";\n```\n出力を`RM`ノードにつなげれば赤外線コード信号が発信されます。","category":"","in":[{"x":60,"y":60,"wires":[{"id":"e2e9c7.80be3638"}]}],"out":[{"x":560,"y":240,"wires":[{"id":"830ed3a3.ae45d","port":0}]}]},{"id":"4ffb835b.4c5edc","type":"file in","z":"c9b17f.ee6f4e8","name":"","filename":"","format":"utf8","sendError":true,"x":310,"y":60,"wires":[["3b63a0da.3794d"]]},{"id":"e2e9c7.80be3638","type":"function","z":"c9b17f.ee6f4e8","name":"","func":"msg.filename=msg.folder+\"jsonSubIr\";\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":60,"wires":[["4ffb835b.4c5edc"]]},{"id":"3b63a0da.3794d","type":"json","z":"c9b17f.ee6f4e8","name":"","property":"payload","action":"","pretty":false,"x":450,"y":60,"wires":[["6104edb0.8642b4"]]},{"id":"6104edb0.8642b4","type":"function","z":"c9b17f.ee6f4e8","name":"","func":"msg.remoteid=msg.payload.filter(function (obj) { if (obj.name == msg.remote) { return true; } })[0].id;\nmsg.filename=msg.folder+\"jsonButton\";\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":120,"wires":[["cdc85f28.3e792"]]},{"id":"cdc85f28.3e792","type":"file in","z":"c9b17f.ee6f4e8","name":"","filename":"","format":"utf8","sendError":true,"x":310,"y":120,"wires":[["ae8f707c.afd39"]]},{"id":"ae8f707c.afd39","type":"json","z":"c9b17f.ee6f4e8","name":"","property":"payload","action":"","pretty":false,"x":450,"y":120,"wires":[["f1ca3462.ca04e8"]]},{"id":"f1ca3462.ca04e8","type":"function","z":"c9b17f.ee6f4e8","name":"","func":"msg.buttonid=msg.payload.filter(function (obj) { if (obj.name == msg.button && obj.subIRId == msg.remoteid) { return true; } })[0].id;\nmsg.filename=msg.folder+\"jsonIrCode\";\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":180,"wires":[["591b7a21.cef524"]]},{"id":"591b7a21.cef524","type":"file in","z":"c9b17f.ee6f4e8","name":"","filename":"","format":"utf8","sendError":true,"x":310,"y":180,"wires":[["d04b8ece.8bb44"]]},{"id":"d04b8ece.8bb44","type":"json","z":"c9b17f.ee6f4e8","name":"","property":"payload","action":"","pretty":false,"x":450,"y":180,"wires":[["830ed3a3.ae45d"]]},{"id":"830ed3a3.ae45d","type":"function","z":"c9b17f.ee6f4e8","name":"","func":"var data=msg.payload.filter(function (obj) { if (obj.buttonId == msg.buttonid) { return true; } })[0].code;\ndelete msg.filename;\ndelete msg.folder;\nmsg.payload = {};\nmsg.payload.action='send';\nmsg.payload.data=data;\nreturn msg;","outputs":1,"noerr":0,"x":170,"y":240,"wires":[[]]}]
```
![RM Get.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/b2ad101b-1cc0-96f4-af86-4362fd9fc3d7.jpeg)


### dashboard のフォームを利用したフローを作成する

以下のフローを読み込ませる。

```json
[{"id":"7369859a.299a1c","type":"subflow","name":"RM Add","info":"データベースにボタンを追加します。\n入力値として下記の様に値を代入してください。\n```\nmsg.folder=\"/home/pi/SharedData/\";\nmsg.deviceName=\"リモート機器名\";\nmsg.buttonName=\"ボタン名\";\nmsg.code=[赤外線コード];\n```\n`deviceName`が見つからない場合は、新たにリモート機器名が追加されます。\n\n初めて使用する際にデータベースを新たに作成するには、指定フォルダに下記初期内容を書き込んだ `jsonSubIr`,`jsonButton`,`jsonIrCode` の３つのファイルを用意してから利用を開始してください。\n\n`jsonSubIr`\n```\n[{\"id\":0,\"name\":\"\"}]\n```\n`jsonButton`\n```\n[{\"id\":0,\"name\":\"\",\"subIRId\":0}]\n```\n`jsonIrCode`\n```\n[{\"buttonId\":0,\"id\":0,\"code\":[]}]```","category":"","in":[{"x":60,"y":60,"wires":[{"id":"8ee2869c.a83c38"}]}],"out":[{"x":680,"y":300,"wires":[{"id":"4dac3c29.d73df4","port":0}]}]},{"id":"21800b0a.1fb5e4","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":120,"wires":[["82cadfb.284a52"]]},{"id":"82cadfb.284a52","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":120,"wires":[["3ed8293f.15f226"]]},{"id":"3ed8293f.15f226","type":"function","z":"7369859a.299a1c","name":"","func":"msg.devices=msg.payload;\nvar devArr=msg.payload.filter(function(obj){if(obj.name==msg.deviceName) return true;});\n\nif(devArr.length>0){ \n    msg.deviceId=devArr[0].id;\n    return [msg,null];\n}\nelse{\n    msg.deviceId=Math.max.apply(Math,msg.devices.map(function(o){return o.id;}))+1;\n    msg.devices.push({id:msg.deviceId,name:msg.deviceName});\n    msg.payload=msg.devices;\n    return [null,msg];\n}","outputs":"2","noerr":0,"x":510,"y":120,"wires":[["140778dc.1df5a7"],["140778dc.1df5a7","7fe62471.ee206c"]]},{"id":"7fe62471.ee206c","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":60,"wires":[[]]},{"id":"b8451907.3ebff8","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":180,"wires":[["18a12738.8890a9"]]},{"id":"18a12738.8890a9","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":180,"wires":[["db8d02e3.8fd3f"]]},{"id":"db8d02e3.8fd3f","type":"function","z":"7369859a.299a1c","name":"","func":"msg.buttons=msg.payload;\nmsg.buttonsId=Math.max.apply(Math,msg.buttons.map(function(o){return o.id;}))+1;\nmsg.buttons.push({id:msg.buttonsId,name:msg.buttonName,subIRId:msg.deviceId});\nmsg.payload=msg.buttons;\nreturn msg;","outputs":"1","noerr":0,"x":510,"y":180,"wires":[["1f4bf2c9.c3737d","b4f23130.065f8"]]},{"id":"140778dc.1df5a7","type":"function","z":"7369859a.299a1c","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":650,"y":120,"wires":[["3e86e3d.2feb41c"]]},{"id":"1f4bf2c9.c3737d","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":180,"wires":[[]]},{"id":"30245555.bda11a","type":"file in","z":"7369859a.299a1c","name":"","filename":"","format":"utf8","sendError":true,"x":270,"y":240,"wires":[["23ad759e.df224a"]]},{"id":"23ad759e.df224a","type":"json","z":"7369859a.299a1c","name":"","property":"payload","action":"","pretty":false,"x":390,"y":240,"wires":[["4dac3c29.d73df4"]]},{"id":"4dac3c29.d73df4","type":"function","z":"7369859a.299a1c","name":"","func":"msg.codes=msg.payload;\nmsg.codeId=Math.max.apply(Math,msg.codes.map(function(o){return o.id;}))+1;\nmsg.codes.push({buttonId:msg.buttonsId,id:msg.codeId,code:msg.code});\nmsg.payload=msg.codes;\nreturn msg;","outputs":"1","noerr":0,"x":510,"y":240,"wires":[["86b906c0.0c08d8"]]},{"id":"86b906c0.0c08d8","type":"file","z":"7369859a.299a1c","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"true","x":650,"y":240,"wires":[[]]},{"id":"8ee2869c.a83c38","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonSubIr\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":120,"wires":[["21800b0a.1fb5e4"]]},{"id":"3e86e3d.2feb41c","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonButton\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":180,"wires":[["b8451907.3ebff8"]]},{"id":"b4f23130.065f8","type":"function","z":"7369859a.299a1c","name":"","func":"msg.filename=msg.folder+\"jsonIrCode\";\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":240,"wires":[["30245555.bda11a"]]},{"id":"b9e72987.10aca8","type":"RM","z":"6243d695.227e98","name":"","device":"e05682e7.53e26","action":"learn","remote":"","button":"","repeat":"6","fix":"","RFSweep":"false","x":210,"y":120,"wires":[["c1c2ce15.21bbb"]]},{"id":"3fedcf22.b3392","type":"subflow:7369859a.299a1c","z":"6243d695.227e98","name":"","x":560,"y":120,"wires":[[]]},{"id":"5fea1f33.94a4f","type":"ui_form","z":"6243d695.227e98","name":"","label":"","group":"68eb2592.56241c","order":2,"width":0,"height":0,"options":[{"label":"リモート機器","value":"deviceName","type":"text","required":true},{"label":"ボタン","value":"buttonName","type":"text","required":true}],"formValue":{"deviceName":"","buttonName":""},"payload":"","submit":"学習","cancel":"キャンセル","topic":"","x":70,"y":120,"wires":[["b9e72987.10aca8","abe667c3.78f238"]]},{"id":"5a825538.0a43dc","type":"comment","z":"6243d695.227e98","name":"リモコン学習＆DB登録＠ダッシュボード","info":"","x":180,"y":40,"wires":[]},{"id":"c1c2ce15.21bbb","type":"change","z":"6243d695.227e98","name":"","rules":[{"t":"set","p":"folder","pt":"msg","to":"/home/pi/SharedData/","tot":"str"},{"t":"move","p":"payload.deviceName","pt":"msg","to":"deviceName","tot":"msg"},{"t":"move","p":"payload.buttonName","pt":"msg","to":"buttonName","tot":"msg"},{"t":"move","p":"payload.data","pt":"msg","to":"code","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":120,"wires":[["fb451c44.4ecb2","3fedcf22.b3392"]]},{"id":"fb451c44.4ecb2","type":"ui_toast","z":"6243d695.227e98","position":"top right","displayTime":"3","highlight":"","outputs":0,"ok":"OK","cancel":"","topic":"赤外線信号を学習しました","name":"","x":590,"y":160,"wires":[]},{"id":"abe667c3.78f238","type":"ui_toast","z":"6243d695.227e98","position":"top right","displayTime":"3","highlight":"","outputs":0,"ok":"OK","cancel":"","topic":"リモコンのボタンを押してください","name":"","x":250,"y":160,"wires":[]},{"id":"4b7880e.086db8","type":"ui_text","z":"6243d695.227e98","group":"68eb2592.56241c","order":1,"width":0,"height":0,"name":"","label":"Broadlink RM mini3 で赤外線信号をデータベースに登録します。","format":"{{msg.payload}}","layout":"row-spread","x":260,"y":80,"wires":[]},{"id":"e05682e7.53e26","type":"rmdevice","z":"","folder":"/home/pi/SharedData/","mac":"34ea3442b992","host":"192.168.1.11"},{"id":"68eb2592.56241c","type":"ui_group","z":"","name":"リモコン学習","tab":"380825c4.6254ea","disp":true,"width":"6","collapse":false},{"id":"380825c4.6254ea","type":"ui_tab","z":"","name":"設定","icon":"settings"}]
```
![dashboard - RM Add.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/5c9c65bc-0677-3202-a964-43bd14007c24.jpeg)

### `RM`ノードで使用するRM mini3を設定する

`RM`ノードの編集で Device 箇所からRM mini3の設定をする。

![RM node - Device.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/73cf5bf5-7157-fc5c-3dd8-5ac8af58b48b.jpeg)

![rmdevice.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/f62a52ef-cc26-a09e-e3e9-13f79570c2c7.jpeg)

### フロー内の form ノードの設定で dashboard の tab / group を適当に設定する

![form - group.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/88411f8a-802d-c95b-9315-66124f2ce6d7.jpeg)


### dashboard を利用してリモコンを登録する

PCかスマホのブラウザで dashboard へアクセスする。
```http://192.168.x.x:1880/ui
```
リモート機器名とボタン名を入力して学習ボタンを押すとRM mini3が受信状態となるのでリモコンをRM mini3に向けて登録するボタンを押す。続けて学習させるには同様の操作を繰り返す。
![dashboard - RM Add - browse.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/620b447c-3033-fd1e-28a4-4d28cfe23730.jpeg)

### 登録したデータでリモコン操作をする

単純に登録ボタンを使う場合は ```RM```ノードで Action に Send を選び、リモート機器とボタンを選択する。
![RM simple.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/f2b4cf17-74f2-bae6-2557-e99c879f8dda.jpeg)

![RM send.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/88bf7aa7-2fe6-a597-98b4-bae7ce01b33d.jpeg)

### データベースから呼び出してリモコン操作をする

![RM Get - RM.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/cd39c6ab-4d4c-9fd5-47a9-41a191cb339f.jpeg)

下記の様に変数に値を入れて`RM Get`サブフローに渡し、`RM`ノードにつなぐ。

```
msg.remote="リモート機器名";
msg.button="ボタン名";
msg.folder="データベースフォルダ";
```

![function node.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/75d9967e-380b-63c9-f4a1-adc47808b3fb.jpeg)

![RM node.jpg](https://qiita-image-store.s3.amazonaws.com/0/307198/3e9b10f1-55da-a600-fecb-65b1a00313d9.jpeg)

ちなみに node-red-contrib-alexa-local 等で「アレクサ、テレビを5にして」とか「アレクサ、暖房を20度にして」とalexaに言った場合、数字部分は `msg.bri` に入っているのでボタン名に数字を入れておくとテレビのチャンネルやエアコンの温度変更等ができる。