Node-RED教程(十二):定制化节点教程

Node-RED系列文章目前已经写了16篇,介绍了Node-RED的安装以及默认安装的一些基本节点的使用,作为物联网的一个可视化拖动的流程,Node-RED的确实很容易上手。还没开始学习的同学可以先看下我以前的文章。

看了我的Node-RED系列文章后,很多同学都问我能不能出一篇详细的定制节点的教程,于是今天我抽点时间,将定制Node-RED的节点的相关教程整理一下,分享给大家,
如果大家还没有用过Node-RED不建议先看本篇文章,可以从第一篇看起,安装一个Node-RED玩玩。
好了下面让我们进入主题。

由于Node-RED是开源的,你可以在github找到项目的所有代码,所以我们有理由相信,我们可以基于Node-RED为原型,进行各种集成,开发。
比较简单的一种就是做一些定制的节点来满足自己的日常需求。如果做的好,拿来卖钱也不是不可能的。

好了让我们言归正传。如何进行自定义节点?如何将自定义的节点发布,部署?

首先让我们了解下Node-RED的节点到底是什么?

我们从github上下载源码后,可以在这个模块找到Node-RED的默认安装的节点,

其中一个html节点的代码

大致我们清楚了节点需要什么内容。

环境准备

要制作一个定制的节点

首先要准备这几个文件

  • 定制节点的html 用于显示配置节点
  • 定制节点的JavaScript 用于处理Node-RED流中的数据
  • package.json文件(可选,如果需要npm安装才必须要)

下面我们来做一个简单的demo,来演示一下,如何定制一个node

定制节点的作用是将payload转化为小写

首先我们创建一个文件夹 名字为node-red-contrib-example-lower-case

然后在其中创建二个文件
一个是lower-case.js 另一个是lower-case.html

文件lower-case.js是将msg.payload转化为消息,使用

js文件解析

lower-case.js

代码归“拿我格子衫来”所属
module.exports = function(RED) {
    function LowerCaseNode(config) {
        RED.nodes.createNode(this,config);
        var node = this;
        node.on('input', function(msg) {
            msg.payload = msg.payload.toLowerCase();
            node.send(msg);
        });
    }
    RED.nodes.registerType("lower-case",LowerCaseNode);
}

下面详细讲解一下这段代码,首先使用module.exports导出一个函数这个函数有一个参数, 定义为叫做RED 其实就是Node-RED的上下文。
然后其中定义一个方法叫做LowerCaseNode 这个方法就是将一个单纯的payload转化的函数。
方法体内有一句

RED.nodes.createNode(this,config);

这一句是一个闭包,直接使用父作用域的RED参数,创建一个Node。 那么this是什么那? this就是上下文,就是flow。 config是该节点的配置项。

紧接着是一个事件监听

node.on('input', function(msg) {})

由于node是基于事件驱动,所以在flow的上下文中,都是用过一系列的监听和触发来进行传递上下文的.
每个节点中都会有这样一个监听函数 有的是这样写的

this.on("input", function(msg, send, done) {})

这里的this其实就是node。

toLowerCase()payload转化,再次赋值给msg.payload

那么这个事件监听函数输出的是什么那,在最后一行给出了答案

node.send(msg);

将msg传递出去,向下流转。

还有最后一句代码

RED.nodes.registerType("lower-case",LowerCaseNode);

这行代码就是注册一个节点。 第一个参数是节点的名称,第二是一个函数。

这里还有一个inject节点的源码,大家可以看一下,相信稍微学过一些js的同学都能看懂

代码归“拿我格子衫来”所属
module.exports = function(RED) {
    "use strict";
    var cron = require("cron");

    function InjectNode(n) {
        RED.nodes.createNode(this,n);

        /* Handle legacy */
        if(!Array.isArray(n.props)){
            n.props = [];
            n.props.push({
                p:'payload',
                v:n.payload,
                vt:n.payloadType
            });
            n.props.push({
                p:'topic',
                v:n.topic,
                vt:'str'
            });
        } else {
            for (var i=0,l=n.props.length; i<l; i++) {
                if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) {
                    n.props[i].v = n.payload;
                    n.props[i].vt = n.payloadType;
                } else if (n.props[i].p === 'topic' && n.props[i].vt === 'str' && !n.props[i].hasOwnProperty('v')) {
                    n.props[i].v = n.topic;
                }
            }
        }

        this.props = n.props;
        this.repeat = n.repeat;
        this.crontab = n.crontab;
        this.once = n.once;
        this.onceDelay = (n.onceDelay || 0.1) * 1000;
        this.interval_id = null;
        this.cronjob = null;
        var node = this;

        node.props.forEach(function (prop) {
            if (prop.vt === "jsonata") {
                try {
                    var val = prop.v ? prop.v : "";
                    prop.exp = RED.util.prepareJSONataExpression(val, node);
                }
                catch (err) {
                    node.error(RED._("inject.errors.invalid-expr", {error:err.message}));
                    prop.exp = null;
                }
            }
        });

        if (node.repeat > 2147483) {
            node.error(RED._("inject.errors.toolong", this));
            delete node.repeat;
        }

        node.repeaterSetup = function () {
            if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
                this.repeat = this.repeat * 1000;
                if (RED.settings.verbose) {
                    this.log(RED._("inject.repeat", this));
                }
                this.interval_id = setInterval(function() {
                    node.emit("input", {});
                }, this.repeat);
            } else if (this.crontab) {
                if (RED.settings.verbose) {
                    this.log(RED._("inject.crontab", this));
                }
                this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true);
            }
        };

        if (this.once) {
            this.onceTimeout = setTimeout( function() {
                node.emit("input",{});
                node.repeaterSetup();
            }, this.onceDelay);
        } else {
            node.repeaterSetup();
        }

        this.on("input", function(msg, send, done) {
            var errors = [];

            this.props.forEach(p => {
                var property = p.p;
                var value = p.v ? p.v : '';
                var valueType = p.vt ? p.vt : 'str';

                if (!property) return;

                if (valueType === "jsonata") {
                    if (p.exp) {
                        try {
                            var val = RED.util.evaluateJSONataExpression(p.exp, msg);
                            RED.util.setMessageProperty(msg, property, val, true);
                        }
                        catch  (err) {
                            errors.push(err.message);
                        }
                    }
                    return;
                }
                try {
                    RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
                } catch (err) {
                    errors.push(err.toString());
                }
            });

            if (errors.length) {
                done(errors.join('; '));
            } else {
                send(msg);
                done();
            }
        });
    }

    RED.nodes.registerType("inject",InjectNode);

    InjectNode.prototype.close = function() {
        if (this.onceTimeout) {
            clearTimeout(this.onceTimeout);
        }
        if (this.interval_id != null) {
            clearInterval(this.interval_id);
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
        } else if (this.cronjob != null) {
            this.cronjob.stop();
            if (RED.settings.verbose) { this.log(RED._("inject.stopped")); }
            delete this.cronjob;
        }
    };

    RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) {
        var node = RED.nodes.getNode(req.params.id);
        if (node != null) {
            try {
                node.receive();
                res.sendStatus(200);
            } catch(err) {
                res.sendStatus(500);
                node.error(RED._("inject.failed",{error:err.toString()}));
            }
        } else {
            res.sendStatus(404);
        }
    });
}

html解析

lower-case.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="fa fa-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>

html的内容是用于配置节点以及节点的备注解释。

代码归“拿我格子衫来”所属
RED.nodes.registerType('lower-case',{  // 注册一个节点,当拖动节点到编辑区域中
  category: 'function',  // 节点归属于哪一个分类
  color: '#a6bbcf',  // 颜色
  defaults: {
      name: {value:""}   // 输入框的值
  },
  inputs:1,  // 能否输入   1是,0 否
  outputs:1,  // 能否输出 1是,0 否
  icon: "file.png",  // 节点的图标
  label: function() {
    return this.name||"lower-case";  // label 的默认值
  }
});

标签<script type="text/html" data-template-name="lower-case"> 中的内容是用于显示在节点抽屉中的,
点击节点即可以显示

标签<script type="text/html" data-help-name="lower-case"> 中的内容是用于显示在帮助文档中的。

package.json

{
  "name" : "node-red-contrib-example-lower-case",
  "node-red" : {
      "nodes": {
          "lower-case": "lower-case.js"
      }
  }
}

如何安装改节点那

在node-red项目根目录中执行

npm install <location of node module>

如果是要在本地安装,linux下安装,用这个

cd ~/.node-red
npm install ~/dev/node-red-contrib-example-lower-case

windows下安装,用这个

cd C:\Users\my_name\.node_red
npm install C:\Users\my_name\Documents\GitHub\node-red-contrib-example-lower-case
拿我格子衫来 CSDN认证博客专家 拿我格子衫来 范马勇次郎 琦玉君
积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步;驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。
已标记关键词 清除标记
<p> 本课程通过一步步的实践演示,带领大家在开源项目的基础上,搭建随心所欲的物联网与智能家居平台。在过程实践中,大家会学习与应用到linux、python、云服务、图像识别、智能语音、单片机、数据库、前端开发等多方面的知识,帮助大家成为IT的全栈工程师。以实战为导向结合物联网各类知识要点学习经典框架进行项目实战,快速掌握智能家居、家庭自动、物联网等必备基础与实战技巧。带你从零玩转智能家居,了解物联网的整体格局,将零散的知识点通过项目快速串联提升自身成就感 </p> <p> 【更新规则】<br />  视频与参考文档内容,随时更新,与最新的软件版本/云服务环境匹配。 </p> <p> <br /> </p> <p> 【课程特色】<br /> 1.通俗易懂,快速入门<br /> 对物联网、智能家居学习经典实践项目结合技术推导进行形象解释,实例演示。<br /> 2. Python主导,实用高效<br /> 使用物联网领域最主流语言Python及其homeassistant 开源家庭自动框架作为课程核心工具。<br /> 3. 案例为师,实战护航<br /> 基于真实操作展示,从零开始结合homeassistant与python自创组件、树莓派或者nas完成整个案例实战。<br /> 4. 持续更新,一劳永逸<br /> 会伴随homeassistant的更新与DIY实战项目课程会支持更新下去,逐步加入更多算法与案例。 </p> <p> <br /> </p> <p> 【联系我们】<br /> 官方网站:https://www.hachina.io<br /> QQ学习讨论群(仅限学员加入):741140729 </p> <p> <img src="https://img-bss.csdn.net/201907040403375571.jpg" alt="" /> </p> <p> <br /> </p>
相关推荐
<p> <span style="color:#313d54;font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;font-size:16px;background-color:#ffffff;">Node-RED是工业网物联网的重要组成部分,我最开始接触Node-RED,也算是一个偶然的机会吧,上班后领导安排我的第一个任务就是调研一下Node-RED,我之后上网查了一下,那个时候网上相对于Node-RED的资料也比较少,只知道它是IBM公司的一个开源项目。直到最近,发现许多大公司的产品都支持Node-RED,比如西门子公司的IoT2000,研华公司的WISE PaaS 网关,美国OPTO 22等设备中都安装了Node-RED,表明它在工业物联网和控制中已经广泛应用了。</span> </p> <p> <img src="https://img-bss.csdnimg.cn/202010200935441926.png" alt="" width="1316" height="524" /> </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> 那么工业物联网为什么要用它?它又处于工业物联网那个层次?它具有哪些特性?它帮助物联网解决了什么问题?为什么说它是柔性动态可重构的解决方案呢? </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;">   </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;">   </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> 本门课程,老师将带领你从Node-RED的发展,工业物联网定位开始讲解,并带领着大家进行手把手安装Node-RED,实际操作演练Node-RED,并搭建一个物联网小平台,给大家带来更好的学习效果。 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> <img src="https://img-bss.csdnimg.cn/202010200936289051.png" alt="" width="1841" height="948" /> </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> <img src="https://img-bss.csdnimg.cn/202010200936561564.png" alt="" width="1920" height="977" /> </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;">   </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;">   </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> 为了能够让小伙伴们快速了解本门课程的结构,本门课程从以下几个方面展开: </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED入门 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED安装与配置 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED教学实战 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED的优势与不足 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED能为我们带来什么 </p> <p style="font-family:'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif;color:#313d54;font-size:16px;background-color:#ffffff;"> Node-RED总结与展望 </p>
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页