Node-RED:自定义节点开发:打造专属工具箱

Node-RED:自定义节点开发:打造专属工具箱

文章目录

关键字: Node-RED、 自定义节点、 节点开发、 npm包、 HTML模板 封装、 调试

摘要

去年我们做了一个智慧楼宇项目,有 20 多种设备需要接入 Modbus。

每次都要写一堆 Function 节点:解析寄存器、转浮点、处理字节序......

不仅流程图臃肿,还容易出错。

后来,我花了一天时间,封装了一个 modbus-parser 自定义节点:

  • 左侧调色板拖出来就能用
  • 可视化配置寄存器地址和数据类型
  • 输出标准化 JSON

从此,新增设备只需填参数,不再写一行 JS。

今天这篇文章,就带你从零开始,开发属于你自己的 Node-RED 节点 。

你将学会:

  • 自定义节点的完整目录结构
  • 如何用 HTML 定义配置界面
  • 如何用 JavaScript 实现核心逻辑
  • 如何本地测试与调试
  • 如何发布到 npm 供团队或社区使用

这不是 API 文档复读,而是一份 "从想法到可复用积木"的实战开发手册。


一、为什么需要自定义节点?------超越子流程的边界

子流程(Subflow)虽好,但有局限:

  • ❌ 无法发布到 npm 共享
  • ❌ 配置界面简陋(只有文本框)
  • ❌ 不能包含 Dashboard 控件
  • ❌ 无法实现复杂状态管理

而自定义节点:

  • ✅ 拥有专业级配置面板
  • ✅ 支持图标、下拉菜单、校验提示
  • ✅ 可打包成 npm 包,一键安装
  • ✅ 性能更高(原生节点调度)

💡 适用场景:

  • 封装通用协议(如 Modbus、BACnet)
  • 集成特定 SDK(如阿里云 IoT、微信 API)
  • 构建团队内部标准组件库

二、项目结构:一个标准自定义节点长什么样?

Node-RED 对自定义节点有约定目录结构:

text 复制代码
node-red-contrib-my-node/
├── package.json                # npm 包信息
├── README.md                   # 使用说明
├── icon.png (可选)             # 节点图标
├── nodes/
│   ├── my-node.html            # 配置界面(前端)
│   └── my-node.js              # 运行逻辑(后端)
└── test/ (可选)
    └── my-node_spec.js         # 单元测试

🔑 关键命名规则:

  • 文件名必须一致:my-node.html + my-node.js
  • npm 包名建议以 node-red-contrib- 开头(社区惯例)

三、第一步:创建 package.json

json 复制代码
{
  "name": "node-red-contrib-modbus-parser",
  "version": "1.0.0",
  "description": "Parse Modbus register data into structured JSON",
  "keywords": ["node-red", "modbus", "parser"],
  "node-red": {
    "nodes": {
      "modbus-parser": "nodes/modbus-parser.js"
    }
  },
  "dependencies": {
    "buffer": "^6.0.3"
  }
}

⚠️ 注意:node-red.nodes 字段告诉 Node-RED 哪个 JS 文件对应哪个节点名。


四、定义配置界面:my-node.html

这是用户双击节点时看到的弹窗。

基础模板:

html 复制代码
<script type="text/html" data-template-name="modbus-parser">
  <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="My Parser">
  </div>
  <div class="form-row">
    <label><i class="fa fa-cog"></i> Register Address</label>
    <input type="number" id="node-input-register" min="0" value="0">
  </div>
  <div class="form-row">
    <label>Data Type</label>
    <select id="node-input-type">
      <option value="uint16">Unsigned 16-bit</option>
      <option value="float32">Float 32-bit</option>
      <option value="string">String</option>
    </select>
  </div>
</script>

<script type="text/javascript">
  RED.nodes.registerType('modbus-parser', {
    category: 'function',       // 分类:function, input, output 等
    color: '#a6bbcf',           // 节点颜色
    defaults: {
      name: {value: ""},
      register: {value: 0, required: true},
      type: {value: "uint16"}
    },
    inputs: 1,
    outputs: 1,
    icon: "arrow-in.png",       // 内置图标或自定义 icon.png
    label: function() {
      return this.name || "Modbus Parser";
    }
  });
</script>

💡 高级技巧:

  • 表单校验 :required: true 自动提示
  • 动态选项 :用 JavaScript 动态填充 <select>
  • 帮助链接 :在底部加 <p>See <a href="...">docs</a></p>

五、实现核心逻辑:my-node.js

这是节点运行时的后端代码。

基础框架:

javascript 复制代码
module.exports = function(RED) {
    function ModbusParserNode(config) {
        RED.nodes.createNode(this, config);
        const node = this;
        
        // 保存配置
        node.register = config.register;
        node.type = config.type;

        // 监听输入消息
        node.on('input', function(msg, send, done) {
            try {
                // 核心解析逻辑
                let value = parseModbusData(
                    msg.payload, 
                    node.register, 
                    node.type
                );
                
                // 修改 msg 并发送
                msg.parsed = value;
                send(msg);
                done(); // 标记完成(用于异步)
            } catch (e) {
                node.error("解析失败: " + e.message, msg);
                done(e); // 报错并终止
            }
        });
    }
    
    RED.nodes.registerType("modbus-parser", ModbusParserNode);
};

function parseModbusData(buffer, reg, type) {
    // 实现具体解析...
    if (type === "float32") {
        return buffer.readFloatBE(reg * 2);
    }
    // ...
}

🔑 关键概念:

  • send() :发送输出消息(支持多输出:send([msg1, null]))
  • done():标记异步操作完成(Node-RED 1.0+ 必须调用)
  • node.error():触发 Catch 节点捕获

六、本地开发与调试:高效迭代的秘诀

步骤 1:链接到本地 Node-RED

bash 复制代码
# 在自定义节点目录下
npm link

# 进入 Node-RED 用户目录(如 ~/.node-red)
npm link node-red-contrib-modbus-parser

步骤 2:启用自动重载(开发模式)

编辑 settings.js:

javascript 复制代码
export NODE_RED_ENABLE_SAFE_MODE=false
// 或启动时加 --safe-mode=false

💡 修改 .js 或 .html 后,刷新浏览器即可生效,无需重启!

步骤 3:调试技巧

  • 在 .js 中加 node.warn("Debug: " + JSON.stringify(msg))
  • 用浏览器开发者工具查看 HTML 表单绑定
  • 用 console.log 输出到 Node-RED 终端(慎用)

七、图标与国际化:提升专业感

自定义图标:

  • 在节点目录放 icon.png(推荐 20x20 或 30x30)
  • 在 .html 的 registerType 中指定:icon: "icon.png"

多语言支持(可选):

html 复制代码
<script type="text/html" data-help-name="modbus-parser">
  <p>解析 Modbus 寄存器数据。</p>
</script>

帮助文档会显示在节点右键 → "Help" 中。


八、发布到 npm:让全世界都能用

步骤:

  1. 注册 npm 账号(npm adduser)

  2. 确保 package.json 版本号正确

  3. 执行发布:

    bash 复制代码
    npm publish
  4. 安装验证:

    bash 复制代码
    npm install node-red-contrib-modbus-parser

📌 发布前检查清单:

  • README.md 包含使用示例
  • 关键配置有默认值
  • 错误处理完善
  • 无硬编码敏感信息
  • 测试通过

💡 社区贡献:发布后可在 Node-RED Flow 提交,让更多人发现。


九、最佳实践:写出高质量节点

原则 说明
单一职责 一个节点只做一件事(如"解析",不包含"连接")
配置最小化 默认值合理,高级选项可折叠
错误明确 报错信息包含上下文(如"寄存器 100 超出范围")
性能注意 避免在 input 中做 heavy 计算,考虑缓存
向后兼容 升级时保留旧配置字段

十、实战案例:封装阿里云 IoT 上报节点

需求:

  • 输入 msg.payload = { temp: 25 }
  • 自动加上设备证书、时间戳
  • 调用阿里云 MQTT 上报

节点配置界面:

  • ProductKey(文本)
  • DeviceName(文本)
  • DeviceSecret(密码框)
  • Topic(文本,默认 /user/update)

核心逻辑(简化):

javascript 复制代码
const aliyun = require('aliyun-iot-device-sdk');
node.device = aliyun({
    productKey: config.productKey,
    deviceName: config.deviceName,
    deviceSecret: config.deviceSecret
});

node.on('input', (msg) => {
    node.device.publish(config.topic, JSON.stringify(msg.payload));
});

✅ 效果:团队所有设备上报,统一用此节点,安全又规范。


写在最后:从使用者到创造者

Node-RED 的强大,不仅在于它提供的 4000+ 节点,

更在于每个人都能成为生态的建设者。

当你把重复的 50 行 Function 代码,

封装成一个带图标、有校验、可共享的自定义节点时,

你就完成了从"自动化使用者"到"工具创造者"的跃迁。

在此之前,不妨动手做一个小节点:
比如 "timestamp-adder" ------ 自动给 msg 加上 ISO 时间戳 。

当你在调色板看到自己写的节点亮起绿灯时,你会明白------
真正的自由,是能按自己的方式扩展世界。


相关推荐
上海懒图科技4 天前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
非科班Java出身GISer8 天前
ArcGIS JS 基础教程(48):SceneView watch 状态监听
arcgis·arcgis js基础·arcgis js入门·arcgis js监听状态·arcgis js监听事件·arcgis js 监听飞行·arcgis js 监听功能
JudithHuang10 天前
React 嵌套路由与布局
前端·react.js·arcgis·react·react router
Dxy123931021611 天前
JS DES 加密详解
开发语言·javascript·arcgis
oscar99914 天前
CPO 共封装光学——当“电互连的极限”只能靠“光进封装”来突破
芯片·封装
非科班Java出身GISer14 天前
ArcGIS JS 基础教程(46):编辑要素属性 FeatureForm
arcgis·arcgis js编辑要素·arcgis js 要素·arcgis js要素属性·arcgis js 编辑属性·arcgis js编辑要素属性
oscar99914 天前
玻璃基板与面板级封装——从“圆形晶圆逻辑”到“矩形大板逻辑”的范式迁移
芯片·封装
oscar99915 天前
3D 异构集成中的热—力—电耦合——被封装结构放大的系统级问题
芯片·封装·电耦合
梅如你15 天前
ISPRS | 1990-2020年间中国人工林与天然林的空间分布数据
arcgis·gis·遥感
oscar99916 天前
TSV 与 3D 堆叠——垂直方向上的密度跃迁及其代价
芯片·封装·tsv