「それ、Node-REDじゃなくても良くね？」って言わないでほしいやつ


ポキオ（@pokiiio）です。
[Node-RED Advent Calendar 2018](https://qiita.com/advent-calendar/2018/nodered)の12日目を担当させていただきます。ゆるいネタでよろしくおねがいします。

![ポキオ Node-RED Advent Calendar 2018](https://lh3.googleusercontent.com/cYci6q0ACNRUaeHEByOmJDPdnb3X_mdXskpKGW9l5pTyf8tHo9MSh3D6yy6O3HuQ6rSbnenjGP72CUHBQ5YOb_l-vCIj7SKnwJCNdEP2l57tXpvI_UEQYkF6VIygeoXEhKfYspa3dQ8=s600 "ポキオ Node-RED Advent Calendar 2018")


# TL;DR

 - Node-RED is すごい
 - 困ったらFunction NodeにJSでつらつら書けばいい
 - JSと親和性が低い処理はExec Nodeでやってしまえばいい
 - ちょっとダサいけど、なんでもできます


# 最高のプロトタイピングツール

ノンプログラミングで、ノードとノードをつなぐだけのフローで処理がデザインできるツールということで、Node-REDを一度知ってからは、ちょいちょい使わせていただいております。個人的にお気に入り（？）な使い方をシェアさせてください。


## Function Nodeの凄み

Node-REDは、その豊富なNodeのおかげで簡単に処理をデザインできることに強みがあると思います。ただ、あまりにもマニアックな処理ができるNodeは準備されていないことが多いですが、そんなときに役立つのがFunction Nodeです。


![ポキオ　京急　ﾀﾞｧ3号](https://lh3.googleusercontent.com/c-QMVlqgnJw1IDvva_5B9P_9SGQaEoF869_wjH3HZ7UNbqDm1Yqu9LAyCDFV1LTwufw2sJ0GsJtHz9jo3VFM0d6eLXBSq80eIw76CciWoTIyPdBZwGR82yNZ8yaNDeZtIPG81EedXJY=s600 "ポキオ　京急　ﾀﾞｧ3号")


たとえば、私は京急が好きで、[京急の運行情報ページから運行情報をしばしばパースする](https://relativelayout.hatenablog.com/entry/2018/07/06/235547)のですが、そんなノードなければFunction Nodeで書いちゃえばよいのです。

普段ArduinoなどのCっぽい言語に慣れている身からすると、JavaScriptはHTTP-GETなどが簡単にできるしrequireも使えるので、最高に便利です。


例：

```
[{"id":"3ad520c3.545e78","type":"tab","label":"フロー 1","disabled":false,"info":""},{"id":"80cbf517.c17cc8","type":"twitter-credentials","z":"","screen_name":"@pokiiiwo"},{"id":"3eb4297b.7362fe","type":"twitter in","z":"3ad520c3.545e78","twitter":"80cbf517.c17cc8","tags":"@keikyu_official","user":"user","name":"京急公式Twitterに新しい投稿","topic":"tweets","inputs":0,"x":180,"y":100,"wires":[["60533152.38f37"]]},{"id":"22429d61.35d622","type":"inject","z":"3ad520c3.545e78","name":"京急 + 山手線","topic":"","payload":"","payloadType":"date","repeat":"600","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":140,"wires":[["60533152.38f37"]]},{"id":"60533152.38f37","type":"http request","z":"3ad520c3.545e78","name":"京急運行情報取得","method":"GET","ret":"txt","url":"http://unkou.keikyu.co.jp","tls":"","x":150,"y":180,"wires":[["77d3de0e.446b9"]]},{"id":"77d3de0e.446b9","type":"function","z":"3ad520c3.545e78","name":"京急運行情報パース","func":"msg.payload = msg.payload.split(\"<div class=unko-panel>\")[1];\nmsg.payload = msg.payload.split(\"</div>\")[0];\nmsg.payload = msg.payload.replace(/\\r?\\n/g,\"\");\nmsg.keikyu = msg.payload;\nmsg.payload = null;\nreturn msg;","outputs":1,"noerr":0,"x":160,"y":220,"wires":[["49eb60e7.4544c"]]},{"id":"49eb60e7.4544c","type":"http request","z":"3ad520c3.545e78","name":"JR運行情報取得","method":"GET","ret":"txt","url":"https://transit.yahoo.co.jp/traininfo/detail/21/0/","tls":"","x":140,"y":260,"wires":[["8119d93e.e43388"]]},{"id":"8119d93e.e43388","type":"function","z":"3ad520c3.545e78","name":"JR運行情報パース","func":"msg.payload = msg.payload.split(\"\\\"og:description\\\" content=\\\"\")[1];\nmsg.payload = msg.payload.split(\"（\")[0];\nmsg.payload = msg.payload.replace(/\\r?\\n/g,\"\");\nmsg.yamanote = msg.payload;\nmsg.payload = null;\nreturn msg;","outputs":1,"noerr":0,"x":150,"y":300,"wires":[["1fbe8bb5.87f034"]]},{"id":"6f00e56f.8150ec","type":"exec","z":"3ad520c3.545e78","command":"python /home/pi/GitHub/TextToEPaper/text_to_e_paper.py","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"電子ペーパーに表示","x":480,"y":280,"wires":[[],[],[]]},{"id":"1fbe8bb5.87f034","type":"function","z":"3ad520c3.545e78","name":"keikyu, yamanote -> payload","func":"msg.payload = \"京浜急行 \\\"\" + msg.keikyu + \"\\\" 山手線 \\\"\" + msg.yamanote + \"\\\"\";\nreturn msg;","outputs":1,"noerr":0,"x":500,"y":140,"wires":[["71e04a63.eb8114"]]},{"id":"7dac4a68.ffcf04","type":"inject","z":"3ad520c3.545e78","name":"","topic":"起動時","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"5","x":150,"y":60,"wires":[["60533152.38f37"]]},{"id":"71e04a63.eb8114","type":"switch","z":"3ad520c3.545e78","name":"差分があった場合だけ更新","property":"payload","propertyType":"msg","rules":[{"t":"neq","v":"info","vt":"global"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":500,"y":180,"wires":[["b6900e34.b64bb"],[]]},{"id":"b6900e34.b64bb","type":"function","z":"3ad520c3.545e78","name":"globalにセット","func":"global.set(\"info\",msg.payload);\nreturn msg;","outputs":1,"noerr":0,"x":460,"y":220,"wires":[["6f00e56f.8150ec"]]}]
```


## 何でもできるExec Node

Function Nodeだけでも最強なんですが、それを遥かに凌駕するのがExec Nodeです（個人の感想です）。
というのも以前、[Raspberry Pi用の電子ペーパーハットを買って、そこに天気予報を表示するガジェットを作ったんですが・・・](![pokiiio WeatherDisplayForEPapers](https://lh3.googleusercontent.com/yp3nca1aNzq_NPH-kMltZ9-1Rl2DpftLXh2oMDxC640N0RBh4wcOn3h804IIg-B3m_bpycWb_h7rEUGKCXi561ZWcpRoAqyngSMToYSxPd67R376lkC6rXbRwS1llMPNJQ6Ds1PmGY8=s600 "pokiiio WeatherDisplayForEPapers")。

![pokiiio WeatherDisplayForEPapers](https://lh3.googleusercontent.com/yp3nca1aNzq_NPH-kMltZ9-1Rl2DpftLXh2oMDxC640N0RBh4wcOn3h804IIg-B3m_bpycWb_h7rEUGKCXi561ZWcpRoAqyngSMToYSxPd67R376lkC6rXbRwS1llMPNJQ6Ds1PmGY8=s600 "pokiiio WeatherDisplayForEPapers")





> 電子ペーパーといっても仕組みは単純で、ちょうどいいサイズの画像をプログラム上で作成して、それを特別なコマンドで電子ペーパーに貼り付けるだけ。コマンドは、いわゆるコマンドラインから叩くようなもので、また画像作成にはJavaScriptではなくPythonのPillowを使ってました（画像処理はPythonのほうが楽）。



定期的な処理をさせるのは、Node-REDで簡単に設計できるが、Pythonとかコマンドラインツールとはあまり親和性が高く感じられないかもしれません。そこでExec Nodeです。


結論から言っちゃえば、Node-REDだろうがPythonのスクリプトやらコマンドラインツールは簡単に叩けて、


```bash
cd ~/GitHub/WeatherDisplayForEPapers;python create_weather_image.py;papirus-draw image.png
```


例えばこんなようなコマンドをExec Nodeで実行するだけ。そのNodeが、よしなに定期的に実行されるように仕込んでしまえば、常に最新の天気予報が電子ペーパーに表示できるというわけです。


その他、電子ペーパーハット上のハードスイッチのイベントもNode-REDで簡単に拾えるので、ボタンが押されたら表示内容を変えたりするなどの処理も簡単にかけます。




# それ、Node-REDじゃなくてよくね？

って言われたら最後ですが、別にプロダクトのコードではなく、サンデープログラマのクソコードなので、なんと言われても強い気持ちで生きていきます。




# Appendix

 - [Node-RED UG勉強会 Vol.7「Node-RED x Connected」](https://node-red.connpass.com/event/83519/)
 - [京急ガジェットを作っているエンジニアから見たNode-RED](https://speakerdeck.com/pokiiio/jing-ji-gazietutowozuo-tuteiruenziniakarajian-tanode-red)