Global context/variables

#1

I’m stuck.

I am writing code for multiple machines and it is the same on all, but for an identifier “tag”.

So as it is, I write the code with the “tag” in nodes here and there and it works.

Then when I copy it to another machine, I have to find (well, you know what I mean) all those tags and edit the “tags” to the new machine’s name.

THIS IS PAINFUL.

Using the global variable, can I get around this?

So I have one node which sets the global context of the “tag” and then all the sub-nodes where the name is/was, will get the global value?

If so, I may need a couple of examples because I am not getting how to do it.

Thanks much in advance.

0 Likes

#2

It would help if you could be a bit more specific about how you want to use this ‘tag’ in your flow.

You can use the functionGlobalContext setting in your settings file to pre-populate a global context property. You can then update the settings file with your desired value on each individual device.

For example:

functionGlobalContext: {
    foo: "bar"
}

Then you can use global.get('foo') to retrieve its value from within a function node, or use global.foo within the various node inputs that support accessing global context directly.

Nick

0 Likes

#3

Ok, my bad for not explaining it how I want.

But please understand I am not exactly sure how to explain it either.

So, as an example:
On all the devices I have a node (javascript thing) with code in it that sets, for instance, the msg.topic.

for the simplest example I can think of just now:

[{“id”:“74477ddd.a39eec”,“type”:“function”,“z”:“e2bd5a4e.5597e8”,“name”:"",“func”:“msg.topic=‘TimePi’;\nreturn msg;”,“outputs”:1,“noerr”:0,“x”:180,“y”:600,“wires”:[[“3c27284c.3a0958”,“bc72f9af.efded”,“a1020c16.f0066”]]}]

And there I set msg.topic to “TimePi”.

Rather than having to find all those nodes on that machine and edit them, I want to set a global thing so that all those nodes get “TimePi” from that and so I only edit ONE node and it propagates down to all others as needed.

Clearer?

Oh, P.S. I’ve tried a couple of ways to “block” the code (the “99” and the </> at the top, but neither seem to have any change on what I am seeing…

0 Likes

#4

To wrap code, put ``` on a newline before and after your code and it will be displayed properly.

If you follow my example of using functionGlobalContext in your settings file, then you could do:

functionGlobalContext: {
 myDeviceName: "TimePi"
}

Your function node to set msg.topic would be:

msg.topic=global.get('myDeviceName');
return msg;

Or you could use a Change node to set msg.payload to global.myDeviceName.

Nick

0 Likes

#5

Thanks much Nick.

Sorry, I was told about the " things, but I saw that at the top and it didn’t work.
Silly me to not remember to actually enter the character itself.

So, the

functionGlobalContext::{ myDeviceName:"TimePi"}

Ok, that is a node - I’m guessing. Where in the flow does it go?

(Phew, got the formatting working.)

I’ll play around and see if I can get it working.

0 Likes

#6

As I said in my original reply:

0 Likes

#7

Sorry. Missed it.

I was more concentrating on the NODE side of things and missed the SETTINGS FILE.

0 Likes

#8

Na, sorry. I can’t work out what it is I am missing.

Reading stuff about it all I am seeing is what you posted - and that’s fair enough but I don’t get it.

They seem to be written for people who already know the answer and not a “noob” like me.
I can’t “find” (or know) where these things being edited are.

I looked at the settings.js file and am not really confident enough to go blindly in and edit stuff.

I read I can get a “CONFIG” node.
Set flow context or global context properties on start up or as needed.
Inputs

If the node receives any message, it will apply the configuration properties contained in the Config node. The trigger message is discarded. This allows you to have alternate configurations and programatically switch between them as needed.

So I am trying that way.

This is the flow I have:

[{"id":"2fa5d66a.f706c2","type":"config","z":"b9c91e78.6a1b7","name":"","properties":[{"p":"context","pt":"global","to":"{ myDeviceName: \"TimePi\"}","tot":"json"}],"active":true,"x":210,"y":1260,"wires":[]},{"id":"8ff1f165.aa1c3","type":"function","z":"b9c91e78.6a1b7","name":"","func":"msg.payload=global.get('myDeviceName');\nreturn msg;","outputs":1,"noerr":0,"x":550,"y":1260,"wires":[["316358.0e0caca8"]]},{"id":"316358.0e0caca8","type":"debug","z":"b9c91e78.6a1b7","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":700,"y":1260,"wires":[]},{"id":"63af7212.51a83c","type":"inject","z":"b9c91e78.6a1b7","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":400,"y":1260,"wires":[["8ff1f165.aa1c3"]]}]

But even that doesn’t work. OBVIOUSLY because I am not doing something right. But I admit, I am not getting what I am missing.

Thanks in advance.

0 Likes

#9

If you are worried about messing up settings.js just keep a copy of it and then if it is a disaster you just have to restore it and restart node red.

settings.js is just a javascript file defining a load of stuff. In there you should already find a section functionGlobalContext, all you have to do is to add in there the globals you want as described by Nick. Just make sure you get the commas right if there is already stuff in there.

0 Likes

#10

In the mean time, I edited the “settings.js” file to this:

   functionGlobalContext: {
        // os:require('os'),
        // octalbonescript:require('octalbonescript'),
        // jfive:require("johnny-five"),
        // j5board:require("johnny-five").Board({repl:false})
        myDeviceName: "TelePi"
    },

And rebooted.

Then tried the latter part of the flow I previously posted.

Nothing.
Oh, and Yes I know I have changed it from msg.topic to msg.payload.
But for now to just get it working I’ll set it as payload rather than topic.

I’ll retract the last part.

Now (typically after a few minutes and my posting the confusion) It is working.

ARGH!

0 Likes

#11

(Head in hands with shame)

Ok, it is working as in: I can set the name and it is … there.

I have one example where I get it and set a TOPIC in a basic message flow.

Where I fell over is when MQTT is involved.

I have MQTT nodes and they are sending packets on channels with the device name as part of the topic.

eg:
MQTT TOPIC:
TEMP/(device name)

Now, I thought what I could do is prefix the device name with TOPIC/ then send it to the MQTT node and have the TOPIC set as {{msg.topic}}

Is that the right direction?

Or is there a way to append it in the MQTT TOPIC field on the MQTT node?

0 Likes

#12

(And not wanting to flood the thread)

I’ve been working on what I just posted.

Building the TOPIC in a node before the MQTT seems easier.

So I have this:

[{"id":"9ed05fb4.f4b728","type":"inject","z":"9b7e7466.a4b698","name":"","topic":"","payload":"boo","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":650,"y":840,"wires":[["cc2b24d6.d8fbc8"]]},{"id":"b0958d4d.f35c","type":"debug","z":"9b7e7466.a4b698","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"topic","x":1000,"y":760,"wires":[]},{"id":"771a258a.db51f4","type":"debug","z":"9b7e7466.a4b698","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1010,"y":800,"wires":[]},{"id":"3dd040c9.046da","type":"mqtt in","z":"9b7e7466.a4b698","name":"test topic","topic":"TEST/TelePi","qos":"2","broker":"3f12fa81.11a276","x":700,"y":920,"wires":[["de0bd61e.f9a45"]]},{"id":"7f07752c.ee0c64","type":"mqtt out","z":"9b7e7466.a4b698","name":"test topic","topic":"{{msg.topic}}","qos":"2","retain":"","broker":"3f12fa81.11a276","x":1000,"y":840,"wires":[]},{"id":"de0bd61e.f9a45","type":"debug","z":"9b7e7466.a4b698","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":920,"y":920,"wires":[]},{"id":"cc2b24d6.d8fbc8","type":"function","z":"9b7e7466.a4b698","name":"","func":"msg.topic=global.get('myDeviceName');\nmsg.topic =\"TEST/\" + msg.topic;\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":840,"wires":[["7f07752c.ee0c64","b0958d4d.f35c","771a258a.db51f4"]]},{"id":"3f12fa81.11a276","type":"mqtt-broker","z":"","broker":"192.168.0.99","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"20","cleansession":true,"willTopic":"EOM","willQos":"2","willPayload":"'TelePi telemetry failure'","birthTopic":"SOM","birthQos":"2","birthPayload":"'Awaiting Pi Face'"}]

I inject “boo” and go through a Function node. That gets the global name, appends it to “TEMP/” as the topic and sends on the entire message.

Two debug nodes show me I get the message and topic correct.
I send that to the MQTT node and send it.

The receiving MQTT node sees nothing.

In this example I have used TelePi as that is a remote machine and NOT the MQTT server, so better for testing.

0 Likes

#13

If you use fixed topic does it work fully? If so then put debug nodes to check what you are sending to the mqtt out to make sure it is right, and start with the mqtt In subscribing to everything to check the out is working.

0 Likes

#14

Possibly a slight tangent… If you just want to set some node parameters (rather than inside a function) you can also use system environment variables (outside of Node-RED). In a node property for example you can use $(MY_THING_NAME) and at runtime it will use the environment variable MY_THING_NAME - which can be useful when moving between machines etc. Note: you can only (currently) substitute the complete variable - you can’t do $(MY_THING)/extra

0 Likes

#15

Back to your current problem - the clue is in the Tip of the MQTT node...

ie - leave the topic field blank - then the msg.topic you pass in will be used.

0 Likes

#16

dceejay.

THANK YOU THANK YOU THANK YOU.

Now working and now I can move on to the next stage of my writing quest.

0 Likes