練習としてCreating your first nodeを動かすことができました。次はもう少し実際の利用にあわせてInfluxDBにメッセージを保存するnodeを書いてみます。node-red-nodesのstorageディレクトリにデータベース用のnodeがいくつかあります。今回はPostgreSQLの110-postgres.jsを参考にします。
Cannot find module 'influx'
前回settings.jsに追加のnodesディレクトリを設定しました。コンテナを起動するときにDockerホストのディレクトリをマウントするようにしています。
...
nodesDir: '/data/nodes',
...
最初はこの追加ディレクトリにInfluxDBのnodeを配置しましたが、require("influx");
のところでモジュールの読み込みに失敗してしまいます。InfluxDBクライアントはNode-REDのpackage.jsonの最後に追加しました。google/nodejs-runtimeのDockerfileではONBUILD RUN npm install
でnpmモジュールをインストールしています。
"dependencies": {
"express": "3.17.2",
"when": "3.7.2",
...
"influx":"3.3.0"
},
作成したnodeをnpmパッケージとして公開する方法がPackagingにあります。まだ開発中なのでNode-REDをcloneしたディレクトリにあるnodes/core/storage/
配下に直接追加してDockerイメージを再作成することにします。npmに登録するのはテストが通ってからにします。
InfluxDBのnode
ファイル名の数字のprefixはnodesディレクトリ内でnodeのロード順を定義するようです。とりあえず98にしました。jsファイルにnodeのロジックを記述します。
module.exports = function(RED) {
"use strict";
var influx = require("influx");
function InfluxDBNode(n) {
RED.nodes.createNode(this,n);
this.host = n.host;
this.port = n.port;
this.db = n.db;
this.user = this.credentials.user;
this.password = this.credentials.password;
}
RED.nodes.registerType("influxdb",InfluxDBNode,{
credentials: {
user: {type:"text"},
password: {type: "password"}
}
});
function InfluxNode(n) {
RED.nodes.createNode(this,n);
this.influxdb = n.influxdb;
this.influxConfig = RED.nodes.getNode(this.influxdb);
var node = this;
if(this.influxConfig) {
node.clientdb = influx({
host : this.influxConfig.host,
port : this.influxConfig.port,
username : this.influxConfig.user,
password : this.influxConfig.password,
database : this.influxConfig.db
});
node.on("input", function(msg) {
node.clientdb.writePoint(msg.topic, JSON.parse(msg.payload), function(err) {
if(err) {
node.error(err);
}
});
});
}
}
RED.nodes.registerType("influx",InfluxNode);
}
htmlファイルにはnodeの設定を記述します。ユーザーが値を入力するフォームのテンプレートとNode-REDへの登録を行います。今回は最初なのでcategoryはconfig
とsotrage-out
だけ作成してレコードを登録する機能だけ実装します。
<script type="text/x-red" data-template-name="influxdb">
<div class="form-row">
<label for="node-config-input-host"><i class="fa fa-bookmark"></i> Host</label>
<input class="input-append-left" type="text" id="node-config-input-host" placeholder="localhost" style="width: 40%;" >
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
<input type="text" id="node-config-input-port" placeholder="8086" style="width:45px">
</div>
<div class="form-row">
<label for="node-config-input-db"><i class="fa fa-briefcase"></i> Database</label>
<input type="text" id="node-config-input-db" placeholder="">
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-user"></i> Username</label>
<input type="text" id="node-config-input-user" placeholder="username">
<label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
<input type="password" id="node-config-input-password" placeholder="password">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('influxdb',{
category: 'config',
defaults: {
host: { value:"localhost",required:true},
port: { value: 8086,required:true},
db: { value:"influxdb",required:true}
},
credentials: {
user: {type:"text"},
password: {type: "password"}
},
label: function() {
return this.db;
}
});
</script>
<script type="text/x-red" data-template-name="influx">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-influxdb"><i class="fa fa-tag"></i> Server</label>
<input type="text" id="node-input-influxdb">
</div>
</script>
<script type="text/x-red" data-help-name="influx">
<p>A InfluxDB I/O node. </p>
</script>
<script type="text/javascript">
RED.nodes.registerType("influx",{
category: "storage-output",
color:"#dbb84d",
defaults: {
influxdb: {type:"influxdb",required:true},
name: {value:""}
},
inputs: 1,
outputs: 0,
icon: "influxdb.png",
align: "right",
label: function() {
var influxDBNode = RED.nodes.node(this.influxdb);
return this.name||(influxDBNode?influxDBNode.label():"influxdb");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
最後にInfluxDBのアイコンを用意します。
~/docker_apps/node-red/public/icons/influxdb.png
Dockerイメージの作成と起動
Dockerイメージのビルドとコンテナを起動します。
$ cd ~/docker_apps/node-red/
$ docker build -t node-red .
$ docker run -d --name node-red \
-p 1880:1880 \
-v /opt/nodes:/data/nodes \
node-red
Node-REDでflowの作成
input:inject
inject nodeをworkspaceにドラッグ&ドロップします。テスト用にPayloadにInfluxDBに保存するJSONを文字列で書きます。キーはダブルクォートで囲います。TopicはInfluxDBのseries名になります。
- Payload: string {"temperature":4.5,"humidity":6.7}
- Topic: sensortest
- Repeat: None
- Name: dummy-input
storage:influx
storageカテゴリからinfluxをworkspaceにドラッグ&ドロップします。nodeのconfigに接続するInfluxDBのHost名やDatabase名を設定します。
-
node
-
Name: influxdb-store
-
Server: sensor-test
-
config
-
Host: 10.1.3.67
-
Port: 8086
-
Database: sensor-test
-
User: admin
-
Password: password
デプロイと実行
InfluxDBにテスト用のDatabaseとユーザーを作成します。
$ curl -X POST 'http://10.1.3.67:8086/db?u=root&p=root' -d '{"name": "sensor-test"}'
$ curl -X POST 'http://10.1.3.67:8086/db/sensor-test/users?u=root&p=root' \
-d '{"name": "admin", "password": "password"}'
画面右上のDeploy
ボタンを押してデプロイします。テストなのでflowの起動はinject nodeの左側のボタンをクリックします。
InfluxDBの管理画面
sensor-test
データベースのクエリ画面を開きます。
- Databases > sensor-test > Explore Data
以下のような簡単なQueryを記述してExecute Query
ボタンを押すと1レコードヒットしました。Node-REDのinject nodeに定義したPayloadがInfluxDBに保存できたようです。