Node-REDのノードを作り、Node-RED Libraryに登録する


AmbientというIoTデーター可視化サービスのNode-REDノードを作り、Node-RED Libraryに登録したので、一連の流れを忘備録として共有します。Ambientノードの使い方は[「Node-REDでAmbientにデーター送信して可視化する」](http://qiita.com/AmbientData/items/904a6bc471a4e5342a43)をご覧ください。

作業はMac OS上でおこないましたので、コマンドなどはMac OSのものです。

## Ambient

[Ambient](https://ambidata.io)は、マイコンから送られるセンサーデーターなどを受信し、蓄積し、可視化(グラフ化)するIoTクラウドサービスです。Ambientの概要は[「Ambientを使ってみる」](https://ambidata.io/docs/gettingstarted/)をご覧ください。

## Ambientノードの設計

Node-REDのAmbientノードを作る上で、次のように要件を整理しました。

* ノードは他の人も簡単に利用できるよう、Node-RED Libraryに登録する
* Ambientはデーターを受信し、蓄積して可視化(グラフ化)するサービスなので、ノード化する際は入力:1、出力:0のノードとする
* Ambientにデーターを送信する時は、「チャネルId」と「ライトキー」を指定して送信するので、「チャネルId」と「ライトキー」をノード・プロパティーとして設定することとする
* ノードのカテゴリーはGoogle Chartと同じ「advanced」とする

Node-RED Libraryはnode.jsのパッケージマネージャー「npm」を使って実現されています。Node-RED Libraryに登録するためには、モジュールをGithubに登録し、npmに登録する(と自動的にNode-RED Libraryに登録される)という流れになります。

## ファイル構造

Node-RED公式ドキュメントの[「パッケージング」](http://nodered.jp/docs/creating-nodes/packaging)を参考に、次のようなファイルを用意しました。

```
- node-red-contrib-ambient
    - package.json
    - ambient.js
    - ambient.html
    - README.md
    - .gitignore
```

ディレクトリー名(node-red-contrib-ambient)がGithubのリポジトリー名になり、npmのパッケージ名になり、Node-REDのノード名になります。[「パッケージング」](http://nodered.jp/docs/creating-nodes/packaging)によれば、サードパーティー提供のノードは「node-red-contrib-」というプレフィクスを使うことが推奨されているので、「node-red-contrib-ambient」という名前にしました。

## ノード動作の記述 .js

ノードの動作はjavascriptで記述します。フレームワークは公式ドキュメントの[「Node作成」](http://nodered.jp/docs/creating-nodes/first-node)と[「JavaScriptファイル」](http://nodered.jp/docs/creating-nodes/node-js)をご覧ください。

```javascript
module.exports = function(RED) {
    var request = require('request');
    var hostname = 'http://ambidata.io';

    function ambient(n) {
        RED.nodes.createNode(this,n);
        this.channelId = n.channelId;
        this.writeKey = n.writeKey;
        var node = this;

        this.on('input', function(msg) {
            var payload = msg.payload;
            payload.writeKey = node.writeKey;
            options = {  // HTTPパケットの組み立て
                url: hostname + '/api/v2/channels/' + node.channelId + '/data',
                headers: {'Content-Type': 'application/json'},
                body: payload,
                json: true
            };

            request.post(options, function(err, res, body) {  // HTTP POST
                if (err) {
                    node.error(err);
                    node.status({fill:"red", shape:"ring", text:"ambient send failed"});
                } else {
                    node.status({});
                }
            });
        });
    }
    RED.nodes.registerType("Ambient",ambient);
}

```

Ambientノードは上流のノードからメッセージを受信したら、HTTPパケットを組み立てて、Ambientサーバーに送信(POST)します。HTTP POSTするために、requestというパッケージを使います。

データー送信に必要な「チャネルId」と「ライトキー」はノード・プロパティーとして設定されるので、それを次のように受け取ります。

```javascript
    function ambient(n) {
        RED.nodes.createNode(this,n);
        this.channelId = n.channelId;
        this.writeKey = n.writeKey;
        ...
    }

```

## ノードの定義 .html

ノードの定義はhtmlで記述します。定義はノードの全体定義、編集テンプレートの定義、Infoの記述の三つに分かれます。

### ノードの全体定義
```html
<script type="text/javascript">
    RED.nodes.registerType('Ambient',{
        category: 'advanced',
        color: '#a6bbcf',
        defaults: {
            name: {value:""},
            channelId: {value:"",required:true},
            writeKey: {value:"",required:true},
        },
        inputs:1,
        outputs:0,
        icon: "arrow-in.png",
        align: "right",
        label: function() {
            return this.name||"Ambient";
        }
    });
</script>

```

ノードの全体定義ではノードの名称、パレット上のカテゴリー、プロパティー、入出力数、アイコンなどを定義します。要件に書いたように、カテゴリーは「advanced」、入力数:1、出力数:0としました。またフローの右端に置かれるノードなので、align: "right"を指定しています。
プロパティーは"defaults"で定義します。名前(name)の他にチャネルId(channelId)とライトキー(writeKey)を定義し、必須(required:true)に設定しました。こうすると、これらのプロパティーが未定義の時はノードに赤い三角が表示され、必須のプロパティーが未定義であることが分かります。

![undefined property](http://ambidata.io/wp/wp-content/uploads/2016/09/fig2-1-1.jpg)

### 編集テンプレートの定義

```html
<script type="text/x-red" data-template-name="Ambient">
    <div class="form-row">
        <label for="node-input-name"><i class="icon-tag"></i> Name</label>
        <input type="text" id="node-input-name" placeholder="Name">
    </div>
    <div class="form-row">
        <label for="node-input-channelId"><i class="icon-tag"></i> ChannelId</label>
        <input type="text" id="node-input-channelId" placeholder="ChannelId">
    </div>
    <div class="form-row">
        <label for="node-input-writeKey"><i class="icon-tag"></i> WriteKey</label>
        <input type="text" id="node-input-writeKey" placeholder="WriteKey">
    </div>
</script>

```

ノードの編集テンプレートを記述しています。入力するプロパティーごとに\<div\>要素を定義し、\<label\>タグと\<input\>タグを記述します。Ambientノードには名前、チャネルId、ライトキーの三つのプロパティーがあるので、それぞれに対応した三つの\<div\>要素を記述しています。\<input\>タグの中のid属性をnode-input-プロパティー名とすると、ノードの動作を記述するjavascriptで次のように参照できるようになります。

```javascript
    function ambient(n) {
        RED.nodes.createNode(this,n);
        this.channelId = n.channelId;
        this.writeKey = n.writeKey;
        ...
    }

```


### Infoの記述

```html
<script type="text/x-red" data-help-name="Ambient">
    <p>Ambientにデーターを送信して可視化する</p>
    <p>
        AmbientはIoTクラウドサービスで、センサーデーターを受信し、蓄積し、可視化(グラフ化)します。
        Ambientの詳細、使い方は<a href="https://ambidata.io">こちら</a>をご覧ください。
    </p>
    <p>
        ワークスペース上のAmbientノードで、AmbientのチャネルIDとライトキーを設定します。
    </p>
    <p>
        データーは<pre>{"d1":データー1, "d2":データー2, ...}</pre>という形式で渡します。
    </p>
    <p>
        チャートは<a href="https://ambidata.io">Ambientサイト</a>で見ることができます。
    </p>
</script>

```

ワークスペース上のノードをクリックした時に、右側に現れるInfoの内容を記述します。1行目はパレット上のノードにマウスオーバーした時にポップアップする説明文としても使われます。

![popup description](http://ambidata.io/wp/wp-content/uploads/2016/09/fig2-2-1.jpg)

## README.md

Githubに登録した時にリポジトリーページの説明文に使われ、そのままnpmの説明文とNode-RED Libraryの説明文に反映されます。
機能概要、インストール方法、データーの送信方法などを記述しました。

## package.json

適当な場所でnode-red-contrib-ambientディレクトリーを作り、npm initでpackage.jsonのひな形を作り、git initします。

```sh
$ mkdir node-red-contrib-ambient
$ cd node-red-contrib-ambient
$ npm init
$ git init

```

package.jsonはnpmのパッケージ情報ファイルです。Node-REDのノード用には"node-red"というエントリーでノード名とjavascriptファイルの場所を記述します。

```javascript
{
  "name": "node-red-contrib-ambient",
  "version": "0.0.4",
  "description": "Ambient Node to send data to Ambient. Ambient is a cloud service for IoT, Internet of Things. It receives sensor data, stores them and visualize them.",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/TakehikoShimojima/node-red-contrib-ambient.git"
  },
  "keywords": [
    "node-red",
    "Ambient"
  ],
  "node-red": {
    "nodes": {
      "Ambient": "ambient.js"
    }
  },
  "author": "Takehiko Shimojima",
  "license": "MIT",
  "dependencies": {
    "request": "^2.74.0"
  }
}

```

## 開発とデバッグ

node-red-contrib-ambientディレクトリーで.jsファイルと.htmlファイル、README.mdファイルを作っていきます。

javascriptが使うモジュールはnpm installでインストールします。この時、--saveオプションをつけることで、必要なモジュールがpackage.jsonに反映されるようにします。
開発中のノードをNode-RED環境に追加する時もnpmコマンドが使えるようです。開発中のディレクトリーが"どこか/node-red-contrib-ambient"だとすると、次のようにnpm installすると、開発中のノードがNode-REDエディターのパレットに現れ、扱えるようになります。

```sh
$ cd $HOME/.node-red
$ npm install どこか/node-red-contrib-ambient

```

npm installでノードをNode-REDエディターに取り込み、動作を確認し、必要があればファイルを修正するというループを繰り返してノードを完成させます。

## 登録

ノードを記述する.jsファイルと.htmlファイル、README.mdファイルが出来上がったら、まずGithubに登録します。

### Githubへの登録

Githubへのアカウント登録は終わっているものとして、まずGithubで「node-red-contrib-ambient」という名前のリポジトリーを作ります。
開発したファイルを次のようにローカルリポジトリーにcommitし、さらにリモートリポジトリーに反映させます。

```sh
$ git add .
$ git commit -m “Initial submission"
$ git remote add origin https://github.com/TakehikoShimojima/node-red-contrib-ambient.git
$ git push -u origin master

```

これでGithubにモジュールが登録できました。実際に登録したモジュールはこちらをご覧ください。

* [Githubのnode-red-contrib-ambient](https://github.com/TakehikoShimojima/node-red-contrib-ambient)

### npmへの登録

npm adduser でユーザー登録した後に、次のようにモジュールを登録します。

```sh
$ npm publish

```

これでnpmに登録できました。実際に登録したモジュールはこちらをご覧ください。

* [npmのnode-red-contrib-ambient](https://www.npmjs.com/package/node-red-contrib-ambient)

Node-RED Libraryにも反映されます。

[![node-red library](http://ambidata.io/wp/wp-content/uploads/2016/09/fig2-4.jpg)](http://flows.nodered.org/node/node-red-contrib-ambient)

これでAmbientノードも他のライブラリーと同様に使えるようになりました。

```sh
$ cd $HOME/.node-red
$ npm install node-red-contrib-ambient

```