Can I access number of outputs within function node?


#1

My function node has 6 outputs. Is there a way to access this number from within the function node?

i.e. so I don't have to explicitly specify the number in my first line:

var outputcount = 6;

// Clear all backgrounds on any type of change
var outputs = [];
for (i=0; i<outputcount; i++) {
    outputs[i] = {background : ""};
}

// If the change was that a scene was set, light up that scene
if (msg.topic == "home/topliving/light/scene") {
    // we read from global memory, instead of from incoming msg
    // so scenes are remembered even when redeploying
    var currentScene = global.get("home.topliving.light.scene");
    for (i=0; i<outputcount; i++) {
        if (i == currentScene) {
            outputs[i] = { background: "#00A676" };
        }
    }
}
return outputs;

#2

No - there is no API for that.


#3

Thanks for confirming


#4

As the number of outputs is stored in the flow_xx.json, you could read it from the file:

function get_outputs(node_name, callback) {
    var fs = global.get('fs');
    const flow_file = '/home/pi/.node-red/flows_rpi4.json';
    
    fs.readFile(flow_file, function (err, data) {
        if (err) {
            node.warn(err);
        } else {
            var nodes = JSON.parse(data);
            Object.keys(nodes).forEach(function(key) {
                if (nodes[key].name === node_name) {
                    callback(nodes[key].outputs);
                }
            });
        }
    });
}

get_outputs('fun_name', function(outputs) {
    msg.payload = outputs;
    node.send(msg);
});

Replace fun_name by the name of you function and flow_rpi4.json by your flow name.
Note: fs has to be defined in the settings.js

[{"id":"2d3bbb37.d24584","type":"function","z":"ac4aa9f6.c24288","name":"fun_name","func":"function get_outputs(node_name, callback) {\n    var fs = global.get('fs');\n    const flow_file = '/home/pi/.node-red/flows_rpi4.json';\n    \n    fs.readFile(flow_file, function (err, data) {\n        if (err) {\n            node.warn(err);\n        } else {\n            var nodes = JSON.parse(data);\n            Object.keys(nodes).forEach(function(key) {\n                if (nodes[key].name === node_name) {\n                    callback(nodes[key].outputs);\n                }\n            });\n        }\n    });\n}\n\nget_outputs('fun_name', function(outputs) {\n    msg.payload = outputs;\n    node.send(msg);\n});\n","outputs":2,"noerr":0,"x":360,"y":1600,"wires":[["a2137d2d.e68aa"],[]]},{"id":"a2137d2d.e68aa","type":"debug","z":"ac4aa9f6.c24288","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":1600,"wires":[]},{"id":"a037c85e.9e5c28","type":"inject","z":"ac4aa9f6.c24288","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":1600,"wires":[["2d3bbb37.d24584"]]}]

#5

Hi @cflurin - that’s pretty impressive! I note the function node API gives access to the flow name (node.name), so it could be further improved. Is there a way to access the name of the flow this function is within? If so - that would make for a generic code block that could be included in any function without modification!


#6

Right, you can use node.name or node.id (preferred).

function get_outputs(callback) {
    const fs = global.get('fs');
    const flow_file = '/home/pi/.node-red/flows_rpi4.json';
    
    fs.readFile(flow_file, function (err, data) {
        if (err) {
            node.warn(err);
        } else {
            var nodes = JSON.parse(data);
            Object.keys(nodes).forEach(function(key) {
                // if (nodes[key].name === node.name) {
                if (nodes[key].id === node.id) {
                    callback(nodes[key].outputs);
                }
            });
        }
    });
}

get_outputs(function(outputs) {
    msg.payload = outputs;
    node.send(msg);
});

No, I don't think it's possible - but if you use the node.id, the node identification is unique.


#7

Hey @cflurin,

I haven't tested it yet in a function node, but perhaps you can use something like this:

RED.nodes.eachNode(function(anotherNode) {
    if (anotherNode].id === node.id) {
         // Do something (e.g. with node.name) ...
    }
});

Then you don't have to parse the file, since Node-RED has already loaded everything in memory ...
Bart


#8

No, the RED.nodes.eachNode api is not available in the Function node.

This is a lot of work to avoid keeping a number in the Function and the Outputs value in sync... how often do you change it?!


#9

Agree! I probably won't do it for that reason, would be great if the API might support it in the future though...


#10

As @knolleary said the RED api doesn't work in a function. However it's available in core and contrib-nodes.
It also works in the dsm-node. As an example see the Flow statistics.


#11

Pretty useless cause one has to wire the outputs anyway by hand...

To do something like that just output an array -> split -> switch. This also follows more the msg based philosophy of node-red.


#12

I don't understand

The reason I'd find it useful is that I want to have a generic function that I can reuse about 20 times. One for each lighting zone. The number of circuits in a given lighting zone may change from time to time. So it would make life easier.

No problem really changing the number by hand, just wondered if there was a better way. Either way I've learned a lot just from this thread so thanks to all!


#13

Changing outputs on multiple (20?) functions and rewire them doesnt mean generic for me
and the whole approach looks pretty (over-)complicated (even if you didnt tell what exactly you want to achieve).


#14

I really don't understand what point you are trying to make

Was just asking if there was a built-in way to determine number of outputs; as if so, I'd use it when writing function code


#15

Agreed -- this and all other properties of the function node's definition should be available to the code inside it...


#16

err - no... there are many properties of a node that would not be appropriate to expose to the inside of a function.


#17

Well, internal properties I agree should not be necessarily exposed -- I guess I was only thinking of the properties entered into the edit panel by the flow author...

... which I see now is only the Name and # of outputs. So my statement still stands ;*)

Actually, the labels on the output ports would be helpful for writing modular function code.


#18

Splitting output to different branches can be done with default set of nodes in many different ways. Multiple outputs of the function node is only one of them. And function stays reusable if you create the logic by following the strategy path the Node-RED offers.
By asking supportive system expose itself - in JavaScript land it's basically "ask it to be open for modifications" and this will be opened gate to self-destruction. Do you really want this kind of system to rely to build your stuff on top? Probably not.


#19

Sorry if I’m being dim here, but I don’t understand any of the reasons people are giving that it’s a bad idea to be able to determine the number of outputs from within a function node. To my untrained ear, it literally sounds like people are being superstitious about it. I cannot see how Node RED would fall apart if it allowed users to access something like the number of outputs. After all, it allows you to access other things about the function node like its name. Self destruction? Appropriate? Sorry I don’t understand this!


#20

Hi @hazymat, I can see no reason for not making node.outputs available. I will make it so.