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)
  • .htmlregisterType 中指定: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 时间戳

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


相关推荐
qq_2148032912 小时前
ArcGIS Runtime Java SDK初始化报错:Could not find runtime
java·开发语言·arcgis
ZHSH.3 天前
ArcGIS地统计综合实战 | 洛杉矶臭氧浓度预测-pretict-pretictable-pretiction
arcgis·预测·地统计分析
cehuishi95275 天前
利用ARCGIS手搓宗地图详细版
arcgis
百***78455 天前
node.js+npm的环境配置以及添加镜像(保姆级教程)
arcgis·npm·node.js
枝上棉蛮6 天前
从“高门槛”到“零门槛”:ArcGIS 和 GISBox如何破解中小用户GIS工具使用难题?
arcgis·gis·gisbox·服务分发·场景编辑·切片转换·gis服务器
Q一件事6 天前
arcgis用累计值进行分级
arcgis
啦啦球晃晃6 天前
ARCGIS删除自定义的七参数转换
arcgis
百***35336 天前
node.js+npm的环境配置以及添加镜像(保姆级教程)
arcgis·npm·node.js
江上清风山间明月8 天前
Android 系统超级实用的分析调试命令
android·内存·调试·dumpsys