Change msg.topic dynamically

Hi,
I want to change a msg.topic dynamically in my flow "Pth":


to
a) Pth_H
b) Pth_WW
c) Pth_PC
(Heating, Hotwater, passive Cooling)
and the msg.payload to = 0 if off

I get these values:


grafik

I´ve made a "flow.modus" but I don´t know how to link the topic of Pth with the topic of "M".

Which would also be nice, to set Pth = 0, if the compressor is a 0Hz:
grafik

this could be quite a complicated function beyond my horizons. :grimacing:

I'm not sure I understood you correctly.

You want to change the topic dinamically, and you have already a function. The thing you need is to know the conditions that trigger the change, and then just rewrite the topic, using a conditional IF

if <condition1> then {
   msg.topic  = "topic1";
} elseif  <condition2> {
   msg.topic = "topic2";
} else {
   msg.topic = "topic_default";
}

As for the value of Pth, you just need to check the property that gives you the frequency (might be msg.payload.freq or whatever you use), check that, and if it's 0, just set the Pth value to 0.

I don't know if that is what you're asking for, but if it is, seems pretty straightforward to me.

thanks,
forget about the frequency, no good idea from me.

in the meantime I´ve made a new function:

var P = msg.payload["Pth"];
var M = msg.payload[43086];
if(M == 10) {
    M = "Pth_aus";
    P = 0;
}
if(M == 20) {
   M = "Pth_WW";
}
if(M == 30) {
    M = "Pth_H";
}
if(M == 60) {
    M = "Pth_PC";
}
msg.payload = P;
msg.topic = M;
return msg;

it looks like it is the solution

Just to optimize things a bit, and since I assume you can't have different values of M on the same message, I'd use elseif instead:

var P = msg.payload["Pth"];
var M = msg.payload[43086];
if (M == 10) {
    M = "Pth_aus";
    P = 0;
} else if (M == 20) {
   M = "Pth_WW";
} else if (M == 30) {
    M = "Pth_H";
} else if (M == 60) {
    M = "Pth_PC";
} else {
    M = "ERROR";
}
msg.payload = P;
msg.topic = M;
return msg;

This will stop comparing once you find a match. It also allows you to handle errors, in case the value for M is not what you expect.

1 Like

thanks,
how can I output the msg as an object ?
I don´t want to use the join node.

What do you have to join?

Either you join the node and process the messages (which is probebly the best if the readings are synced), or you use context (flow or global) to store the variables whenever they come, and read them when you need to.

It´s just for the influxdb, it must be an object for

@Becker
I think this is an ideal situation to use a JavaScript switch

var P = msg.payload["Pth"];
var M = msg.payload[43086];
switch (M) {
	case 0:
		M = "Pth_aus";
		P = 0;
		break;
	case 20:
		M = "Pth_WW";
		break;
	case 30:
		M = "Pth_H";
		break;
	case 60:
		M = "Pth_PC";
		break;
	default:
		M = "ERROR";
}
msg.payload = P;
msg.topic = M;
return msg;
1 Like

In this case, yes. They're probably executed in the exact same way.

However, I do not like having to break each branch in the Javascript switch (for long branches, the potential for disaster if you forget one is astounding), and that's why I often use IFs (personal preference).

Ifs also allow to compare more than one variable, if you need to add more than one condition, and can compare different variables for different branches if necessary.

But again, in this particular case, it won't make much of a difference, and I doubt execution time changes much between both.

I don´t get a object output

var P = msg.payload["Pth"];
var M = msg.payload[43086];
if(M == 10) {
    M = "Pth_aus";
    P = 0;
}
if(M == 20) {
   M = "Pth_WW";
}
if(M == 30) {
    M = "Pth_H";
}
if(M == 60) {
    M = "Pth_PC";
}
msg.payload = {payload:"P"};
return msg;

I need an output as:
Object
-Pth_H = ...
-Pth_WW = ...
-Pth_PC = ...

:thinking:

If you can send a JSON object and the DB parses it into a record, it's easy, you just need to prepare it in the function. You have many different ways of doing it, this is one of them:

//declare the empty object
var db_object = {};
// assign the properties 
db_object.Pth_H = (valuePth_H);
db_object.Pth_WW = (valuePth_WW);
db_object.Pth_PC = (valuePth_PC);

//now either you pass the object as the payload
// msg.payload = db_object;

//or

//pass the object inside the payload
// msg.payload = { db_obj: db_object};

Of course, this assumes you have those values previously.

If the readings are synced (you read them with the same interval) it's better to use a join node, and then process the message in a function or a change node to create an object.

if the readings are not synced (that you may have more than one reading on one sensor for each on the other ones), it's better to use global context, then use an inject node to read the values at fixed intervals.

1 Like

thank you,

I think I got a solution:

var Pth = msg.payload["Pth"];
var M = msg.payload[43086];
var Pth1;
var Pth2;
var Pth3;
var msg1 = {};
var msg2 = {};
var msg3 = {};
var msg4 = {};

if(M == 10) {
    Pth1 = 0;
    Pth2 = 0;
    Pth3 = 0;
}
if(M == 20) {
   Pth1 = Pth;
   Pth2 = 0;
   Pth3 = 0;
}
if(M == 30) {
    Pth1 = 0;
    Pth2 = Pth;
    Pth3 = 0;
}
if(M == 60) {
    Pth1 = 0;
    Pth2 = 0;
    Pth3 = Pth * (-1);
}

msg1.payload = Pth1;
msg1.topic = "Pth_WW";
msg2.payload = Pth2;
msg2.topic = "Pth_H"
msg3.payload = Pth3;
msg3.topic = "Pth_PC";
msg4.payload = msg.payload["Pel"];
msg4.topic = "Pel";
return [msg1,msg2,msg3,msg4];

the output can go directly into influxdb
grafik

Is it possible to rename the object ? the name of the object is always the last topic.

There are several issues with your code.

First, I do not understand why you join messages first, process the messages separately, then join them again afterwards. It does not seem logical to me.

Either you join them earlier, then you get all the parameters for the function and can directly build the object without need of the second join node, or you don't, and then you have to join after the function.

Second, you're still using your old IF structure. I suggested using if - else if - else structure, which is more efficient and saves you processing time. @zenofmud suggested using a switch case instead, which is also more efficient and better than the if ... if... if... structure.

You can find about if-else if -else here
You can find about switch here

Could you post the objects that you get before the function and what you're looking for after it? Because I'm still not sure what you have for processing and what you want to get at the end.

1 Like

thanks for explanations :nerd_face:

I try to describe it: Pth is the thermal power of the heat pump. It is always calculated using the same formula.
now I would like to divide the thermal output depending on the operating mode of the heat pump, just like the integrated heat meter does.
for this I take the operating mode output (M).
to use the 3 input values in the function, I used the first join node.
to write the 4 output values into an object for Influxdb, I used the join node again.

If-else I´ll change, I didn't know that this is relevant for cpu-load.

Can you paste one of the messages that you're getting as an input for all this process?
You can use a debug node and change it to display the whole msg object instead of "msg."
image

Here you can find information about how to work with messages, you can just copy the value and paste it in a preformatted text block with this button:
image

Pth (calculated):

{"topic":"Pth","payload":1707,"raw":{"register":"40072","factor":10,"size":"s16","mode":"R","titel":"BF1 EP14 Flow","info":"Current flow EP14|Current flow EP15","unit":"l/m","min":"0","max":"0","logset":true,"data":9.1,"raw_data":9.1,"timestamp":1622038201687},"_msgid":"f3a5f191.83dfc"}

operating mode (NibePi Input):

{"topic":"43086","payload":30,"raw":{"register":"43086","factor":1,"size":"u8","mode":"R","titel":"Prio","info":"Indicates what heating action (HW/heat/pool) currently prioritised 10=Off 20=Hot Water 30=Heat 40=Pool 41=Pool 2 50=Transfer 60=Cooling","unit":"","min":"0","max":"0","logset":false,"data":30,"raw_data":30,"timestamp":1622038236527},"_msgid":"ca52d597.17fda8"}

grafik


with else:

var Pth = msg.payload["Pth"];
var M = msg.payload[43086];
var Pth1;
var Pth2;
var Pth3;
var msg1 = {};
var msg2 = {};
var msg3 = {};
var msg4 = {};
if(M == 10) {
    Pth1 = 0;
    Pth2 = 0;
    Pth3 = 0;
}
else if(M == 20) {
   Pth1 = Pth;
   Pth2 = 0;
   Pth3 = 0;
}
else if(M == 30) {
    Pth1 = 0;
    Pth2 = Pth;
    Pth3 = 0;
}
else if(M == 60) {
    Pth1 = 0;
    Pth2 = 0;
    Pth3 = Pth * (-1);
}
msg1.payload = Pth1;
msg1.topic = "Pth_WW";
msg2.payload = Pth2;
msg2.topic = "Pth_H"
msg3.payload = Pth3;
msg3.topic = "Pth_PC";
msg4.payload = msg.payload["Pel"];
msg4.topic = "Pel";
return [msg1,msg2,msg3,msg4];

Okay, my question here is: Do you calculate the Pth topic from the same data as NibePi input? Or they do come from two completely separate places?

If they do come from different places, are they in sync? Meaning, are you gettign one message of the NibePi input for each message with the Pth topic, or the frequencies are different?

If they are the same, you may want to check this example.

If not, I recommend you that you store the values separately in a global object that contains the values you need, as described in the context documentation. This allows you to store and update each variable inside the object separately. After that, you can retrieve the full object periodically with an inject node and a change node that reads the global object and puts it as the payload of the message.

Pth comes from 3 register: forerun/return temperature and flow * factor.
these registers are updated every 2 seconds. (in the picture all of the LOG.SET values)
The other registers are updated every 40-50s, as operating mode.
So I have to join the output of Pth (2s update) with operating mode (40-50s) to calculate with them.
The output gets together with the LOG.SET + some other registers every minute to influxdb.

If not, I recommend you that you store the values separately in a global object that contains the values you need, as described in the context documentation. This allows you to store and update each variable inside the object separately. After that, you can retrieve the full object periodically with an inject node and a change node that reads the global object and puts it as the payload of the message.

yeah I know this possibility, but don't see any advantage in my situation.

When you have to join several inputs with different update timings, you can store the variables in a global or flow object. This avoids the need for joining messages, you just update the values as they come.

Whenever you need to read the values, you can read the whole object and get all the variable values in a single go (for instance, with an inject node and a change node to put the stored object as the msg payload).

I use this system to get statuses from 6 mobile robots and about 30 docking stations, each updating at different, variable rates. It works fine, and I can avoid delaying messages until I have updates from each and every one of them.

can you please show me your flow for example or a part of it in a picture?
i'm not sure i got it right.

I know how to store in a global/flow variable, but not in a global/flow object.