Node Maker

A set of subflow nodes that can be used to quickly create a new Node-RED node, the property panel, and the node's behavior. You can use Node Maker to easily create an initial template or the entire work necessary for creating a Node-RED node. Find screenshots and documentation on the GitHub repo here.

Why Node Maker?

Time and time again I've found that I need to create nodes with a rich property panel user interface. While Node-RED's fantastic editor includes the ability to make nodes and package them via it's subflow functionality it falls short in the key area of designing a rich user interface. Often this pushes users into creating nodes from scratch that can furnish full control over the property UI:

  • Place multiple input fields and types next to each other.
  • Implement custom controls; i.e. option boxes, selectboxes with multiselect, etc.
  • Create groups of repeating input fields aka "editableLists".
  • Design a tabbed interface to pack more 'pages' of input fields.
  • Customize the initial size of the property panel.

Now with Node Maker you can create rich property panel combinations without even having to *leave the Node-RED Editor interface!

* technically you'll have to refresh the webpage to see newly created nodes in the palette

Requirements

Node Maker is currently being developed under Ubuntu Linux; it may work just as well on macOS. It does NOT currently work on Windows; however, a ticket has been opened for that possibility here (ticket #1).

How to Install Node Maker

More information can be found on the GitHub repo

[{"id":"9771d13dd19bf60f","type":"subflow","name":"use templts.","info":"If you use any of the code templates, be sure to place this node before it.","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"ebd81684ada1c31c"}]}],"out":[{"x":420,"y":100,"wires":[{"id":"6bb34952a24fe530","port":0}]}],"env":[],"meta":{},"color":"#F3B567","icon":"font-awesome/fa-file-code-o"},{"id":"ebd81684ada1c31c","type":"template","z":"9771d13dd19bf60f","name":"re-size fix","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Allow dynamic re-size after init. appearance \n            setTimeout(function () {\n                $('#node-props').css('width', '100%');\n            }, 30);","output":"str","x":180,"y":40,"wires":[["6bb34952a24fe530"]]},{"id":"6bb34952a24fe530","type":"function","z":"9771d13dd19bf60f","name":"compose defaults, othercode","func":"msg._oneditprepare.push(msg.payload);\n\nString.prototype.delRightMost = function (sFind) {\n    for (var i = this.length; i >= 0; i = i - 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(0, f);\n            break;\n        }\n    }\n    return this;\n};\nString.prototype.getRightMost = function (sFind) {\n    for (var i = this.length; i >= 0; i = i - 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(f + sFind.length, f + sFind.length + this.length);\n        }\n    }\n    return this;\n};\nString.prototype.delLeftMost = function (sFind) {\n    for (var i = 0; i < this.length; i = i + 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(f + sFind.length, f + sFind.length + this.length);\n            break;\n        }\n    }\n    return this;\n};\nString.prototype.getLeftMost = function (sFind) {\n    for (var i = 0; i < this.length; i = i + 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(0, f);\n            break;\n        }\n    }\n    return this;\n};\nvar indent = \" \".repeat(4);\nvar tabIndent = \"\";\nvar edit_dialog = \" \".repeat(8) + '<div class=\"form-row\">' + \"\\n\";\nvar cTotal = 0;\nvar nItems = 0;\nvar initTab = false;\nvar firstTabName = '';\nvar bInRGroup = false;    \nmsg.edit_dialog.forEach(function(item, index) {\n    if (item.indexOf('\"type\":\"tab_definition\"') == -1 && item.indexOf('form-row rgroup') == -1) {\n        item = item.replace(/^/gm, tabIndent);\n        item = item.replace(/^/gm, tabIndent);\n        if (bInRGroup) {\n            item = item.replace(/^/gm, \" \".repeat(4));\n        }\n    }\n    if (item.indexOf('<div class=\"form-row rgroup\"') > -1) {\n        edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">').trimEnd() + \"\\n\";\n        item += \" \".repeat(4) + '<div class=\"form-row\">' + \"\\n\";\n        item = item.replace(/^/gm, \" \".repeat(8) + tabIndent).trimEnd() + \"\\n\";\n        bInRGroup = true;\n        edit_dialog += item;\n        return;\n    }\n    if (item.indexOf('<!--form-row rgroup-->') > -1) {\n        edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">').trimEnd() + \"\\n\";\n        edit_dialog += \" \".repeat(8) + tabIndent + \"</div><!--form-row rgroup-...-template-->\\n\";\n        if (index < msg.edit_dialog.length - 1) {\n            edit_dialog += indent + indent + tabIndent + '<div class=\"form-row\">' + \"\\n\";\n        }\n        bInRGroup = false;\n        return;\n    }\n    if (item.indexOf('\"type\":\"tab_definition\"') > -1) {\n        let tab = JSON.parse(item);\n        if (initTab == false) {\n            \n            // Inject the tab row\n            let r = edit_dialog.getRightMost('<div class=\"form-row\">');\n            edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">');\n            edit_dialog += '<div class=\"form-row func-tabs-row\">' + r;\n            edit_dialog += indent + indent + indent + '<ul style=\"min-width: 600px; margin-bottom: 20px;\" id=\"func-tabs\"></ul>' + \"\\n\";\n            edit_dialog += indent + indent + \"</div><!--func-tabs-row-->\\n\";\n            edit_dialog += indent + indent + '<div id=\"func-tabs-content\" style=\"min-height: calc(100% - 95px);\">' + \"\\n\";\n\n            // Start first tab\n            edit_dialog += indent + indent + indent + '<div id=\"func-tab-' + tab.name + '\" style=\"display:none\">' + \"\\n\";\n            edit_dialog += indent + indent + indent + indent + '<div class=\"form-row\">' + \"\\n\";\n            tabIndent = \" \".repeat(8);\n\n            // Init tab system\n            let code = \"// Prepare tab system\\n\";\n            code += \"var tabs = RED.tabs.create({\\n\";\n            code += \"    id: 'func-tabs',\\n\";\n            code += \"    onchange: function(tab) {\\n\";\n            code += \"        $('#func-tabs-content').children().hide();\\n\";\n            code += \"        $('#' + tab.id).show();\\n\";\n            code += \"    }\\n\";\n            code += \"});\\n\\n\";\n\n            // Add first tab code\n            code += \"// Add first tab, \" + tab.name + \"\\n\";\n            code += \"tabs.addTab({\\n\";\n            code += \"    id: 'func-tab-\" + tab.name + \"',\\n\";\n            code += \"    iconClass: 'fa fa-\" + tab.icon + \"',\\n\";\n            code += \"    label: '\" + tab.label + \"'\\n\";\n            code += \"});\\n\\n\";\n            firstTabName = tab.name;\n            code = code.replace(/^/gm, \" \".repeat(12));\n            msg._oneditprepare.push(code);\n            initTab = true;\n        }else{\n\n            // Add subsequent tabs \n            let r = edit_dialog.getRightMost('    <div class=\"form-row\"');\n            edit_dialog = edit_dialog.delRightMost('    <div class=\"form-row\"');\n            edit_dialog += \"</div><!--func-tab-tab#-->\\n\"; // close out last tab\n            edit_dialog += \" \".repeat(12) + '<div id=\"func-tab-' + tab.name + '\" style=\"display:none\">' + \"\\n\";\n            edit_dialog += \" \".repeat(16) + '<div class=\"form-row\"' + r;\n\n            // Add additional tab code\n            let code = \"// Add tab, \" + tab.name + \"\\n\";            \n            code += \"tabs.addTab({\\n\";\n            code += \"    id: 'func-tab-\" + tab.name + \"',\\n\";\n            code += \"    iconClass: 'fa fa-\" + tab.icon + \"',\\n\";\n            code += \"    label: '\" + tab.label + \"'\\n\";\n            code += \"});\\n\\n\";\n            code = code.replace(/^/gm, \" \".repeat(12));\n            msg._oneditprepare.push(code);\n        }        \n    }else{\n        if (tabIndent == '') {\n            item = item.replace(/^/gm, indent + indent + indent);\n        }else{\n            item = item.replace(/^/gm, indent);\n        }\n        let c = item.delLeftMost('col-').getLeftMost('\"').getLeftMost(' ');\n        cTotal += Number(c);\n        if (cTotal >= 83) {\n            let m = 5 * nItems;\n            if (m > 0) {\n                item = item.replace(\n                    'class=\"col ',\n                    'style=\"margin-right:-' + m.toString() + 'px;\" class=\"col '\n                );\n            }\n            if (nItems == 3) {\n                let r = edit_dialog.getRightMost('<div class=\"form-row\">');\n                edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">');\n                edit_dialog += '<div class=\"form-row\" style=\"margin-right:10px;\">' + r;\n            }\n            edit_dialog += item + \"\\n\";\n            if (index < msg.edit_dialog.length - 1) {\n                if (bInRGroup) {\n                    edit_dialog += \" \".repeat(4);\n                }\n                edit_dialog += indent + indent + tabIndent + \"</div><!--form-row-->\\n\";\n                edit_dialog += indent + indent + tabIndent + '<div class=\"form-row\">' + \"\\n\";\n                cTotal = 0;\n                nItems = 0;\n            }\n        } else {\n            edit_dialog += item + \"\\n\";\n            nItems++;\n        }\n    }\n});\nif (initTab) { \n    edit_dialog += \" \".repeat(16) + \"</div><!--form-row-->\\n\";\n    edit_dialog += \" \".repeat(12) + \"</div><!--func-tab-tab#-->\\n\";\n    edit_dialog += \" \".repeat(8) + \"</div><!--func-tabs-content-->\";\n\n    // Activate the first tab if present\n    let code = \" \".repeat(12) + 'tabs.activateTab(\"func-tab-' + firstTabName + '\");' + \"\\n\";\n    msg._oneditprepare.push(code);\n}else{\n    edit_dialog += \" \".repeat(8) + \"</div><!--form-row-->\";\n}\nmsg.edit_dialog = edit_dialog;\n\nlet defaults = '';\nmsg.defaults.forEach(function(item, index) {\n    defaults += \"            \" + item;\n    if (index < msg.defaults.length -1) {\n        defaults += \",\\n\";\n    }else{\n        defaults += \"\\n\";\n    }\n});\nmsg.defaults = defaults;\nmsg.oneditprepare = \"\\n\" + msg._oneditprepare.join(\"\\n\");\nmsg.oneditresize = \"\\n\" + msg._oneditresize.join(\"\\n\");\nmsg.oneditsave = \"\\n\" + msg._oneditsave.join(\"\\n\");\nmsg.oneditcancel = \"\\n\" + msg._oneditcancel.join(\"\\n\");\nmsg.defaults = msg.defaults.slice(0, -1);\nvar othercode = \"\";\nfor (var key in msg._othercode) {\n    var value = msg._othercode[key];\n    othercode += value;\n}\nmsg.othercode = \"\\n\" + othercode;\nmsg.runtimecode = \"\\n\" + msg.runtimecode.replace(/^/gm, \"    \".repeat(4));\nmsg.useTemplates = true;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":240,"y":100,"wires":[[]]},{"id":"7996feeba4e5f327","type":"subflow","name":"spinner","info":"","category":"node maker","in":[{"x":60,"y":40,"wires":[{"id":"62b4d0382560364d"}]}],"out":[{"x":640,"y":40,"wires":[{"id":"79f6b4d4f4df2a1a","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75 sml-lbl"},{"l":{"en-US":"2/3 row"},"v":"col col-66 sml-lbl"},{"l":{"en-US":"1/2 row"},"v":"col col-50 sml-lbl"},{"l":{"en-US":"1/3 row"},"v":"col col-33 sml-lbl"},{"l":{"en-US":"1/4 row"},"v":"col col-25 sml-lbl"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"num","value":"","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["num"]}}},{"name":"field_min","type":"num","value":"0","ui":{"label":{"en-US":"Min"},"type":"spinner"}},{"name":"field_max","type":"num","value":"100","ui":{"label":{"en-US":"Max"},"type":"spinner"}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-sort"},{"id":"62b4d0382560364d","type":"function","z":"7996feeba4e5f327","name":"spinner","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n} else {\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nmsg.field_default = env.get('field_default');\nlet fc = env.get('field_columns');\nif (msg.field_label == '') {\n    fc += ' no-label';\n}\nmsg.field_min = env.get('field_min');\nmsg.field_max = env.get('field_max');\nmsg.field_columns = fc;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":160,"y":40,"wires":[["07a16db04c64dc2c"]]},{"id":"07a16db04c64dc2c","type":"template","z":"7996feeba4e5f327","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    {{{field_label_elm}}}\n    <input type=\"text\" id=\"node-input-{{field_name}}\">\n    {{{field_has_hidden_type}}}\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["85eb05fc963311be"]]},{"id":"85eb05fc963311be","type":"function","z":"7996feeba4e5f327","name":"function 39","func":"msg.edit_dialog.push(msg.payload);\nmsg.defaults.push(msg.field_name + ': {value:' + msg.field_default + '}');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["5156716df3b6fd1d"]]},{"id":"5156716df3b6fd1d","type":"template","z":"7996feeba4e5f327","name":"oneditprepare","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"            // Prepare spinner {{field_name}}\n            $(\"#node-input-{{field_name}}\").spinner({ min: {{field_min}}, max: {{field_max}} });","output":"str","x":440,"y":40,"wires":[["79f6b4d4f4df2a1a"]]},{"id":"79f6b4d4f4df2a1a","type":"function","z":"7996feeba4e5f327","name":"function 40","func":"if (!msg.hasOwnProperty('rgroup_name')) {\n    msg._oneditprepare.push(msg.payload);\n} else {\n    msg.rgroup_details.push({\n        field_name: msg.field_name,\n        field_min: msg.field_min,\n        field_max: msg.field_max,\n        field_default: msg.field_default,\n        field_types: ''\n    });\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":100,"wires":[[]]},{"id":"8d72584ecbd0d019","type":"subflow","name":"confignode","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"b0d3a03a3d6ca839"}]}],"out":[{"x":420,"y":160,"wires":[{"id":"084f5d08fdf5e058","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str"]}}},{"name":"field_placeholder","type":"str","value":"","ui":{"label":{"en-US":"Placeholder"},"type":"input","opts":{"types":["str"]}}},{"name":"confignode_type","type":"str","value":"","ui":{"label":{"en-US":"Type"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-gear"},{"id":"b0d3a03a3d6ca839","type":"function","z":"8d72584ecbd0d019","name":"function 33","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n} else {\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nmsg.field_default = env.get('field_default');\nmsg.field_placeholder = env.get('field_placeholder');\nmsg.field_columns = env.get('field_columns');\nmsg.confignode_type = env.get('confignode_type');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["901ff16324846881"]]},{"id":"901ff16324846881","type":"template","z":"8d72584ecbd0d019","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    {{{field_label_elm}}}\n    <input type=\"text\" id=\"node-input-{{field_name}}\" placeholder=\"{{{field_placeholder}}}\">\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["084f5d08fdf5e058"]]},{"id":"084f5d08fdf5e058","type":"function","z":"8d72584ecbd0d019","name":"function 34","func":"msg.edit_dialog.push(msg.payload);\nmsg.defaults.push(msg.field_name + ': {value:\"' + msg.field_default + '\", type: \"' + msg.confignode_type + '\"}');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[[]]},{"id":"162e81fc107019c0","type":"subflow","name":"end rgroup","info":"","category":"node maker","in":[{"x":60,"y":40,"wires":[{"id":"86ccecca4fabac7f"}]}],"out":[{"x":560,"y":100,"wires":[{"id":"f7e683ae5d08c682","port":0}]}],"env":[],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-reorder"},{"id":"86ccecca4fabac7f","type":"function","z":"162e81fc107019c0","name":"function 32","func":"msg.edit_dialog.push(\"<!--form-row rgroup-->\");\nmsg.rgroup_details = JSON.stringify(msg.rgroup_details, null, 4);\nmsg.rgroup_details = msg.rgroup_details.replace(/^/gm, \"    \".repeat(5)).trim();\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["569fb952e0619afb"]]},{"id":"569fb952e0619afb","type":"template","z":"162e81fc107019c0","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare editableList for {{rgroup_name}}\n            $(\"#node-input-{{rgroup_name}}-container\").css('height', '{{rgroup_height}}px').editableList({\n                addItem: function (container, i, opt) {\n                    let repeatingFields = {{{rgroup_details}}};\n                    let html = $('#rgroup-{{rgroup_name}}-template').html();\n                    repeatingFields.forEach(function(rf) {\n                        let fniType = \"{{rgroup_name}}\" + rf.field_name + \"Type\" + String(i).padStart(2, '0');\n                        html = html.replaceAll('id=\"node-input-' + rf.field_name + \"Type\" + '\"', 'id=\"node-input-' + fniType + '\"');\n                        let fni = \"{{rgroup_name}}\" + rf.field_name + String(i).padStart(2, '0');\n                        html = html.replaceAll('id=\"node-input-' + rf.field_name + '\"', 'id=\"node-input-' + fni + '\"');\n                        html = html.replaceAll('for=\"node-input-' + rf.field_name + '\"', 'for=\"node-input-' + fni + '\"');\n                        container.html(html);\n                    });\n                    repeatingFields.forEach(function (rf) {\n                        let fni = \"{{rgroup_name}}\" + rf.field_name + String(i).padStart(2, '0');\n                        if (rf.field_types != '') {\n                            let fniType = \"{{rgroup_name}}\" + rf.field_name + \"Type\" + String(i).padStart(2, '0');\n                            $(\"#node-input-\" + fni).typedInput({\n                                type: rf.field_default_type,\n                                types: rf.field_types,\n                                typeField: \"#node-input-\" + fniType\n                            });\n                        }else if(rf.hasOwnProperty('field_min')) {\n                            $(\"#node-input-\" + fni).spinner({ min: rf.field_min, max: rf.field_max });\n                        }\n\n                        // Restore entry\n                        if (Object.keys(opt).length != 0) {\n                            if ($(\"#node-input-\" + fni).attr('type') == 'hidden') {\n                                $(\"#node-input-\" + fni).typedInput('type', opt[rf.field_name + 'Type']);\n                                $(\"#node-input-\" + fni).typedInput('value', opt[rf.field_name]);\n                            }else{\n                                if ($(\"#node-input-\" + fni).attr('type') == 'checkbox') {\n                                    $(\"#node-input-\" + fni).prop('checked', opt[rf.field_name]);\n                                }else{\n                                    $(\"#node-input-\" + fni).val(opt[rf.field_name]);\n                                }\n                            }\n                        }else{\n                            if ($(\"#node-input-\" + fni).attr('type') == 'hidden') {\n                                $(\"#node-input-\" + fni).typedInput('value', rf.field_default);\n                            } else {\n                                if ($(\"#node-input-\" + fni).attr('type') != 'checkbox') {\n                                    $(\"#node-input-\" + fni).val(rf.field_default);\n                                }\n                            }\n                        }\n                    });\n                },\n                // sortItems: function (items) {\n                //    \n                // },\n                sortable: {{rgroup_sortable}},\n                removable: true\n            });\n\n            // Load prior editableList data for {{rgroup_name}}\n            if (this.{{rgroup_name}}) {\n                this.{{rgroup_name}}.forEach(function (m) {\n                    $('#node-input-{{rgroup_name}}-container').editableList('addItem', m);\n                });\n            }\n","output":"str","x":180,"y":100,"wires":[["f73faadc1c4b41c5"]]},{"id":"f73faadc1c4b41c5","type":"function","z":"162e81fc107019c0","name":"function 36","func":"msg._oneditprepare.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["c62255b98a430015"]]},{"id":"f7e683ae5d08c682","type":"function","z":"162e81fc107019c0","name":"function 37","func":"msg._oneditsave.push(msg.payload);\ndelete msg.rgroup_name;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":100,"wires":[[]]},{"id":"c62255b98a430015","type":"template","z":"162e81fc107019c0","name":"","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"            // Save editableList for {{rgroup_name}}\n            {{rgroup_name}}Items = $(\"#node-input-{{rgroup_name}}-container\").editableList('items');\n            let {{rgroup_name}} = [];\n            {{rgroup_name}}Items.each(function(){\n                let entry = {};\n                $(this).find('input[id]').each(function() {\n                    let id = $(this).attr('id').substring(11 + \"{{rgroup_name}}\".length);\n                    id = id.slice(0, -2);\n                    let v = false;\n                    if ($(this).attr('type') == 'checkbox') {\n                        v = $(this).prop('checked');\n                    }else{\n                        v = $(this).val();\n                    }\n                    entry[id] = v;\n                });\n                {{rgroup_name}}.push(entry);\n            });\n            this.{{rgroup_name}} = {{rgroup_name}};\n","output":"str","x":420,"y":40,"wires":[["f7e683ae5d08c682"]]},{"id":"973db1310fdebc19","type":"subflow","name":"start rgroup","info":"Use the start rgroup node to define a repeatable set of properties; also known as an “editableList” in Node-RED’s API. An example of Node-RED’s editable list is used in the property panel for the [switch and change nodes](https://nodered.org/docs/user-guide/nodes#change). Using the start rgroup and end rgroup nodes makes designing repeatable lists easy. \n\nSimply place the following compatible controls between the start/end rgroup nodes:\n\n* label\n* field\n* selectbox\n* checkbox\n\nThe start rgroup node has the following properties:\n\n* Group Name - A JavaScript compatible variable name (no spaces hyphens or punctuation).\n* Label - The actual text that will be used when displaying the label.\n* Icon - A font awesome icon that will appear to the left of the label. You can specify the icon name sans any ‘fa’ prefix; i.e. bathtub\n* Sortable - Determines if the contents of the editable list can be sorted.\n* Height - Determines the height of the listbox that contains the repeating group of fields.\n","category":"node maker","in":[{"x":60,"y":40,"wires":[{"id":"c3b7bb6404eb0c25"}]}],"out":[{"x":400,"y":40,"wires":[{"id":"b6607748c85f3e81","port":0}]}],"env":[{"name":"rgroup_name","type":"str","value":"","ui":{"label":{"en-US":"Group Name"},"type":"input","opts":{"types":["str"]}}},{"name":"rgroup_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"rgroup_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"rgroup_sortable","type":"bool","value":"true","ui":{"label":{"en-US":"Sortable"}}},{"name":"rgroup_height","type":"num","value":"150","ui":{"label":{"en-US":"Height"},"type":"input","opts":{"types":["num"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-reorder"},{"id":"c3b7bb6404eb0c25","type":"function","z":"973db1310fdebc19","name":"function 30","func":"msg.rgroup_name = env.get('rgroup_name');\nmsg.rgroup_label = env.get('rgroup_label');\nlet icon = env.get('rgroup_icon');\nif (icon != '') {\n    msg.rgroup_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.rgroup_icon = '';\n}\nif (msg.rgroup_label == '') {\n    msg.rgroup_label_elm = '';\n} else {\n    msg.rgroup_label_elm = '<label for=\"node-input-' + msg.rgroup_name + '\">';\n    msg.rgroup_label_elm += msg.rgroup_icon + msg.rgroup_label + '</label>';\n}\n//msg.field_columns = env.get('field_columns');\nmsg.rgroup_height = env.get('rgroup_height');\nmsg.rgroup_sortable = env.get('rgroup_sortable');\nmsg.rgroup_details = [];\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":190,"y":40,"wires":[["593c4461e9fefc86"]]},{"id":"593c4461e9fefc86","type":"template","z":"973db1310fdebc19","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div class=\"form-row rgroup\" style=\"margin-bottom:0;\">\n    {{{rgroup_label_elm}}}\n</div>\n<div class=\"form-row node-input-{{rgroup_name}}-container-row\">\n    <ol id=\"node-input-{{rgroup_name}}-container\"></ol>\n</div>\n<div id=\"rgroup-{{rgroup_name}}-template\" style=\"display:none\">\n","output":"str","x":180,"y":100,"wires":[["b6607748c85f3e81"]]},{"id":"b6607748c85f3e81","type":"function","z":"973db1310fdebc19","name":"function 35","func":"msg.defaults.push(msg.rgroup_name + ':[]');\nmsg.edit_dialog.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":190,"y":160,"wires":[[]]},{"id":"7d1e49e9a2f7337a","type":"subflow","name":"tab","info":"The tab node allows you to group your input fields and other controls in your node’s property panel under tabs. An example of tabs can be seen in the native function node. Simply wire a tab node between the start and finish nodes and before the UI nodes you wish to have appear under a tab. The tab node uses the following properties to determine its appearance: \n\n* Tab Name - A JavaScript compatible variable name (no spaces hyphens or punctuation).\n* Label - The actual text that will be used when displaying the label.\n* Icon - A font awesome icon that will appear to the left of the label. You can specify the icon name sans any ‘fa’ prefix; i.e. bathtub","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"97041e5e58e0392a"}]}],"out":[{"x":300,"y":40,"wires":[{"id":"97041e5e58e0392a","port":0}]}],"env":[{"name":"tab_name","type":"str","value":"","ui":{"label":{"en-US":"Tab Name"},"type":"input","opts":{"types":["str"]}}},{"name":"tab_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"tab_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-bookmark"},{"id":"97041e5e58e0392a","type":"function","z":"7d1e49e9a2f7337a","name":"function 26","func":"let tab = JSON.stringify({\n    type: 'tab_definition',\n    label: env.get('tab_label'),\n    icon: env.get('tab_icon'),\n    name: env.get('tab_name')\n});\nmsg.edit_dialog.push(tab);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[[]]},{"id":"3aaeb350dea9a1f1","type":"subflow","name":"buttongroup","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"f3308a3fdb36ac02"}]}],"out":[{"x":540,"y":100,"wires":[{"id":"d8c795cf2bf3ed56","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75"},{"l":{"en-US":"2/3 row"},"v":"col col-66"},{"l":{"en-US":"1/2 row"},"v":"col col-50"},{"l":{"en-US":"1/3 row"},"v":"col col-33"},{"l":{"en-US":"1/4 row"},"v":"col col-25"}]}}},{"name":"field_name","type":"str","value":"fruity","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_values","type":"str","value":"Apple:a,Banana:b,Coconut:c","ui":{"label":{"en-US":"Values"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"c","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-clone"},{"id":"f3308a3fdb36ac02","type":"function","z":"3aaeb350dea9a1f1","name":"function 27","func":"msg.field_name = env.get('field_name');\nmsg.field_default = env.get('field_default');\nmsg.field_values = env.get('field_values');\nlet fv = msg.field_values.split(',');\nlet bg = '';\nfv.forEach(function (v) {\n    v = v.split(':');\n    bg += '<button type=\"button\" class=\"red-ui-button toggle ' + msg.field_name + '\" value=\"' + v[1] + '\">' + v[0] + '</button>';\n});\nmsg.button_group = bg;\nmsg.field_columns = env.get('field_columns');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["804acd8df99594f0"]]},{"id":"647aa85626d8ec6c","type":"function","z":"3aaeb350dea9a1f1","name":"function 28","func":"msg.edit_dialog.push(msg.payload);\nmsg.defaults.push(msg.field_name + ': {value:\"' + msg.field_default+ '\"}');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["0db6dff4a160e916"]]},{"id":"804acd8df99594f0","type":"template","z":"3aaeb350dea9a1f1","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    <span class=\"button-group\">\n        {{{button_group}}}\n    </span>\n    <input type=\"hidden\" id=\"node-input-{{field_name}}\">\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["647aa85626d8ec6c"]]},{"id":"0db6dff4a160e916","type":"template","z":"3aaeb350dea9a1f1","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare button group {{field_name}}\n            $(\".{{field_name}}\").on(\"click\", function () {\n                $(\".{{field_name}}\").removeClass(\"selected\");\n                $(this).addClass(\"selected\");\n                $('#node-input-{{field_name}}').val($(this).val());\n            });\n            $('button.{{field_name}}[value=\"' + $('#node-input-{{field_name}}').val() + '\"]').addClass(\"selected\");\n","output":"str","x":400,"y":40,"wires":[["d8c795cf2bf3ed56"]]},{"id":"d8c795cf2bf3ed56","type":"function","z":"3aaeb350dea9a1f1","name":"function 29","func":"msg._oneditprepare.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":100,"wires":[[]]},{"id":"c70964ba34544694","type":"subflow","name":"button","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"5fd3369e032c68b0"}]}],"out":[{"x":380,"y":80,"wires":[{"id":"41f4b3c1077a06f2","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75"},{"l":{"en-US":"2/3 row"},"v":"col col-66"},{"l":{"en-US":"1/2 row"},"v":"col col-50"},{"l":{"en-US":"1/3 row"},"v":"col col-33"},{"l":{"en-US":"1/4 row"},"v":"col col-25"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Button Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Caption"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"button_size","type":"str","value":"red-ui-button","ui":{"label":{"en-US":"Size"},"type":"select","opts":{"opts":[{"l":{"en-US":"Regular"},"v":"red-ui-button btn-regular"},{"l":{"en-US":"Small"},"v":"red-ui-button red-ui-button-small"}]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-square-o"},{"id":"5fd3369e032c68b0","type":"function","z":"c70964ba34544694","name":"function 21","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nmsg.field_label_elm = msg.field_icon + msg.field_label;\nmsg.button_size = env.get('button_size');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["09a5b20f490d7474"]]},{"id":"09a5b20f490d7474","type":"template","z":"c70964ba34544694","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    <button id=\"btn_{{field_name}}\" type=\"button\" class=\"{{{button_size}}}\">{{{field_label_elm}}}</button>\n</div>","output":"str","x":160,"y":100,"wires":[["41f4b3c1077a06f2"]]},{"id":"41f4b3c1077a06f2","type":"function","z":"c70964ba34544694","name":"function 22","func":"msg.edit_dialog.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[[]]},{"id":"60de16a0fefb7ace","type":"subflow","name":"editor","info":"","category":"node maker","in":[{"x":60,"y":40,"wires":[{"id":"b39ca36c12467718"}]}],"out":[{"x":640,"y":40,"wires":[{"id":"d7e901b6cb4bab1b","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"editor_expand","type":"bool","value":"true","ui":{"label":{"en-US":"Allow Expanded"},"type":"input","opts":{"types":["bool"]}}},{"name":"editor_formatting","type":"str","value":"mustache:handlebars, JSON:json, JavaScript:javascript, CSS:css, Markdown:markdown, PHP:php, Python:python, SQL:sql, XML:xml, YAML:yaml, none:text","ui":{"label":{"en-US":"Formatting"}}},{"name":"editor_format_default","type":"str","value":"text","ui":{"label":{"en-US":"Format default"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-file-code-o"},{"id":"b39ca36c12467718","type":"function","z":"60de16a0fefb7ace","name":"function 16","func":"msg.field_columns = env.get('field_columns');\nmsg.field_name = env.get('field_name');\nmsg.field_default = env.get('field_default');\nmsg.editor_expand = env.get('editor_expand');\nmsg.editor_formatting = env.get('editor_formatting');\nmsg.field_label = env.get('field_label');\nmsg.editor_format_default = env.get('editor_format_default');\nif (msg.editor_format_default == '') {\n    msg.editor_format_default = 'text';\n}\nlet expandOffset = '';\nif (msg.editor_formatting == '' && msg.field_label == '') {\n    expandOffset = 'style=\"margin-top:-3px;\"';\n}\nif (msg.editor_expand) {\n    msg.editor_expand = '<button id=\"node-function-expand-' + msg.field_name + '\" class=\"red-ui-button red-ui-button-small\" ' + expandOffset + '><i class=\"fa fa-expand\"></i></button>' + \"\\n\";\n}else{\n    msg.editor_expand = '';\n}\nlet selectList = '';\nif (msg.editor_formatting != '') {\n    let formattingList = msg.editor_formatting.replaceAll(' ', '').split(',');\n    selectList = '<span data-i18n=\"node-red:template.label.format\">Syntax Highlight</span>:' + \"\\n\";\n    selectList += '<select id=\"node-input-format_' + msg.field_name + '\" style=\"width:110px; font-size: 10px !important;  height: 24px; padding:0;\">' + \"\\n\";\n    formattingList.forEach(function(fl) {\n        fl = fl.split(':');\n        selectList += \" \".repeat(4) + '<option value=\"' + fl[1] + '\">' + fl[0] + \"</option>\\n\";\n    });\n    selectList += \"</select>\\n\";\n}\nselectList = selectList.replace(/^/gm, \" \".repeat(8));\nmsg.editor_options = '';\nif (selectList != '' || msg.editor_expand != '') {\n    msg.editor_options += \"\\n\" + \" \".repeat(4) + '<div style=\"position:absolute;right:0;display:inline-block;text-align:right;font-size: 0.8em;z-index:100;\">' + \"\\n\";\n    msg.editor_options += selectList;\n    msg.editor_options += msg.editor_expand;\n    msg.editor_options += \" \".repeat(4) + \"</div>\";\n}\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n} else {\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":210,"y":40,"wires":[["304b11e03551ef93"]]},{"id":"304b11e03551ef93","type":"template","z":"60de16a0fefb7ace","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    {{{field_label_elm}}}{{{editor_options}}}\n    <div style=\"height: 250px; min-height:150px;\" class=\"node-text-editor\" id=\"node-input-{{field_name}}-editor\"></div>\n</div>","output":"str","x":200,"y":80,"wires":[["dfc2e6d83eaa08f0"]]},{"id":"dfc2e6d83eaa08f0","type":"function","z":"60de16a0fefb7ace","name":"function 17","func":"msg.defaults.push(msg.field_name + ': {value:\"' +  msg.field_default.replace('\"', '\\\"') + '\"}');\nmsg.defaults.push(msg.field_name + '_format: {value:\"' + msg.editor_format_default + '\"}');\nmsg.edit_dialog.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":210,"y":120,"wires":[["433c2ca4801d1b7f"]]},{"id":"433c2ca4801d1b7f","type":"template","z":"60de16a0fefb7ace","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare code editor {{field_name}}\n            this.editor_{{field_name}} = RED.editor.createEditor({\n                id: 'node-input-{{field_name}}-editor',\n                mode: 'ace/mode/text',\n                value: this.{{field_name}} || \"{{{field_default}}}\"\n            });\n            $('#node-input-{{field_name}}-editor').parent().css('position', 'relative');\n            $(\"#node-function-expand-{{field_name}}\").on(\n                \"click\", \n                expandButtonClickHandler(\n                    this.editor_{{field_name}},\n                    '{{field_name}}',\n                    '{{{field_label_elm}}}'\n                )\n            );\n            RED.popover.tooltip($(\"#node-function-expand-{{field_name}}\"), RED._(\"node-red:common.label.expand\"));\n            let editor_{{field_name}} = this.editor_{{field_name}};\n            $(\"#node-input-format_{{field_name}}\").on(\"change\", function () {\n                var mod = \"ace/mode/\" + $(\"#node-input-format_{{field_name}}\").val();\n                editor_{{field_name}}.getSession().setMode({\n                    path: mod,\n                    v: Date.now()\n                });\n            });\n            $('#node-input-format_{{field_name}}').val(this.{{field_name}}_format || '{{editor_format_default}}').trigger('change');\n","output":"str","x":460,"y":40,"wires":[["7a21bd543c1073c8"]]},{"id":"7a21bd543c1073c8","type":"function","z":"60de16a0fefb7ace","name":"function 18","func":"msg._oneditprepare.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":80,"wires":[["312a31b33615964e"]]},{"id":"312a31b33615964e","type":"template","z":"60de16a0fefb7ace","name":"oneditsave","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Save code editor {{field_name}}\n            this.{{field_name}}_format = $('#node-input-format_{{field_name}}').val();\n            this.{{field_name}} = this.editor_{{field_name}}.getValue();\n            this.editor_{{field_name}}.destroy();\n            delete this.editor_{{field_name}};\n","output":"str","x":450,"y":140,"wires":[["7ecf6f5b801255fe"]]},{"id":"7ecf6f5b801255fe","type":"function","z":"60de16a0fefb7ace","name":"function 19","func":"msg._oneditsave.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":180,"wires":[["35eacbc0f8e14635"]]},{"id":"35eacbc0f8e14635","type":"template","z":"60de16a0fefb7ace","name":"oneditcancel","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Cancel code editor {{field_name}}\n            this.editor_{{field_name}}.destroy();\n            delete this.editor_{{field_name}};\n","output":"str","x":450,"y":240,"wires":[["decf02259cf4f980"]]},{"id":"decf02259cf4f980","type":"function","z":"60de16a0fefb7ace","name":"function 20","func":"msg._oneditcancel.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":280,"wires":[["7e05fb4fc292719b"]]},{"id":"7e05fb4fc292719b","type":"template","z":"60de16a0fefb7ace","name":"othercode","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"\n/**\n * Expand behavior for code editor \n */\nfunction expandButtonClickHandler(editor, fieldName, fieldLabelElm) {\n    $('#node-function-expand-' + fieldName).click(function() {\n        setTimeout(function() {\n            $('ul.red-ui-tray-breadcrumbs li:nth-child(2)').html(fieldLabelElm);\n        }, 500);\n    });\n    return function (e) {\n        e.preventDefault();\n        var value = editor.getValue();\n        editor.saveView(`inside function-expandButtonClickHandler ${editor.__stateId}`);\n        RED.editor.editJavaScript(\n            {\n                value: value,\n                width: \"Infinity\",\n                stateId: editor.__stateId,\n                mode: \"ace/mode/\" + $('#node-input-format_' + fieldName).val(),\n                focus: true,\n                cancel: function () {\n                    setTimeout(function () {\n                        editor.focus();\n                    }, 250);\n                },\n                complete: function (v, cursor) {\n                    editor.setValue(v, -1);\n                    setTimeout(function () {\n                        editor.restoreView();\n                        editor.focus();\n                    }, 250);\n                }\n            }\n        );\n    }\n}\n","output":"str","x":450,"y":340,"wires":[["af0afbba9a411b15"]]},{"id":"af0afbba9a411b15","type":"function","z":"60de16a0fefb7ace","name":"function 5","func":"msg._othercode.expandEditor = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":380,"wires":[["d7e901b6cb4bab1b"]]},{"id":"d7e901b6cb4bab1b","type":"junction","z":"60de16a0fefb7ace","x":540,"y":380,"wires":[[]]},{"id":"5904ee94d99998b2","type":"subflow","name":"textarea","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"3a8bd58bf294fc23"}]}],"out":[{"x":420,"y":160,"wires":[{"id":"56c0f86d94e65e2f","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"}]}}},{"name":"textarea_rows","type":"num","value":"4","ui":{"label":{"en-US":"Rows"},"type":"spinner"}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"field_placeholder","type":"str","value":"","ui":{"label":{"en-US":"Placeholder"},"type":"input","opts":{"types":["str"]}}},{"name":"field_required","type":"bool","value":"false","ui":{"label":{"en-US":"Required"},"type":"input","opts":{"types":["bool"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-file-text-o"},{"id":"3a8bd58bf294fc23","type":"function","z":"5904ee94d99998b2","name":"function 14","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n} else {\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nmsg.field_placeholder = env.get('field_placeholder');\nmsg.field_required = env.get('field_required');\nmsg.field_default = env.get('field_default');\nmsg.field_columns = env.get('field_columns');\nmsg.textarea_rows = env.get('textarea_rows');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["accd61ce5b3e8df8"]]},{"id":"accd61ce5b3e8df8","type":"template","z":"5904ee94d99998b2","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}} txtarea\">\n    {{{field_label_elm}}}\n    <textarea id=\"node-input-{{field_name}}\" name=\"node-input-{{field_name}}\" rows=\"{{textarea_rows}}\" placeholder=\"{{{field_placeholder}}}\">\n        {{{field_default}}}\n    </textarea>\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["56c0f86d94e65e2f"]]},{"id":"56c0f86d94e65e2f","type":"function","z":"5904ee94d99998b2","name":"function 15","func":"msg.edit_dialog.push(msg.payload);\nlet theDefault = msg.field_default;\nlet required = '';\nif (msg.field_required) {\n    required = ', required:true';\n}\nmsg.defaults.push(msg.field_name + ': {value:\"' + theDefault + '\"' + required + '}');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[[]]},{"id":"a9570576c6d79609","type":"subflow","name":"option","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"4f14c5db823380c0"}]}],"out":[{"x":540,"y":220,"wires":[{"id":"f3c884a018b98b9c","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75"},{"l":{"en-US":"2/3 row"},"v":"col col-66"},{"l":{"en-US":"1/2 row"},"v":"col col-50"},{"l":{"en-US":"1/3 row"},"v":"col col-33"},{"l":{"en-US":"1/4 row"},"v":"col col-25"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"radio_group","type":"str","value":"","ui":{"label":{"en-US":"Group"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"unchecked","ui":{"label":{"en-US":"Default"},"type":"select","opts":{"opts":[{"l":{"en-US":"checked"},"v":"checked"},{"l":{"en-US":"unchecked"},"v":"unchecked"}]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-dot-circle-o"},{"id":"4f14c5db823380c0","type":"function","z":"a9570576c6d79609","name":"function 10","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nmsg.radio_group = env.get('radio_group');\nif (env.get('field_default') == 'checked') {\n    msg.checked = 'checked';\n    msg.field_default = true;\n} else {\n    msg.checked = '';\n    msg.field_default = false;\n}\nmsg.field_columns = env.get('field_columns');\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["4dd9f408962124c5"]]},{"id":"4dd9f408962124c5","type":"template","z":"a9570576c6d79609","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}} reg-lbl\">\n    <label for=\"{{radio_group}}-{{field_name}}\" style=\"width:18px;\">\n    <input type=\"radio\" id=\"{{radio_group}}-{{field_name}}\" value=\"{{field_name}}\" name=\"{{radio_group}}\" style=\"margin-top:-3px;\" {{checked}}>{{{field_label}}}</label>\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["77cc34048bcc7da4"]]},{"id":"77cc34048bcc7da4","type":"function","z":"a9570576c6d79609","name":"function 11","func":"let radio_group = '<input type=\"hidden\" id=\"node-input-' + msg.radio_group + '\">' + \"\\n\";\nlet pre = '';\nif (msg.edit_dialog.join(\"\\n\").indexOf(radio_group) == -1) {\n    pre = radio_group;\n}\nmsg.edit_dialog.push(pre + msg.payload);\nif (msg.defaults.indexOf(msg.radio_group + ': {value:\"') == -1) {\n    if (msg.field_default) {\n        msg.defaults.push(msg.radio_group + ': {value:\"' + msg.field_name + '\"}');\n    }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["8b96d7bb4892c0d6"]]},{"id":"8b96d7bb4892c0d6","type":"template","z":"a9570576c6d79609","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare radio group {{radio_group}}\n            let v = $('#node-input-{{radio_group}}').val();\n            $('input[name=\"{{radio_group}}\"]').attr('checked', false);\n            $('#{{radio_group}}-' + v).attr('checked', true);\n","output":"str","x":420,"y":40,"wires":[["742418ba92cd639a"]]},{"id":"742418ba92cd639a","type":"function","z":"a9570576c6d79609","name":"function 12","func":"if (msg._oneditprepare.indexOf(msg.payload) == -1) {\n    msg._oneditprepare.push(msg.payload);\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":100,"wires":[["9960c6b71ddcbcdc"]]},{"id":"9960c6b71ddcbcdc","type":"template","z":"a9570576c6d79609","name":"oneditsave","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Save radio group {{radio_group}}\n            let v = $('input[name=\"{{radio_group}}\"]:checked').val();\n            $('#node-input-{{radio_group}}').val(v);\n","output":"str","x":410,"y":160,"wires":[["f3c884a018b98b9c"]]},{"id":"f3c884a018b98b9c","type":"function","z":"a9570576c6d79609","name":"function 13","func":"if (msg._oneditsave.indexOf(msg.payload) == -1) {\n    msg._oneditsave.push(msg.payload);\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":220,"wires":[[]]},{"id":"da6577bfc4038dbb","type":"subflow","name":"checkbox","info":"","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"b08129ad78aa131f"}]}],"out":[{"x":320,"y":160,"wires":[{"id":"d46ba26faedba5a9","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75"},{"l":{"en-US":"2/3 row"},"v":"col col-66"},{"l":{"en-US":"1/2 row"},"v":"col col-50"},{"l":{"en-US":"1/3 row"},"v":"col col-33"},{"l":{"en-US":"1/4 row"},"v":"col col-25"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"checked","ui":{"label":{"en-US":"Default"},"type":"select","opts":{"opts":[{"l":{"en-US":"checked"},"v":"checked"},{"l":{"en-US":"unchecked"},"v":"unchecked"}]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-check-square"},{"id":"b08129ad78aa131f","type":"function","z":"da6577bfc4038dbb","name":"function 8","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nif (env.get('field_default') == 'checked') {\n    msg.checked = 'checked';\n    msg.field_default = true;\n}else{\n    msg.checked = '';\n    msg.field_default = false;\n}\nmsg.field_columns = env.get('field_columns');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":40,"wires":[["005ee05cba1991fe"]]},{"id":"005ee05cba1991fe","type":"template","z":"da6577bfc4038dbb","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}} reg-lbl\">\n    <label for=\"node-input-{{field_name}}\" style=\"width:18px;\">\n    <input type=\"checkbox\" id=\"node-input-{{field_name}}\" name=\"node-input-{{field_name}}\" style=\"margin-top:-3px;\" {{checked}}>{{{field_label}}}</label>\n</div><!--col-->","output":"str","x":180,"y":100,"wires":[["d46ba26faedba5a9"]]},{"id":"d46ba26faedba5a9","type":"function","z":"da6577bfc4038dbb","name":"function 9","func":"msg.edit_dialog.push(msg.payload);\nif (msg.field_default == 'checked') {\n    msg.field_default = true;\n}else{\n    msg.field_default = false;\n}\nmsg.defaults.push(msg.field_name + ': {value:' + msg.field_default.toString() + '}');\nif (msg.hasOwnProperty('rgroup_name')) {\n    msg.rgroup_details.push({\n        field_name: msg.field_name,\n        field_default_type: '',\n        field_types: ''\n    });\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":160,"wires":[[]]},{"id":"b30b4cf6837f20a7","type":"subflow","name":"selectbox","info":"Node Maker Docs\nThe selectbox node represents the use of Node-RED’s TypedInput and Typed Input Multiple Selectbox. For an example, see the bottom of the page for [Node-RED Inputs field for examples](https://nodered.org/docs/creating-nodes/edit-dialog#inputs). The following properties define the select box’s appearance:\n\n* Layout - The number of columns the label will consume out of a division of four or three equal areas. To align controls on the panel on the same row, ensure the used column space totals to consume an entire row.\n* Field Name - A JavaScript compatible variable name (no spaces hyphens or punctuation).\n* Label - The actual text that will be used when displaying the label.\n* Icon - A font awesome icon that will appear to the left of the label. You can specify the icon name sans any ‘fa’ prefix; i.e. bathtub\n* Values - The values of the selectbox are represented as a comma delimited string of captions:value (a caption separated by a colon and value). The captions will appear in the selectbox as a dropdown combobox. \n* Default - The default value or default selection (comma delimited if multiple is enabled) for the selectbox.\n* Allow Multiple - true to allow more than one value to be selected or false for only a single value. \n","category":"node maker","in":[{"x":60,"y":40,"wires":[{"id":"a898132ca0292c19"}]}],"out":[{"x":540,"y":100,"wires":[{"id":"3d97bfe487ea399d","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75 sml-lbl"},{"l":{"en-US":"2/3 row"},"v":"col col-66 sml-lbl"},{"l":{"en-US":"1/2 row"},"v":"col col-50 sml-lbl"},{"l":{"en-US":"1/3 row"},"v":"col col-33 sml-lbl"},{"l":{"en-US":"1/4 row"},"v":"col col-25 sml-lbl"}]}}},{"name":"field_name","type":"str","value":"fruit","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"Fruits Label","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"box","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"field_values","type":"str","value":"Apple:a,Banana:b,Cherry:c","ui":{"label":{"en-US":"Values"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"b","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str"]}}},{"name":"selectbox_multiple","type":"bool","value":"false","ui":{"label":{"en-US":"Allow Multiple"},"type":"input","opts":{"types":["bool"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-list"},{"id":"a898132ca0292c19","type":"function","z":"b30b4cf6837f20a7","name":"function 23","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n} else {\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n} else {\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nmsg.field_default = env.get('field_default');\nmsg.selectbox_multiple = env.get('selectbox_multiple');\nif (msg.selectbox_multiple) {\n    msg.selectbox_multiple = 'multiple: \"true\",' + \"\\n\";\n}else{\n    msg.selectbox_multiple = '';\n}\nmsg.field_values = env.get('field_values');\nlet fv = msg.field_values.split(',');\nlet o = '';\nfv.forEach(function(v, i) {\n    v = v.split(':');\n    o += '{ \"value\":\"' + v[1] + '\", \"label\": \"' + v[0].trim().replace('\"', '\\\"') + '\"}';\n    if (i < fv.length-1) {\n        o += \",\\n\";\n    }\n});\no = o.replace(/^/gm, \" \".repeat(28));\nmsg.field_values = o;\nmsg.field_columns = env.get('field_columns');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":40,"wires":[["d0c143eac43904cd"]]},{"id":"d0c143eac43904cd","type":"template","z":"b30b4cf6837f20a7","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    {{{field_label_elm}}}\n    <input type=\"text\" id=\"node-input-{{field_name}}\">\n</div><!--col-->","output":"str","x":160,"y":100,"wires":[["1c0a4ee1081ab0ae"]]},{"id":"1c0a4ee1081ab0ae","type":"function","z":"b30b4cf6837f20a7","name":"function 24","func":"msg.edit_dialog.push(msg.payload);\nmsg.defaults.push(msg.field_name + ': {value:\"' + msg.field_default.replaceAll('\"', '\\\"') + '\"}');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["436c6476640d73e2"]]},{"id":"436c6476640d73e2","type":"template","z":"b30b4cf6837f20a7","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare selectbox {{field_name}}\n            $(\"#node-input-{{field_name}}\").typedInput({\n                types: [\n                    {\n                        value: \"{{field_name}}\",\n                        {{{selectbox_multiple}}}\n                        options: [\n{{{field_values}}}\n                        ]\n                    }\n                ]\n            });\n","output":"str","x":420,"y":40,"wires":[["3d97bfe487ea399d"]]},{"id":"3d97bfe487ea399d","type":"function","z":"b30b4cf6837f20a7","name":"function 25","func":"if (!msg.hasOwnProperty('rgroup_name')) {\n    msg._oneditprepare.push(msg.payload);\n} else {\n    msg.rgroup_details.push({\n        field_name: msg.field_name,\n        field_types: [{\n                value: msg.field_name,\n                multiple: (msg.selectbox_multiple != ''),\n                options: JSON.parse('[' + msg.field_values + ']')\n        }],\n        field_default_type: null,\n        field_default: msg.field_default\n    });\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":100,"wires":[[]]},{"id":"d0a39641213352a7","type":"subflow","name":"label","info":"The label node is used to place a standalone label in the property panel. Use this node to mark specific sections or to provide users with additional information. \n\nOther nodes may also include a built-in label and this node may not be necessary for nodes that have their own. The following properties for defining a label may apply to other nodes as well:\n\n* Layout - The number of columns the label will consume out of a division of four or three equal areas. To align controls on the panel on the same row, ensure the used column space totals to consume an entire row. \n* Label - The actual text that will be used when displaying the label.\n* Icon - A font awesome icon that will appear to the left of the label. You can specify the icon name sans any ‘fa’ prefix; i.e. bathtub \n\n","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"b15245fe8570aaf7"}]}],"out":[{"x":320,"y":160,"wires":[{"id":"84054fdfc2c499b0","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75"},{"l":{"en-US":"2/3 row"},"v":"col col-66"},{"l":{"en-US":"1/2 row"},"v":"col col-50"},{"l":{"en-US":"1/3 row"},"v":"col col-33"},{"l":{"en-US":"1/4 row"},"v":"col col-25"}]}}},{"name":"label_caption","type":"str","value":"&nbsp;","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"label_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-font"},{"id":"fe135416b9ef2efa","type":"template","z":"d0a39641213352a7","name":"edit_dialog","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div class=\"{{field_columns}} reg-lbl\">\n    <label class=\"full-lbl\">\n        {{{label_icon}}}<span>{{{label_caption}}}</span>\n    </label>\n</div><!--col-->","output":"str","x":190,"y":100,"wires":[["84054fdfc2c499b0"]]},{"id":"b15245fe8570aaf7","type":"function","z":"d0a39641213352a7","name":"function 6","func":"msg.label_caption = env.get('label_caption');\nlet icon = env.get('label_icon');\nif (icon != '') {\n    msg.label_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n}else{\n    msg.label_icon = '';\n} \nmsg.field_columns = env.get('field_columns');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":40,"wires":[["fe135416b9ef2efa"]]},{"id":"84054fdfc2c499b0","type":"function","z":"d0a39641213352a7","name":"function 7","func":"msg.edit_dialog.push(msg.payload);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":160,"wires":[[]]},{"id":"7062e6f741533eb2","type":"subflow","name":"field ","info":"The field node represents an instance of the Node-RED [Inputs field](https://nodered.org/docs/creating-nodes/edit-dialog#inputs). A plain input text box can be achieved with the no “Typed” checkbox or type definition specified. The following properties for defining a field are as follows:\n\n* Layout - The number of columns the label will consume out of a division of four or three equal areas. To align controls on the panel on the same row, ensure the used column space totals to consume an entire row.\n* Field Name - A JavaScript compatible variable name (no spaces hyphens or punctuation).\n* Label - The actual text that will be used when displaying the label.\n* Icon - A font awesome icon that will appear to the left of the label. You can specify the icon name sans any ‘fa’ prefix; i.e. bathtub\n* Default - the optional default contents of the field.\n* Placeholder - displays a grey value in the input field as a suggested or example value.\n* Required - whether the input field is required and must be filled out. True will invoke a basic validation check to ensure the value is supplied. A missing value will cause the node’s appearance to show an “needs configuration” triangle and subsequent message when the user attempts to deploy the flow. Additionally, a red border will appear around any required fields not filled out.\n* Typed - This section allows the field input to have one or more types to be prefixed to the field area and influences the fields display behavior. See the [Node-RED Inputs field for examples](https://nodered.org/docs/creating-nodes/edit-dialog#inputs).\n* Type Definitions - Allows for defining a custom type using JSON. When the above Typed checkbox “Include additional type definition array” is checked, this property becomes relevant. For example, you can create a custom input option such as “the complete msg object” as used in the [debug node](https://nodered.org/docs/user-guide/nodes#debug).\n* Default Type - This string value determines the default type to be selected for the input field when your node’s property panel is first opened. \n","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"73a3b0f6af6b0aeb"}]}],"out":[{"x":560,"y":260,"wires":[{"id":"67eed6e284e28123","port":0},{"id":"eda19645927d3dda","port":0}]}],"env":[{"name":"field_columns","type":"str","value":"col-100","ui":{"label":{"en-US":"Layout"},"type":"select","opts":{"opts":[{"l":{"en-US":"full-row"},"v":"col-100"},{"l":{"en-US":"3/4 row"},"v":"col col-75 sml-lbl"},{"l":{"en-US":"2/3 row"},"v":"col col-66 sml-lbl"},{"l":{"en-US":"1/2 row"},"v":"col col-50 sml-lbl"},{"l":{"en-US":"1/3 row"},"v":"col col-33 sml-lbl"},{"l":{"en-US":"1/4 row"},"v":"col col-25 sml-lbl"}]}}},{"name":"field_name","type":"str","value":"","ui":{"label":{"en-US":"Field Name"},"type":"input","opts":{"types":["str"]}}},{"name":"field_label","type":"str","value":"","ui":{"label":{"en-US":"Label"},"type":"input","opts":{"types":["str"]}}},{"name":"field_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"field_default","type":"str","value":"","ui":{"label":{"en-US":"Default"},"type":"input","opts":{"types":["str","num","bool","json","bin"]}}},{"name":"field_placeholder","type":"str","value":"","ui":{"label":{"en-US":"Placeholder"},"type":"input","opts":{"types":["str"]}}},{"name":"field_required","type":"bool","value":"false","ui":{"label":{"en-US":"Required"}}},{"name":"typed","type":"str","value":"","ui":{"label":{"en-US":"Typed:"},"type":"none"}},{"name":"field_type_string","type":"bool","value":"false","ui":{"label":{"en-US":"str"},"type":"checkbox"}},{"name":"field_type_number","type":"bool","value":"false","ui":{"label":{"en-US":"num"},"type":"checkbox"}},{"name":"field_type_boolean","type":"bool","value":"false","ui":{"label":{"en-US":"bool"},"type":"checkbox"}},{"name":"field_type_msg","type":"bool","value":"false","ui":{"label":{"en-US":"msg"},"type":"checkbox"}},{"name":"field_type_flow","type":"bool","value":"false","ui":{"label":{"en-US":"flow"},"type":"checkbox"}},{"name":"field_type_global","type":"bool","value":"false","ui":{"label":{"en-US":"global"},"type":"checkbox"}},{"name":"field_type_json","type":"bool","value":"false","ui":{"label":{"en-US":"json"},"type":"checkbox"}},{"name":"field_type_additional","type":"bool","value":"false","ui":{"label":{"en-US":"Include additional type definition array (below)"},"type":"checkbox"}},{"name":"field_additional_types","type":"json","value":"[{\"value\":\"full\",\"label\":\"complete msg object\",\"hasValue\":false}]","ui":{"label":{"en-US":"Type Definitions"},"type":"input","opts":{"types":["json"]}}},{"name":"field_default_type","type":"str","value":"","ui":{"label":{"en-US":"Default Type"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","icon":"font-awesome/fa-i-cursor"},{"id":"2f6d8f31aa468d9f","type":"template","z":"7062e6f741533eb2","name":"edit_dialog","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<div class=\"{{field_columns}}\">\n    {{{field_label_elm}}}\n    <input type=\"text\" id=\"node-input-{{field_name}}\" placeholder=\"{{{field_placeholder}}}\">\n    {{{field_has_hidden_type}}}\n</div><!--col-->","output":"str","x":150,"y":100,"wires":[["a0b8b4c7a26b07dd"]]},{"id":"73a3b0f6af6b0aeb","type":"function","z":"7062e6f741533eb2","name":"function 2","func":"msg.field_name = env.get('field_name');\nmsg.field_label = env.get('field_label');\nlet icon = env.get('field_icon');\nif (icon != '') {\n    msg.field_icon = '<i class=\"fa fa-' + icon + '\"></i> ';\n}else{\n    msg.field_icon = '';\n}\nif (msg.field_label == '') {\n    msg.field_label_elm = '';\n}else{\n    msg.field_label_elm = '<label for=\"node-input-' + msg.field_name + '\">';\n    msg.field_label_elm += msg.field_icon + msg.field_label + '</label>';\n}\nmsg.field_placeholder = env.get('field_placeholder');\nmsg.field_required = env.get('field_required');\nmsg.field_default = env.get('field_default');\n\nmsg.field_type_string = env.get('field_type_string');\nmsg.field_type_number = env.get('field_type_number');\nmsg.field_type_boolean = env.get('field_type_boolean');\nmsg.field_type_msg = env.get('field_type_msg');\nmsg.field_type_flow = env.get('field_type_flow');\nmsg.field_type_global = env.get('field_type_global');\nmsg.field_type_json = env.get('field_type_json');\nmsg.field_type_additional = env.get('field_type_additional');\nmsg.field_default_type = env.get('field_default_type');\nmsg.field_types = [];\nif (msg.field_type_string) {\n    msg.field_types.push('str');\n}\nif (msg.field_type_number) {\n    msg.field_types.push('num');\n}\nif (msg.field_type_boolean) {\n    msg.field_types.push('bool');\n}\nif (msg.field_type_msg) {\n    msg.field_types.push('msg');\n}\nif (msg.field_type_flow) {\n    msg.field_types.push('flow');\n}\nif (msg.field_type_global) {\n    msg.field_types.push('global');\n}\nif (msg.field_type_json) {\n    msg.field_types.push('json');\n}\nif (msg.field_type_additional) {\n    msg.field_additional_types = env.get('field_additional_types');\n    msg.field_types = msg.field_types.concat(msg.field_additional_types);\n}\nmsg.field_has_hidden_type = '';\nif (msg.field_types.length > 0) {\n    if (msg.field_default_type == '') {\n        msg.field_default_type = msg.field_types[0];\n    }\n    msg.field_has_hidden_type = '<input type=\"hidden\" id=\"node-input-' + env.get('field_name') + 'Type\"';\n    msg.field_has_hidden_type += 'value=\"' + msg.field_default_type + '\">';\n    msg.field_types_s = JSON.stringify(msg.field_types);\n}\nlet fc = env.get('field_columns');\nif (msg.field_label == '') {\n    fc += ' no-label';\n}\nmsg.field_columns = fc;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":140,"y":40,"wires":[["2f6d8f31aa468d9f"]]},{"id":"a0b8b4c7a26b07dd","type":"function","z":"7062e6f741533eb2","name":"function 3","func":"msg.edit_dialog.push(msg.payload);\nlet theDefault = msg.field_default;\nif (msg.field_default_type != 'bool' && msg.field_default_type != 'num') {\n    theDefault = '\"' + theDefault + '\"';\n}\nlet required = '';\nif (msg.field_required) {\n    required = ', required:true';\n}\nmsg.defaults.push(msg.field_name + ': {value:' + theDefault + required + '}');\nif (msg.field_types.length > 0) {\n    msg.defaults.push(msg.field_name + 'Type: {value:\"' + msg.field_default_type + '\"}');\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":140,"y":160,"wires":[["250dcfedab51f0e5"]]},{"id":"3c2eac4c542cd18e","type":"template","z":"7062e6f741533eb2","name":"oneditprepare","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Prepare field {{field_name}}\n            $(\"#node-input-{{field_name}}\").typedInput({\n                type: \"{{{field_default_type}}}\",\n                types: {{{field_types_s}}},\n                typeField: \"#node-input-{{field_name}}Type\"\n            });\n","output":"str","x":460,"y":40,"wires":[["67eed6e284e28123"]]},{"id":"250dcfedab51f0e5","type":"switch","z":"7062e6f741533eb2","name":"field_has_hidden_type","property":"field_has_hidden_type","propertyType":"msg","rules":[{"t":"nempty"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":180,"y":220,"wires":[["11ee4d2cf1435263"],["eda19645927d3dda"]]},{"id":"67eed6e284e28123","type":"function","z":"7062e6f741533eb2","name":"function 4","func":"if (!msg.hasOwnProperty('rgroup_name')) {\n    msg._oneditprepare.push(msg.payload);\n}else{\n    msg.rgroup_details.push({\n        field_name: msg.field_name,\n        field_default_type: msg.field_default_type,\n        field_types: msg.field_types,\n        field_default: msg.field_default\n    });\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":100,"wires":[[]]},{"id":"eda19645927d3dda","type":"function","z":"7062e6f741533eb2","name":"function 38","func":"if (msg.hasOwnProperty('rgroup_name')) {\n    msg.rgroup_details.push({\n        field_name: msg.field_name,\n        field_default_type: '',\n        field_types: '',\n        field_default: msg.field_default\n    });\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":260,"wires":[[]]},{"id":"11ee4d2cf1435263","type":"junction","z":"7062e6f741533eb2","x":340,"y":40,"wires":[["3c2eac4c542cd18e"]]},{"id":"65d5e45697fed99a","type":"subflow","name":"finish node","info":"A finish node marks the end of a node definition. The finish node will finalize writing all\nthe files needed to create your node; the package.json, html, javascript, and ecompassing\nproject folder within your node-red instance's node_module folder. It will also restart the\nnode-red instance if it was started using the command:\n\n``\nkill -9 `cat /tmp/node-red.pid`; nohup nice node-red > /dev/null 2>&1 & echo $! > /tmp/node-red.pid\n``\n\nThis makes ongoing development easy as you simply need to refresh the browser to see node changes\nafter you have invoked the flow.","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"37f124c38d4954c7"}]}],"out":[],"env":[],"meta":{},"color":"#DDAA99"},{"id":"0eb2dcf71e3bf43f","type":"template","z":"65d5e45697fed99a","name":"nodemakerhtml","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<style>\n    {{{customcss}}}\n</style>\n<script type=\"text/html\" data-template-name=\"{{node_name}}\">\n    {{{inc_node_name}}}\n    <div id=\"node-props\" style=\"width: {{row_width}}px;\">\n        {{{edit_dialog}}}\n    </div><!--node-props-->\n</script>\n\n<script type=\"text/html\" data-help-name=\"{{node_name}}\">\n    {{{helpdetails}}}\n</script>\n\n<script type=\"text/javascript\">\n    RED.nodes.registerType('{{node_name}}', {\n        category: '{{node_category}}',\n        color: '{{node_color}}',\n        defaults: {\n            name: {value:\"\"},\n            {{{defaults}}}\n        },\n        inputs: {{numinputs}},\n        outputs: {{numoutputs}},\n        icon: \"font-awesome/fa-{{{node_icon}}}\",\n        label: function() {\n            return this.name || \"{{node_name}}\";\n        },\n        labelStyle: function() {\n            return this.name ? \"node_label_italic\" : \"\";\n        },\n        oneditprepare: function() {\n            {{{oneditprepare}}}\n        },\n        oneditresize: function() {\n            {{{oneditresize}}}        \n        },\n        oneditsave: function() {\n            {{{oneditsave}}}\n        },\n        oneditcancel: function() {\n            {{{oneditcancel}}}\n        }\n    });\n    {{{othercode}}}\n</script>\n","output":"str","x":360,"y":200,"wires":[["1d0b5202023b245b"]]},{"id":"46fdba84593e4a18","type":"file","z":"65d5e45697fed99a","name":"write nodename/nodename.html","filename":"nodename_html","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":670,"y":220,"wires":[["f26a2d19e71211be"]]},{"id":"2a9f513d9174ccde","type":"exec","z":"65d5e45697fed99a","command":"kill -9 `cat /tmp/node-red.pid`; nohup nice node-red > /dev/null 2>&1 & echo $! > /tmp/node-red.pid","addpay":"","append":"","useSpawn":"false","timer":"","winHide":false,"oldrc":false,"name":"restart node-red","x":200,"y":480,"wires":[["e674d758d3c14af8"],["e674d758d3c14af8"],["e674d758d3c14af8"]]},{"id":"e674d758d3c14af8","type":"debug","z":"65d5e45697fed99a","name":"debug 50","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":440,"y":480,"wires":[]},{"id":"43b6b50718414f9f","type":"template","z":"65d5e45697fed99a","name":"nodemakerjs","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"module.exports = function(RED) {\n    function {{node_name}}(config) {\n        RED.nodes.createNode(this,config);\n        var node = this;\n        {{{runtimecode}}}\n    }\n    RED.nodes.registerType('{{node_name}}', {{node_name}});\n}","output":"str","x":350,"y":320,"wires":[["8f4279aa76193d2d"]]},{"id":"8f4279aa76193d2d","type":"file","z":"65d5e45697fed99a","name":"write nodename/nodename.js","filename":"nodename_js","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":670,"y":340,"wires":[["2a9f513d9174ccde"]]},{"id":"fa07b8c430e2643d","type":"switch","z":"65d5e45697fed99a","name":"","property":"nodemakerhtml","propertyType":"msg","rules":[{"t":"empty"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":170,"y":220,"wires":[["0eb2dcf71e3bf43f"],["987b04d149c1e6b2"]]},{"id":"987b04d149c1e6b2","type":"change","z":"65d5e45697fed99a","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"nodemakerhtml","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":240,"wires":[["1d0b5202023b245b"]]},{"id":"f26a2d19e71211be","type":"switch","z":"65d5e45697fed99a","name":"","property":"nodemakerjs","propertyType":"msg","rules":[{"t":"empty"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":170,"y":340,"wires":[["43b6b50718414f9f"],["0c85d78ee708d905"]]},{"id":"0c85d78ee708d905","type":"change","z":"65d5e45697fed99a","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"nodemakerjs","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":360,"wires":[["8f4279aa76193d2d"]]},{"id":"3d44470abb3be3af","type":"function","z":"65d5e45697fed99a","name":"compose defaults, othercode","func":"if (msg.hasOwnProperty('useTemplates')) return msg;\nmsg._oneditprepare.push(msg.payload);\n\nString.prototype.delRightMost = function (sFind) {\n    for (var i = this.length; i >= 0; i = i - 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(0, f);\n            break;\n        }\n    }\n    return this;\n};\nString.prototype.getRightMost = function (sFind) {\n    for (var i = this.length; i >= 0; i = i - 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(f + sFind.length, f + sFind.length + this.length);\n        }\n    }\n    return this;\n};\nString.prototype.delLeftMost = function (sFind) {\n    for (var i = 0; i < this.length; i = i + 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(f + sFind.length, f + sFind.length + this.length);\n            break;\n        }\n    }\n    return this;\n};\nString.prototype.getLeftMost = function (sFind) {\n    for (var i = 0; i < this.length; i = i + 1) {\n        var f = this.indexOf(sFind, i);\n        if (f != -1) {\n            return this.substring(0, f);\n            break;\n        }\n    }\n    return this;\n};\nvar indent = \" \".repeat(4);\nvar tabIndent = \"\";\nvar edit_dialog = \" \".repeat(8) + '<div class=\"form-row\">' + \"\\n\";\nvar cTotal = 0;\nvar nItems = 0;\nvar initTab = false;\nvar firstTabName = '';\nvar bInRGroup = false;    \nmsg.edit_dialog.forEach(function(item, index) {\n    if (item.indexOf('\"type\":\"tab_definition\"') == -1 && item.indexOf('form-row rgroup') == -1) {\n        item = item.replace(/^/gm, tabIndent);\n        item = item.replace(/^/gm, tabIndent);\n        if (bInRGroup) {\n            item = item.replace(/^/gm, \" \".repeat(4));\n        }\n    }\n    if (item.indexOf('<div class=\"form-row rgroup\"') > -1) {\n        edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">').trimEnd() + \"\\n\";\n        item += \" \".repeat(4) + '<div class=\"form-row\">' + \"\\n\";\n        item = item.replace(/^/gm, \" \".repeat(8) + tabIndent).trimEnd() + \"\\n\";\n        bInRGroup = true;\n        edit_dialog += item;\n        return;\n    }\n    if (item.indexOf('<!--form-row rgroup-->') > -1) {\n        edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">').trimEnd() + \"\\n\";\n        edit_dialog += \" \".repeat(8) + tabIndent + \"</div><!--form-row rgroup-...-template-->\\n\";\n        if (index < msg.edit_dialog.length - 1) {\n            edit_dialog += indent + indent + tabIndent + '<div class=\"form-row\">' + \"\\n\";\n        }\n        bInRGroup = false;\n        return;\n    }\n    if (item.indexOf('\"type\":\"tab_definition\"') > -1) {\n        let tab = JSON.parse(item);\n        if (initTab == false) {\n            \n            // Inject the tab row\n            let r = edit_dialog.getRightMost('<div class=\"form-row\">');\n            edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">');\n            edit_dialog += '<div class=\"form-row func-tabs-row\">' + r;\n            edit_dialog += indent + indent + indent + '<ul style=\"min-width: 600px; margin-bottom: 20px;\" id=\"func-tabs\"></ul>' + \"\\n\";\n            edit_dialog += indent + indent + \"</div><!--func-tabs-row-->\\n\";\n            edit_dialog += indent + indent + '<div id=\"func-tabs-content\" style=\"min-height: calc(100% - 95px);\">' + \"\\n\";\n\n            // Start first tab\n            edit_dialog += indent + indent + indent + '<div id=\"func-tab-' + tab.name + '\" style=\"display:none\">' + \"\\n\";\n            edit_dialog += indent + indent + indent + indent + '<div class=\"form-row\">' + \"\\n\";\n            tabIndent = \" \".repeat(8);\n\n            // Init tab system\n            let code = \"// Prepare tab system\\n\";\n            code += \"var tabs = RED.tabs.create({\\n\";\n            code += \"    id: 'func-tabs',\\n\";\n            code += \"    onchange: function(tab) {\\n\";\n            code += \"        $('#func-tabs-content').children().hide();\\n\";\n            code += \"        $('#' + tab.id).show();\\n\";\n            code += \"    }\\n\";\n            code += \"});\\n\\n\";\n\n            // Add first tab code\n            code += \"// Add first tab, \" + tab.name + \"\\n\";\n            code += \"tabs.addTab({\\n\";\n            code += \"    id: 'func-tab-\" + tab.name + \"',\\n\";\n            code += \"    iconClass: 'fa fa-\" + tab.icon + \"',\\n\";\n            code += \"    label: '\" + tab.label + \"'\\n\";\n            code += \"});\\n\\n\";\n            firstTabName = tab.name;\n            code = code.replace(/^/gm, \" \".repeat(12));\n            msg._oneditprepare.push(code);\n            initTab = true;\n        }else{\n\n            // Add subsequent tabs \n            let r = edit_dialog.getRightMost('    <div class=\"form-row\"');\n            edit_dialog = edit_dialog.delRightMost('    <div class=\"form-row\"');\n            edit_dialog += \"</div><!--func-tab-tab#-->\\n\"; // close out last tab\n            edit_dialog += \" \".repeat(12) + '<div id=\"func-tab-' + tab.name + '\" style=\"display:none\">' + \"\\n\";\n            edit_dialog += \" \".repeat(16) + '<div class=\"form-row\"' + r;\n\n            // Add additional tab code\n            let code = \"// Add tab, \" + tab.name + \"\\n\";            \n            code += \"tabs.addTab({\\n\";\n            code += \"    id: 'func-tab-\" + tab.name + \"',\\n\";\n            code += \"    iconClass: 'fa fa-\" + tab.icon + \"',\\n\";\n            code += \"    label: '\" + tab.label + \"'\\n\";\n            code += \"});\\n\\n\";\n            code = code.replace(/^/gm, \" \".repeat(12));\n            msg._oneditprepare.push(code);\n        }        \n    }else{\n        if (tabIndent == '') {\n            item = item.replace(/^/gm, indent + indent + indent);\n        }else{\n            item = item.replace(/^/gm, indent);\n        }\n        let c = item.delLeftMost('col-').getLeftMost('\"').getLeftMost(' ');\n        cTotal += Number(c);\n        if (cTotal >= 83) {\n            let m = 5 * nItems;\n            if (m > 0) {\n                item = item.replace(\n                    'class=\"col ',\n                    'style=\"margin-right:-' + m.toString() + 'px;\" class=\"col '\n                );\n            }\n            if (nItems == 3) {\n                let r = edit_dialog.getRightMost('<div class=\"form-row\">');\n                edit_dialog = edit_dialog.delRightMost('<div class=\"form-row\">');\n                edit_dialog += '<div class=\"form-row\" style=\"margin-right:10px;\">' + r;\n            }\n            edit_dialog += item + \"\\n\";\n            if (index < msg.edit_dialog.length - 1) {\n                if (bInRGroup) {\n                    edit_dialog += \" \".repeat(4);\n                }\n                edit_dialog += indent + indent + tabIndent + \"</div><!--form-row-->\\n\";\n                edit_dialog += indent + indent + tabIndent + '<div class=\"form-row\">' + \"\\n\";\n                cTotal = 0;\n                nItems = 0;\n            }\n        } else {\n            edit_dialog += item + \"\\n\";\n            nItems++;\n        }\n    }\n});\nif (initTab) { \n    edit_dialog += \" \".repeat(16) + \"</div><!--form-row-->\\n\";\n    edit_dialog += \" \".repeat(12) + \"</div><!--func-tab-tab#-->\\n\";\n    edit_dialog += \" \".repeat(8) + \"</div><!--func-tabs-content-->\";\n\n    // Activate the first tab if present\n    let code = \" \".repeat(12) + 'tabs.activateTab(\"func-tab-' + firstTabName + '\");' + \"\\n\";\n    msg._oneditprepare.push(code);\n}else{\n    edit_dialog += \" \".repeat(8) + \"</div><!--form-row-->\";\n}\nmsg.edit_dialog = edit_dialog;\n\nlet defaults = '';\nmsg.defaults.forEach(function(item, index) {\n    defaults += \"            \" + item;\n    if (index < msg.defaults.length -1) {\n        defaults += \",\\n\";\n    }else{\n        defaults += \"\\n\";\n    }\n});\nmsg.defaults = defaults;\nmsg.oneditprepare = \"\\n\" + msg._oneditprepare.join(\"\\n\");\nmsg.oneditresize = \"\\n\" + msg._oneditresize.join(\"\\n\");\nmsg.oneditsave = \"\\n\" + msg._oneditsave.join(\"\\n\");\nmsg.oneditcancel = \"\\n\" + msg._oneditcancel.join(\"\\n\");\nmsg.defaults = msg.defaults.slice(0, -1);\nvar othercode = \"\";\nfor (var key in msg._othercode) {\n    var value = msg._othercode[key];\n    othercode += value;\n}\nmsg.othercode = \"\\n\" + othercode;\nmsg.runtimecode = \"\\n\" + msg.runtimecode.replace(/^/gm, \"    \".repeat(4));\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":100,"wires":[["fa07b8c430e2643d"]]},{"id":"37f124c38d4954c7","type":"template","z":"65d5e45697fed99a","name":"re-size fix","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"            // Allow dynamic re-size after init. appearance \n            setTimeout(function () {\n                $('#node-props').css('width', '100%');\n            }, 30);","output":"str","x":200,"y":40,"wires":[["3d44470abb3be3af"]]},{"id":"1d0b5202023b245b","type":"junction","z":"65d5e45697fed99a","x":500,"y":220,"wires":[["46fdba84593e4a18"]]},{"id":"85e13a6e48f6a1c3","type":"subflow","name":"start node","info":"The start node marks the beginning of a new node defintion. To create a node,\nconnect an inject node to the start node and connect the start node to a \nfinish node. You may wire various other nodes between the start and finish\nnodes to define your node's user interface.\n\nThe following properties can be configured in the properties panel:\n\n* Node Name - choose a JavaScript variable safe name for your node.\n* Version - A valid version string in the form of major, minor, patch i.e. 1.0.0\n* Description - a short description of your node.\n* Category - The palette group the node will appear under; existing i.e. common or you can define your own.\n* Icon - An existing fontawesome name, sans the 'font-awesome/fa-' prefix; i.e. bathtub\n* Color - The color of the node as it appears in the palette and editor. I.e. #E2D96E\n* Keywords - Used in the package.json file for reference and indexing by npm.\n* Author Name - The name of the node author. Used in package.json\n* Author Email - The email of the node author. Used in package.json\n* GitHub Handle - The handle used for the GitHub repo URL, I.e. john will appears as https://github.com/john/node-name\n* Max Row Width - The initial width of the property dialog (in pixels) when it is first opened.\n* Inputs - Whether the node accepts an input or not; 1 or 0 our the only acceptable values.\n* Outputs - The number of outputs the node will have.\n* Include node name... - Determines if a default name field will appear at the top of the property window; appears above any tab or other input fields.\n","category":"node maker","in":[{"x":40,"y":40,"wires":[{"id":"729874432b3551f1"}]}],"out":[{"x":740,"y":200,"wires":[{"id":"1d506b65823c19c3","port":0}]}],"env":[{"name":"node_name","type":"str","value":"","ui":{"label":{"en-US":"Node Name"},"type":"input","opts":{"types":["str"]}}},{"name":"node_version","type":"str","value":"0.0.1","ui":{"label":{"en-US":"Version"},"type":"input","opts":{"types":["str"]}}},{"name":"node_description","type":"str","value":"","ui":{"label":{"en-US":"Description"},"type":"input","opts":{"types":["str"]}}},{"name":"node_category","type":"str","value":"","ui":{"label":{"en-US":"Category"},"type":"input","opts":{"types":["str"]}}},{"name":"node_icon","type":"str","value":"","ui":{"label":{"en-US":"Icon"},"type":"input","opts":{"types":["str"]}}},{"name":"node_color","type":"str","value":"","ui":{"label":{"en-US":"Color"},"type":"input","opts":{"types":["str"]}}},{"name":"keywords","type":"str","value":"","ui":{"label":{"en-US":"Keywords"},"type":"input","opts":{"types":["str"]}}},{"name":"author_name","type":"str","value":"","ui":{"label":{"en-US":"Author Name"},"type":"input","opts":{"types":["str"]}}},{"name":"author_email","type":"str","value":"","ui":{"label":{"en-US":"Author Email"}}},{"name":"github_handle","type":"str","value":"","ui":{"label":{"en-US":"GitHub Handle"},"type":"input","opts":{"types":["str"]}}},{"name":"row_width","type":"num","value":"460","ui":{"label":{"en-US":"Max Row Width"},"type":"input","opts":{"types":["num"]}}},{"name":"numinputs","type":"num","value":"1","ui":{"label":{"en-US":"Inputs"},"type":"spinner","opts":{"min":0,"max":1}}},{"name":"numoutputs","type":"num","value":"1","ui":{"label":{"en-US":"Outputs"},"type":"spinner"}},{"name":"inc_node_name","type":"bool","value":"true","ui":{"label":{"en-US":"Include node name field at the top"},"type":"checkbox"}}],"meta":{},"color":"#DDAA99"},{"id":"729874432b3551f1","type":"function","z":"85e13a6e48f6a1c3","name":"function 1","func":"msg.node_name = env.get('node_name');\nmsg.node_version = env.get('node_version');\nmsg.node_description = env.get('node_description');\nmsg.node_folder = env.get('HOME') + '/.node-red/node_modules/node-red-' + msg.node_name + '/';\nlet k = env.get('keywords');\nk = '\"' + k.replaceAll(' ', '').replaceAll(',', '\",\"') + '\"';\nmsg.node_category = env.get('node_category');\nmsg.node_icon = env.get('node_icon');\nmsg.node_color = env.get('node_color');\nmsg.keywords = k;\nmsg.author_name = env.get('author_name');\nmsg.author_email = env.get('author_email');\nmsg.github_handle = env.get('github_handle');\nmsg.package_json = msg.node_folder + 'package.json';\nmsg.gitignore = msg.node_folder + '.gitignore';\nmsg.license = msg.node_folder + 'LICENSE';\nmsg.readme = msg.node_folder + 'README.md';\nmsg.nodename_js = msg.node_folder + msg.node_name + '/' + msg.node_name + '.js';\nmsg.nodename_html = msg.node_folder + msg.node_name + '/' + msg.node_name + '.html';\nmsg.edit_dialog = [];\nmsg._oneditprepare = [];\nmsg._oneditresize = [];\nmsg._oneditsave = [];\nmsg._oneditcancel = [];\nmsg._othercode = {};\nmsg.defaults = [];\nmsg.row_width = env.get('row_width');\nmsg.numinputs = env.get('numinputs');\nmsg.numoutputs = env.get('numoutputs');\nmsg.inc_node_name = env.get('inc_node_name');\nmsg.nodemakerhtml = '';\nmsg.nodemakerjs = '';\nmsg.runtimecode = '';\nif (msg.inc_node_name) {\n    msg.inc_node_name = '    <div class=\"form-row\">' +\"\\n\";\n    msg.inc_node_name += '        <label for=\"node-input-name\"><i class=\"icon-tag\"></i> Name</label>' + \"\\n\";\n    msg.inc_node_name += '        <input type=\"text\" id=\"node-input-name\" placeholder=\"Name\">' + \"\\n\";\n    msg.inc_node_name += \"    </div>\";\n}else{\n    msg.inc_node_name = '';\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":160,"y":40,"wires":[["1a9cbb92df9f9927","4dc54964fd7d8d7b","a79e8a6b35ceac10","4eaf4f130e10c617","1d506b65823c19c3"]]},{"id":"1a9cbb92df9f9927","type":"template","z":"85e13a6e48f6a1c3","name":"package.json","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"name\": \"node-red-{{node_name}}\",\n    \"version\": \"{{node_version}}\",\n    \"description\": \"A node that merges msg objects until all defined properties are present / conditions met before passing the combined msg along.\",\n    \"author\": \"{{author_name}} <{{author_email}}>\",\n    \"license\": \"MIT\",\n    \"keywords\": [{{{keywords}}}],\n    \"repository\": {\n      \"type\": \"git\",\n      \"url\": \"https://github.com/{{github_handle}}/node-red-{{node_name}}.git\"\n    },\n    \"dependencies\": {\n    },\n    \"node-red\": {\n      \"nodes\": {\n        \"{{node_name}}\": \"{{node_name}}/{{node_name}}.js\"\n      }\n    }\n  }\n  ","output":"str","x":380,"y":40,"wires":[["e164bb1b88912998"]]},{"id":"e164bb1b88912998","type":"file","z":"85e13a6e48f6a1c3","name":"write package.json","filename":"package_json","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":570,"y":40,"wires":[[]]},{"id":"4dc54964fd7d8d7b","type":"template","z":"85e13a6e48f6a1c3","name":".gitignore","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":".DS_Store\nnode_modules\n","output":"str","x":360,"y":80,"wires":[["c2f33b913e572b75"]]},{"id":"c2f33b913e572b75","type":"file","z":"85e13a6e48f6a1c3","name":"write .gitignore","filename":"gitignore","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":560,"y":80,"wires":[[]]},{"id":"a79e8a6b35ceac10","type":"template","z":"85e13a6e48f6a1c3","name":"MIT License","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"MIT License\n\nCopyright (c) 2022 {{author_name}}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","output":"str","x":370,"y":120,"wires":[["6846859d76b0ec5e"]]},{"id":"6846859d76b0ec5e","type":"file","z":"85e13a6e48f6a1c3","name":"write LICENSE","filename":"license","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":560,"y":120,"wires":[[]]},{"id":"4eaf4f130e10c617","type":"template","z":"85e13a6e48f6a1c3","name":"README.md","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"# node-red-{{node_name}}\n{{{node_description}}}\n","output":"str","x":370,"y":160,"wires":[["7631ff7c6efd5ad8"]]},{"id":"7631ff7c6efd5ad8","type":"file","z":"85e13a6e48f6a1c3","name":"write README.md","filename":"readme","filenameType":"msg","appendNewline":false,"createDir":true,"overwriteFile":"true","encoding":"utf8","x":570,"y":160,"wires":[[]]},{"id":"1d506b65823c19c3","type":"template","z":"85e13a6e48f6a1c3","name":"customcss","field":"customcss","fieldType":"msg","format":"css","syntax":"mustache","template":".red-ui-tray-content #dialog-form {\n    white-space:nowrap;\n}\n.full-row .red-ui-typedInput-container {\n    min-width: 70%;\n}\n.col input {\n    min-width: 100%;\n}\n.sml-lbl {\n    height: 66px;\n}\n.sml-lbl label {\n    font-size: smaller;\n    margin-bottom: 0px;\n    display: block!important;\n}\n.reg-lbl label {\n    white-space: nowrap;\n    margin-top: 5px;\n    height: 0px;\n}\n.red-ui-editor .form-row label.full-lbl {\n    white-space: normal;\n    width: 100%;\n}\n.col {\n    float: left;\n    margin-right: 5px;\n    min-height: 36px;\n}\n.col .red-ui-typedInput-container {\n    width: 100%!important;\n}\n.col-50 {\n    width: 50%;\n}\n.col-33 {\n    width: 33%;\n}\n.col-66 {\n    width: 66%;\n}\n.col-25 {\n    width: 25%;\n}\n.col-75 {\n    width: 75%;\n}\n.col-100 .red-ui-typedInput-container {\n    width: 70%!important;\n}\n.col-100.no-label .red-ui-typedInput-container {\n    width: 100%!important;\n}\n.txtarea {\n    padding-bottom: 26px;\n}\n.txtarea label {\n    vertical-align: top;\n    margin-top: 3px;\n}\n.txtarea  textarea {\n    width: 70%;\n    margin-bottom: -28px!important;\n}\n.btn-regular {\n    margin-bottom: 14px!important;\n}\n.red-ui-editableList-item-content {\n    display: inline-block;\n    margin-bottom: -6px;\n    width: -moz-available;\n    width: -webkit-fill-available;\n    width: fill-available;\n}\n.red-ui-editableList-item-content .sml-lbl {\n    height: auto;\n}\n","output":"str","x":370,"y":200,"wires":[[]]},{"id":"677fb756cbb4360f","type":"subflow:85e13a6e48f6a1c3","z":"0a6ae50c8bea82a5","name":"","x":90,"y":80,"wires":[[]]},{"id":"e725dacd17f2954a","type":"subflow:65d5e45697fed99a","z":"0a6ae50c8bea82a5","name":"","x":90,"y":120,"wires":[]},{"id":"036b5f87f876e931","type":"subflow:7062e6f741533eb2","z":"0a6ae50c8bea82a5","name":"","x":70,"y":260,"wires":[[]]},{"id":"a6b1bd7694b9c9ac","type":"subflow:d0a39641213352a7","z":"0a6ae50c8bea82a5","name":"","x":70,"y":220,"wires":[[]]},{"id":"a1b54857a0c26621","type":"subflow:b30b4cf6837f20a7","z":"0a6ae50c8bea82a5","name":"","x":80,"y":300,"wires":[[]]},{"id":"e388909407d1e446","type":"subflow:da6577bfc4038dbb","z":"0a6ae50c8bea82a5","name":"","x":80,"y":340,"wires":[[]]},{"id":"9f164c2dd989dd6f","type":"subflow:a9570576c6d79609","z":"0a6ae50c8bea82a5","name":"","x":290,"y":220,"wires":[[]]},{"id":"f2ed2c1dfc657cca","type":"subflow:5904ee94d99998b2","z":"0a6ae50c8bea82a5","name":"","x":300,"y":260,"wires":[[]]},{"id":"7d191762d10b25c3","type":"subflow:60de16a0fefb7ace","z":"0a6ae50c8bea82a5","name":"","x":290,"y":300,"wires":[[]]},{"id":"99b985c6d10e4c57","type":"subflow:c70964ba34544694","z":"0a6ae50c8bea82a5","name":"","env":[{"name":"button_size","value":null,"type":"str"}],"x":290,"y":340,"wires":[[]]},{"id":"3abd9b2b0fd9f9ed","type":"subflow:3aaeb350dea9a1f1","z":"0a6ae50c8bea82a5","name":"","x":310,"y":380,"wires":[[]]},{"id":"4a16563387a4c014","type":"subflow:7d1e49e9a2f7337a","z":"0a6ae50c8bea82a5","name":"","x":610,"y":80,"wires":[[]]},{"id":"71cd7e9d4614bc4b","type":"subflow:973db1310fdebc19","z":"0a6ae50c8bea82a5","name":"","x":90,"y":520,"wires":[[]]},{"id":"2919c812e437c739","type":"subflow:162e81fc107019c0","z":"0a6ae50c8bea82a5","name":"","x":90,"y":560,"wires":[[]]},{"id":"5115041e2d80cee4","type":"subflow:8d72584ecbd0d019","z":"0a6ae50c8bea82a5","name":"","x":310,"y":420,"wires":[[]]},{"id":"feeb53a74fc7c359","type":"comment","z":"0a6ae50c8bea82a5","name":"Use 'start node' and 'finish node' to define a new node.","info":"","x":220,"y":40,"wires":[]},{"id":"3dd6eba0d907050e","type":"comment","z":"0a6ae50c8bea82a5","name":"Design your interface wiring UI nodes between the start & finish nodes.","info":"","x":270,"y":180,"wires":[]},{"id":"0530435dac1f862a","type":"comment","z":"0a6ae50c8bea82a5","name":"Organize your UI into tabs with the tab node.","info":"","x":730,"y":40,"wires":[]},{"id":"b70c68b8740b96fc","type":"comment","z":"0a6ae50c8bea82a5","name":"Create repeating groups; (only label, field, selectbox, checkbox. & spinner)","info":"","x":280,"y":480,"wires":[]},{"id":"8f8fa30ef736679c","type":"comment","z":"0a6ae50c8bea82a5","name":"Use templates to code your node's behaviors.","info":"","x":730,"y":180,"wires":[]},{"id":"300f0735525d0102","type":"inject","z":"0a6ae50c8bea82a5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":640,"y":360,"wires":[["d28fcd585276f2b8"]]},{"id":"d28fcd585276f2b8","type":"subflow:85e13a6e48f6a1c3","z":"0a6ae50c8bea82a5","name":"","env":[{"name":"node_name","value":"example","type":"str"},{"name":"node_description","value":"An example node","type":"str"},{"name":"node_category","value":"common","type":"str"},{"name":"node_icon","value":"globe","type":"str"},{"name":"node_color","value":"#E2D96E","type":"str"},{"name":"keywords","value":"example","type":"str"},{"name":"author_name","value":"Armand Goldman","type":"str"},{"name":"author_email","value":"[email protected]","type":"str"},{"name":"github_handle","value":"armandgoldman","type":"str"}],"x":850,"y":360,"wires":[["ace7471c4689165d"]]},{"id":"40046e3c0a327112","type":"subflow:65d5e45697fed99a","z":"0a6ae50c8bea82a5","name":"","x":990,"y":420,"wires":[]},{"id":"ace7471c4689165d","type":"subflow:7062e6f741533eb2","z":"0a6ae50c8bea82a5","name":"","env":[{"name":"field_name","value":"test","type":"str"},{"name":"field_label","value":"Test","type":"str"},{"name":"field_icon","value":"globe","type":"str"},{"name":"field_type_string","type":"bool","value":"true"},{"name":"field_default_type","value":"str","type":"str"}],"x":1010,"y":360,"wires":[["f7c03cbde7e9de62"]]},{"id":"c1fd7ca5c1059b98","type":"comment","z":"0a6ae50c8bea82a5","name":"Invoke to create an example node definition","info":"","x":730,"y":320,"wires":[]},{"id":"4fc797c031edc224","type":"example","z":"0a6ae50c8bea82a5","name":"","test":"","testType":"str","x":780,"y":520,"wires":[["13ae6a3488364edd"]]},{"id":"723986549b69e89b","type":"inject","z":"0a6ae50c8bea82a5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":640,"y":520,"wires":[["4fc797c031edc224"]]},{"id":"13ae6a3488364edd","type":"debug","z":"0a6ae50c8bea82a5","name":"debug 51","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":940,"y":520,"wires":[]},{"id":"11b1fb0b3f0c08b9","type":"template","z":"0a6ae50c8bea82a5","name":"nodemakerhtml","field":"nodemakerhtml","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<style>\n    {{{customcss}}}\n</style>\n<script type=\"text/html\" data-template-name=\"{{node_name}}\">\n    {{{inc_node_name}}}\n    <div id=\"node-props\" style=\"width: {{row_width}}px;\">\n        {{{edit_dialog}}}\n    </div><!--node-props-->\n</script>\n\n<script type=\"text/html\" data-help-name=\"{{node_name}}\">\n    {{{helpdetails}}}\n</script>\n\n<script type=\"text/javascript\">\n    RED.nodes.registerType('{{node_name}}', {\n        category: '{{node_category}}',\n        color: '{{node_color}}',\n        defaults: {\n            name: {value:\"\"},\n            {{{defaults}}}\n        },\n        inputs: {{numinputs}},\n        outputs: {{numoutputs}},\n        icon: \"font-awesome/fa-{{{node_icon}}}\",\n        label: function() {\n            return this.name || \"{{node_name}}\";\n        },\n        labelStyle: function() {\n            return this.name ? \"node_label_italic\" : \"\";\n        },\n        oneditprepare: function() {\n            {{{oneditprepare}}}\n        },\n        oneditresize: function() {\n            {{{oneditresize}}}        \n        },\n        oneditsave: function() {\n            {{{oneditsave}}}\n        },\n        oneditcancel: function() {\n            {{{oneditcancel}}}\n        }\n    });\n    {{{othercode}}}\n</script>\n","output":"str","x":820,"y":220,"wires":[[]]},{"id":"75ad4d90b1a97b9c","type":"template","z":"0a6ae50c8bea82a5","name":"nodemakerjs","field":"nodemakerjs","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"module.exports = function(RED) {\n    function {{node_name}}(config) {\n        RED.nodes.createNode(this,config);\n        var node = this;\n        {{{runtimecode}}}\n    }\n    RED.nodes.registerType('{{node_name}}', {{node_name}});\n}","output":"str","x":810,"y":260,"wires":[[]]},{"id":"f90dfc43615618b4","type":"template","z":"0a6ae50c8bea82a5","name":"runtimecode","field":"runtimecode","fieldType":"msg","format":"javascript","syntax":"mustache","template":"node.on('input', function(msg) {\n    msg.payload = \"{{node_name}}\";\n    node.send(msg);\n});","output":"str","x":810,"y":420,"wires":[["40046e3c0a327112"]]},{"id":"eb56b1f2bff50aa7","type":"template","z":"0a6ae50c8bea82a5","name":"runtimecode","field":"runtimecode","fieldType":"msg","format":"javascript","syntax":"mustache","template":"node.on('input', function(msg) {\n    msg.payload = \"{{node_name}}\";\n    node.send(msg);\n});","output":"str","x":630,"y":260,"wires":[[]]},{"id":"b73a92a09ff11387","type":"comment","z":"0a6ae50c8bea82a5","name":"Test created example node. Refresh browser after above flow.","info":"","x":780,"y":480,"wires":[]},{"id":"a3b93925ea64a640","type":"subflow:7996feeba4e5f327","z":"0a6ae50c8bea82a5","name":"","x":80,"y":380,"wires":[[]]},{"id":"05ee1cee1d2cdd54","type":"subflow:9771d13dd19bf60f","z":"0a6ae50c8bea82a5","name":"","x":630,"y":220,"wires":[[]]},{"id":"f7c03cbde7e9de62","type":"subflow:9771d13dd19bf60f","z":"0a6ae50c8bea82a5","name":"","x":630,"y":420,"wires":[["f90dfc43615618b4"]]}]

Collection Info

Flow Info

Created 2 years ago
Rating: 5 3

Owner

Actions

Rate:

Node Types

Core
  • change (x2)
  • comment (x7)
  • debug (x2)
  • exec (x1)
  • file (x6)
  • function (x42)
  • inject (x2)
  • switch (x3)
  • template (x37)
Other
  • example (x1)
  • junction (x3)
  • subflow (x17)
  • subflow:162e81fc107019c0 (x1)
  • subflow:3aaeb350dea9a1f1 (x1)
  • subflow:5904ee94d99998b2 (x1)
  • subflow:60de16a0fefb7ace (x1)
  • subflow:65d5e45697fed99a (x2)
  • subflow:7062e6f741533eb2 (x2)
  • subflow:7996feeba4e5f327 (x1)
  • subflow:7d1e49e9a2f7337a (x1)
  • subflow:85e13a6e48f6a1c3 (x2)
  • subflow:8d72584ecbd0d019 (x1)
  • subflow:973db1310fdebc19 (x1)
  • subflow:9771d13dd19bf60f (x2)
  • subflow:a9570576c6d79609 (x1)
  • subflow:b30b4cf6837f20a7 (x1)
  • subflow:c70964ba34544694 (x1)
  • subflow:d0a39641213352a7 (x1)
  • subflow:da6577bfc4038dbb (x1)

Tags

  • node
  • development
  • coding
  • program
  • automate
  • ui
  • interface
  • api
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option