Node-REDのノードをTypeScriptで開発する
はじめてのノード開発で紹介されている、受け取ったメッセージの文字列をすべて小文字に変換するノードをサンプルに、TypeScriptで開発する方法について説明します。
npmにNode-RED の型定義が公開されているので、これを利用します。
動作を確認した環境
- Node.js v12.14.0
- TypeScript v3.8.3
- Node-RED v0.20.5
プロジェクトの作成
Node-REDノードのプロジェクトを作成します。
mkdir node-red-contrib-lowercase cd node-red-contrib-lowercase npm init -y
Node-RED、TypeScriptとNode-REDの型定義をインストールします。
npm install --save-dev node-red npm install --save-dev typescript @types/node-red
TypeScript設定ファイルの作成
コンパイル対象のファイルはsrc
の下に配置し、コンパイル後のファイルはdist
に生成されます。<br /> 想定するディレクトリ構成は次のようになります。
.
├── dist
└── nodes
│ ├── lower-case.html
│ └── lower-case.js
├── node_modules
├── package-lock.json
├── package.json
├── src
└── nodes
│ ├── lower-case.html
│ └── lower-case.ts
└── tsconfig.json
次のコマンドを実行し、設定ファイルを作成します。
tsconfig.json
がプロジェクトのルートディレクトリに生成されます。npx tsc --init
tsconfig.json
を次のように編集します。{ "compilerOptions": { "module": "commonjs", "target": "es5", "outDir": "./dist", "rootDir": "src", "lib": ["es5", "dom"], "moduleResolution": "node", "typeRoots": [ "node_modules/@types", "../../node_modules/@types" ], "esModuleInterop": true }, "include": ["src/**/*"] }
ノードの作成
HTMLファイル
src/nodes/lower-case.html
に、Node-REDエディタに表示されるノードの表示部分を作成します。
ここははじめてのノード開発 - lowercase.htmlと同じです。
<script type="text/javascript">
RED.nodes.registerType('lower-case',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value: ""}
},
inputs: 1,
outputs: 1,
icon: "file.png",
label: function() {
return this.name || "lower-case";
}
});
</script>
<script type="text/html" data-template-name="lower-case">
<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>
</script>
<script type="text/html" data-help-name="lower-case">
<p>A simple node that converts the message payloads into all lower-case characters</p>
</script>
registerType
でノードを登録します。第1引数で指定する文字列がノード名です。
TypeScriptファイル
src/nodes/lower-case.ts
に、ノード実行時の処理部分を作ります。
import { Red, Node, NodeProperties } from 'node-red';
module.exports = function(RED: Red) {
function LowerCaseNode(this: Node, props: NodeProperties) {
RED.nodes.createNode(this, props);
const node = this;
node.on('input', (msg) => {
msg.payload = msg.payload.toLowerCase();
node.send(msg);
});
}
RED.nodes.registerType("lower-case", LowerCaseNode);
}
- Node-REDオブジェクトと、Node、Propeties(ノードの設定項目)に型をつけます(3-4行目)。
RED.nodes.registerType
で、HTMLで登録したノード名に対し、実行する関数を登録します。
package.jsonの編集
コンパイル時に、
dist
の下にNode-REDのノードに必要なファイル群がコピーされるようにします。"scripts": { "build": "npm run copy:html && tsc ", "copy:html": "mkdir -p dist/nodes/ && cp -a src/nodes/*.html dist/nodes/" },
Node-REDにこのプロジェクトがNode-REDのノードだと認識されるよう、設定を追記します。
"node-red": { "nodes": { "lower-case": "dist/nodes/lower-case.js" } }
動作確認
次のコマンドを実行してtsファイルをjsファイルに変換します。
dist
の下に必要なファイルが生成されます。npm run build
Node-REDをローカルにインストールします。
npm install -g node-red
グローバルなnode_modulesにシンボリックリンクを張って、Node-REDにノードを登録します。
npm link
Node-REDを起動します。
node-red
http://127.0.0.1:1880/
にアクセスし、Node-REDの起動を確認します。エディタ上に次のノードを配置して、それぞれノードをつなぎます。
項目 | ノード | 備考 |
---|---|---|
入力 | inject | 「ペイロード」を「文字列」にして、変換したい文字列を入力します。 |
機能 | lower-case | |
出力 | debug |
- 左の実行ボタンをクリックして、injectノードで指定した文字列が小文字になっていればOKです。
独自プロパティの型定義
lower-case
ノードの場合、ノードのプロパティはデフォルトの「Name」のみでした。
しかし、実際にノードを開発する場合、ユーザーに設定させるオリジナルのプロパティを追加することもあります。 このときの型定義は、NodeProperties
を継承したインターフェースを作成して利用します。
例として、先頭の一文字のみ大文字にするか制御するプロパティを追加してみます。
<script type="text/javascript">
RED.nodes.registerType('lower-case',{
category: 'function',
color: '#a6bbcf',
defaults: {
name: {value: ""},
isCapitalize: {value: false}
},
inputs: 1,
outputs: 1,
icon: "file.png",
label: function() {
return this.name || "lower-case";
}
});
</script>
<script type="text/html" data-template-name="lower-case">
<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-isCapitalize"><i class="icon-font"></i> Capitalize</label>
<input type="checkbox" id="node-input-isCapitalize">
</div>
</script>
<script type="text/html" data-help-name="lower-case">
<p>A simple node that converts the message payloads into all lower-case characters</p>
</script>
この状態で、TSファイル側でprops.isCapitalize
を参照するとコンパイル時に次のエラーが出力されます。
src/nodes/lower-case.ts:15:19 - error TS2339: Property 'isCapitalize' does not exist on type 'NodeProperties'.
12 if (props.isCapitalize) {
独自プロパティの型を定義する
src/lib/nodes/interfaces.ts
を作成し、以下を記載します。import { NodeProperties } from 'node-red'; export interface LowerCaseProps extends NodeProperties { isCapitalize: boolean; }
NodeProperties
インターフェースを継承するLowerCaseProps
インターフェースを定義します。その中で、独自のプロパティを定義します。
TSファイルで
LowerCaseProps
を利用するように変更します。import { Red, Node } from 'node-red'; import { LowerCaseProps } from './lib/interfaces'; const capitalize = (str: string) => { return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); }; module.exports = function(RED: Red) { function LowerCaseNode(this: Node, props: LowerCaseProps) { RED.nodes.createNode(this, props); const node = this; node.on('input', (msg) => { if (props.isCapitalize) { msg.payload = capitalize(msg.payload) } else { msg.payload = msg.payload.toLowerCase(); } node.send(msg); }); } RED.nodes.registerType("lower-case", LowerCaseNode); }
これで、さきほどと同様にコンパイルがとおり、無事にjsファイルに変換されます。
設定ノードの型定義
Node-REDでは、ノード内の設定値を別のノードとして定義しておき、利用するノード内でプロパティとして読み込む設定ノードというしくみがあります。 このときの型定義はNode
インターフェースを継承します。
型定義
import { Node, NodeProperties } from 'node-red'; export interface ExampleProps extends NodeProperties { config: string; } export interface ConfigNode extends Node { credentials: { username: string; password: string; }; url: string; }
RED.nodes.getNode
でノードのIDが渡されるように、プロパティ側のconfig
の型をstring
にする。- 設定ノードの型は
Node
を継承する。
TSファイル
import { Red, Node } from 'node-red'; import { ExampleProps, ConfigNode } from './lib/interfaces'; ... 略 ... export const exampleNode = (RED: Red) => { RED.nodes.registerType('example', function( this: Node, props: ExampleProps ) { RED.nodes.createNode(this, props); const configNode = RED.nodes.getNode(props.config) as ConfigNode; const node = this; ... 略 ...
RED.nodes.getNode
で取得したノードの型をConfigNode
にキャストする。