How to drop msg.payload from specific topics?

I have a function node that is receiving input from several nodes, each of which transmits on its own topic. The function checks if any of the payloads are true and then outputs true. Unfortunately it's also passing through all of those input messages.
How can I block those? Is there a way to send them to null or something?

if (msg.payload == true) { //any topic = true
node.status({fill:"green",shape:"dot",text:"present"});
msg.payload = true; //turn on systems
return msg;
} else {
node.status({fill:"black",shape:"dot"});
}

That's obviously acting as an OR operator. I'm also working on a function node which is an AND operator.

if (
    (msg.payload == false)&&(msg.topic="phone 1")
    &&
    (msg.payload == false)&&(msg.topic="phone 2")
    &&
    (msg.payload == false)&&(msg.topic="phone 3")
    &&
    (msg.payload == false)&&(msg.topic="phone 4")
    &&
    (msg.payload == false)&&(msg.topic="phone 5")
   )
    {
        node.status({fill:"green",shape:"dot",text:"all out"});
        msg.payload = false; //turn off systems
        return msg;
    } else {
        node.status({fill:"black",shape:"dot"});
}

If you don't want to passthrough the msg entirely, simply don't return msg
But you can set the payload to anything you want including undefined

msg.payload = undefined

A few things you need to be aware off however:

No Message is EVER sent at the same time, so your function node will ONLY ever see one payload and topic at any given time (not all of them, at the same time)

If you want the function node to process serval messages in one execution, you need to combine them into an array.

With that in mind....

The below, won't work - as your function node will ONLY ever see one version of the payload, and topic per execution (unless as stated you send them as 1 message in the form of an array of messages)

 (msg.payload == false)&&(msg.topic="phone 1")
    &&
    (msg.payload == false)&&(msg.topic="phone 2")
    &&
    (msg.payload == false)&&(msg.topic="phone 3")
    &&
    (msg.payload == false)&&(msg.topic="phone 4")
    &&
    (msg.payload == false)&&(msg.topic="phone 5")
   )

lastly msg is an object, so if you set a property (topic, payload etc etc) send a copy, then do something else with it, in the same node after, the downstream Nodes will get affected.

This is pass-by-ref

I hope this helps

The goal is to create one node which sends an on signal to the heating system and another node that sends off. They need to have different timers (for example, on needs to be immediate, but off needs to wait 30 minutes), hence why I want two separate function nodes. (This will also allow me to expand the logic to fit some other scenarios I'm thinking about.)

@marcus-j-davies do you know how to get a function node to drop all payloads that aren't the desired one?

Uhh...

Not sure why my message come before yours :man_shrugging:
What on earth, my original message has gone?

Oh I edited my message :face_with_open_eyes_and_hand_over_mouth:

see my edit above

(Yeah, that's strange about the messages being out of order!)

If I don't return a message, how ever will I get an output from the node??

This is an ideal situation for a join node to combine {topic: "one", payload: 10} and {topic: "two", payload: 7} into a single payload:

{
"one":10,
"two":7
}

Then they are all available in the same message for your AND / OR comparisons.
And since there is only one combined message, no problem with multiple outputs from the function.

1 Like

To echo the above, and to address my concern.
I think an incorrect expectation is happening (apologies if not :slightly_smiling_face:)

In that these message with different topics are arriving at the same time - They are Not.

Messages will never arrive at the same time.
so you need to combine them in a join node first, and to send them all as 1 big message

Then, you check each Phones value at the same time

/* msg.payload conjured by the join node */
{ 
    'Phone 1' : true,
    'Phone 2' : false,
    'Phone 3' : true,
    'Phone 4' : true
}

const AnyOn = Object.values(msg.payload).filter((v) => v === true).length > 0
const AnyOff = Object.values(msg.payload).filter((v) => v === false).length > 0
const AllOff = Object.values(msg.payload).filter((v) => v === true).length === 0
const AllOn = Object.values(msg.payload).filter((v) => v === false).length === 0

const OnPhones = Object.keys(msg.payload).filter((key) => msg.payload[key] === true);
const OffPhones = Object.keys(msg.payload).filter((key) => msg.payload[key] === false);

/ * do something with above */

if(AllOn){
  return {payload: true}
}

if(AnyOn){
  return {payload: true, topic: OnPhones}
}

You've made an assumption that the data arrives asynchronously. :slight_smile: It's actually from a Unifi node which is queried whenever a door is opened, so the data does come all at once. (It's actually split into multiple 'streams' to handle each user and this combined stream is what is being sent into my AND and OR function nodes.)

However, I want to thank you very much for the script you wrote, I'm going to use it as education. :slight_smile:

1 Like

In that case don't split it, keep it together.

Looking at this snippet of code:

if (
    (msg.payload == false)&&(msg.topic="phone 1")
    &&
    (msg.payload == false)&&(msg.topic="phone 2")
    &&
    (msg.payload == false)&&(msg.topic="phone 3")
    &&
    (msg.payload == false)&&(msg.topic="phone 4")
    &&
    (msg.payload == false)&&(msg.topic="phone 5")
   )

Perhaps this is perfectly normal syntax and I just misunderstand it. I'm not very comfortable with javascript.

You seem to be assigning 5 different values to msg.topic, one after another. Why?
Or did you mean to test eg (msg.topic === "phone 5")

If the data does come "all at once" in the sense of "in a single message", how can it ever be true that msg.topic === "phone 1" and msg.topic === "phone 2" and msg.topic === "phone 3" ... ?

If however the data comes "all at once" in the sense of "multiple messages in the same picosecond", then each message will be processed in isolation and without any knowledge of the preceding message contents.

You seem to be assigning 5 different values to msg.topic, one after another. Why?
My thought was that it would check for 'false' on the topic 'phone 1' and 'false' on the topic 'phone 2', etc.
Though I am good with logic, I'm absolutely not a programmer.

OK. This is not the way Node-red (and other flow based programming languages that I know of) works.

It's not easy to give a simple and clear explanation but you need a slightly different way of thinking about data with Node-red compared to a procedural language.

Hence the suggestion that you use a join node.

But perhaps there is a completely different way to do this.

I already have a function node which checks the MAC addresses which are reported by the Unifi node:

Argh! While writing this, my (remote) raspberry pi just went down and I can't access my flows. I'll have to paste the code Monday or hopefully tomorrow.

Thanks everyone for your help so far.

The RPi rebooted, I'm back in.
This is the function node that is fed from the Unifi node and has multiple outputs:

const lastSeenSeconds = 20;

let presenceCutoff = (Date.now() - (lastSeenSeconds * 1000)) / 1000; 
const people = {
    "presence/Phone1"  : "xx:xx:xx:xx:xx:xx",
    "presence/Phone2"  : "xx:xx:xx:xx:xx:xx",
    "presence/Phone3"  : "xx:xx:xx:xx:xx:xx",
    "presence/Phone4"  : "xx:xx:xx:xx:xx:xx",
    "presence/Phone5"  : "xx:xx:xx:xx:xx:xx"
};

return Object.keys(people).map(function(topic) {
    let devices = msg.payload[0].filter(device => device.mac === people[topic]);
//    let devices = msg.payload[0].filter(device => device.mac === people[topic]) && (device.last_seen > presenceCutoff);
    return {
        topic: topic,
        //retain: true,
        payload: devices.length > 0
    };
});

I was hoping to turn it into something like this, but I'm pretty lost to be honest:

OK - Lets do this methodically.

Can you post the EXACT

payload you're receiving (please format it) - redact/replace where necessary.
What payload output you want as a result of this input payload.

I think this will help in trying to solution your quest

I still think its important to understand, that each execution of the function node (that is each message being sent to it), can ONLY see that one message (an no other, in that same execution context)

1 Like

Thanks Marcus, I could do that, but a single 'packet' from the Unifi node is 13.5kb, so it'll take me a while to redact.
Is it not obvious to see the pertinent details in here?: How to drop msg.payload from specific topics? - #16 by daxliniere
I am currently getting the required information from that flow, though it isn't formatted in a way that's useful to this particular situation.

The exact output should be two channels; 1) Any person present, 2) No people present.

Not to me it isn't.
I'd need someone to explain line by line what that code is doing.
In fact "Explain this code" is a very powerful tool in tech support. When you have to explain it you are more likely to understand it.

Sample data, 20 - 30 "records" with the mac addresses obfuscated so the different phones are still distinguishable but not identifiable would help too.
I feel that I would learn new techniques from this along with (maybe) helping you.

Thanks @jbudd. Since it's over 600 lines long, I've just included a single entry for a network user:

[
    [
        {
            "site_id": "xxxxxxxxxxxxxxxxxxx",
            "assoc_time": 1719157675,
            "latest_assoc_time": 1719157965,
            "hostname": "Galaxy-S10",
            "oui": "",
            "user_id": "xxxxxxxxxxxxxxx",
            "_id": "xxxxxxxxxxxxxxx",
            "mac": "AA:AA:AA:AA:AA:AA", //Dax note: this is the ID you need
            "is_guest": true,
            "first_seen": 1715947769,
            "last_seen": 1719158113, //Dax note: this is not used in the current iteration
            "is_wired": false,
            "usergroup_id": "",
            "wlanconf_id": "xxxxxxxxxxxxxxx",
            "disconnect_timestamp": 1719157673,
            "local_dns_record_enabled": false,
            "local_dns_record": "",
            "name": "XXXXX's phone",
            "use_fixedip": false,
            "network_id": "xxxxxxxxxxxxxxx",
            "fixed_ip": "xxxxxxxxxxxxxxx",
            "fixed_ap_enabled": false,
            "noted": true,
            "_uptime_by_uap": 149,
            "_last_seen_by_uap": 1719158113,
            "_is_guest_by_uap": true,
            "ap_mac": "BB:BB:BB:BB:BB:BB",
            "channel": 6,
            "radio": "ng",
            "radio_name": "wifi0",
            "essid": "SSID",
            "bssid": "CC:CC:CC:CC:CC:CC",
            "powersave_enabled": false,
            "is_11r": false,
            "user_group_id_computed": "xxxxxxxxxxxxxxx",
            "anomalies": 0,
            "anon_client_id": "xxxxxxxxxxxxxxx",
            "ccq": 989,
            "dhcpend_time": 0,
            "idletime": 0,
            "ip": "xxxxxxxxxxxxxxx",
            "noise": -100,
            "nss": 2,
            "rx_rate": 86531,
            "rssi": 26,
            "satisfaction": 99,
            "satisfaction_now": 99,
            "satisfaction_real": 99,
            "satisfaction_reason": 2048,
            "signal": -70,
            "tx_mcs": 5,
            "tx_power": 34,
            "tx_rate": 104000,
            "vlan": 0,
            "radio_proto": "ng",
            "channel_width": 20,
            "satisfaction_avg": {
                "total": 2781,
                "count": 28
            },
            "uptime": 438,
            "tx_bytes": 6107149,
            "rx_bytes": 3497757,
            "tx_packets": 9879,
            "rx_packets": 9007,
            "bytes-r": 126356,
            "tx_bytes-r": 73229,
            "rx_bytes-r": 53126,
            "tx_retries": 4308,
            "wifi_tx_attempts": 13481,
            "authorized": true,
            "qos_policy_applied": true,
            "roam_count": 3
        } //Dax note: there is normally a trailing comma (,) to separate each device, with the last one having no comma.
    ]
]