Error or bug with global get when undefined

#1

Hi.

When i am trying to get a global variable with function
TotkWh = global.get("power.VK.gang.kWh.Total")||0;
i get this error:
function : (error) "TypeError: Cannot read property 'VK' of undefined"

If i write it without . like this it works:
TotkWh = global.get("powerVKgangkWhTotal")||0;

i am using ||0 to get a 0 for the first read when it is undefined.

Is this a bug or am i using the wrong function?

Yes i know i can write it without . but i want it with it.

Here is the flow i am trying to write.

Flow

[{"id":"d0bcb4c3.860368","type":"inject","z":"f841627a.c1e92","name":"","topic":"","payload":"ON","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":100,"wires":[["6d88067f.f655e8"]]},{"id":"6a2a8908.b6a448","type":"inject","z":"f841627a.c1e92","name":"","topic":"","payload":"OFF","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":140,"wires":[["6d88067f.f655e8"]]},{"id":"a7fa44ca.2f3708","type":"comment","z":"f841627a.c1e92","name":"Floorheat","info":"","x":100,"y":60,"wires":[]},{"id":"5ca1fa09.178854","type":"power-monitor","z":"f841627a.c1e92","name":"VKgang","threshold":0,"startafter":1,"stopafter":1,"x":400,"y":120,"wires":[[],["5041b7d7.f4d068","7a26ef14.03737"]]},{"id":"6d88067f.f655e8","type":"function","z":"f841627a.c1e92","name":"Watt","func":"if (msg.payload == \"ON\"){\n msg.payload = 5000\n}\n else {\n msg.payload = 0\n }\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":120,"wires":[["5ca1fa09.178854"]]},{"id":"5041b7d7.f4d068","type":"debug","z":"f841627a.c1e92","name":"Stop event","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":570,"y":180,"wires":[]},{"id":"7a26ef14.03737","type":"function","z":"f841627a.c1e92","name":"Save","func":"//Get global kwh\nTotkWh = global.get(\"power.VK.gang.kWh.Total\")||0;\nDaykWh = global.get(\"power.VK.gang.kWh.Day\")||0;\nWeekkWh = global.get(\"power.VK.gang.kWh.Week\")||0;\nMonthkWh = global.get(\"power.VK.gang.kWh.Month\")||0;\nYearkWh = global.get(\"power.VK.gang.kWh.Year\")||0;\n\n//Calculate and add new power usage to kWh\nTotnewkwh = TotkWh + msg.payload.energy;\nDaynewkwh = DaykWh + msg.payload.energy;\nWeeknewkwh = WeekkWh + msg.payload.energy;\nMonthnewkwh = MonthkWh + msg.payload.energy;\nYearnewkwh = YearkWh + msg.payload.energy;\n\n//Save newkwh to global\nglobal.set(\"power.VK.gang.kWh.Total\",Totnewkwh);\nglobal.set(\"power.VK.gang.kWh.Day\",Daynewkwh);\nglobal.set(\"power.VK.gang.kWh.Week\",Weeknewkwh);\nglobal.set(\"power.VK.gang.kWh.Month\",Monthnewkwh);\nglobal.set(\"power.VK.gang.kWh.Year\",Yearnewkwh);\n\nreturn msg;","outputs":1,"noerr":0,"x":550,"y":120,"wires":[["2a7a781b.971438"]]},{"id":"2a7a781b.971438","type":"debug","z":"f841627a.c1e92","name":"Saved","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":690,"y":120,"wires":[]}]

It is for calculating the accumulated power use of my floorheat cables. Using the node-red-contrib-power-monitor node. I get an input event when it goes off and on and i know the Watt for the cable.

Thanks.

0 Likes

#2

I can confirm that one cannot have . in a context variable name, and I can imagine why. Whether that is a documented feature, an undocumented feature or a bug I don't know. There are probably a number of other characters that are not legal also.

0 Likes

#3

It works fine as long as it is defined first.

Se this flow:

Flow

[{"id":"3bce3fa7.489d4","type":"function","z":"f841627a.c1e92","name":"Save","func":"global.set(\"power.VK.gang.kWh.Total\",555)\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":400,"wires":[["a6870b51.0a0d18"]]},{"id":"6f924b72.354dc4","type":"function","z":"f841627a.c1e92","name":"read","func":"msg.TotkWh = global.get(\"power.VK.gang.kWh.Total\")||0;\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":340,"wires":[["d8e625c9.e5f408"]]},{"id":"7d51ee19.8cf72","type":"inject","z":"f841627a.c1e92","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":400,"wires":[["3bce3fa7.489d4"]]},{"id":"738ea82a.e6c888","type":"inject","z":"f841627a.c1e92","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":340,"wires":[["6f924b72.354dc4"]]},{"id":"a6870b51.0a0d18","type":"debug","z":"f841627a.c1e92","name":"Save","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":410,"y":400,"wires":[]},{"id":"d8e625c9.e5f408","type":"debug","z":"f841627a.c1e92","name":"Read","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":410,"y":340,"wires":[]}]

If you read it gets error. If you save it saves. And now you can read. So it is only the ||0 that is not working as expected.

Thanks

0 Likes

#4

I don't think it is the || 0 that is failing, it is the context.get("a.b.c") where it has not previously been set with context.set

0 Likes

#5

Your get would work if your "power" variable is an object with a nested property structure. Your undefined result is saying that the function is looking for a property that doesn't exist, leading me to suspect you're not storing as an object.

0 Likes

#6

Power isn't a variable when used like this (or at least it shouldn't be, I don't think), it is a string that happens to have dots in it. Having said that I haven't examined the docs for context.set/get. Not at pc at the moment.

0 Likes

#7

Ok.

Is there another way i can easy check if it exist or have been set and if not set it to 0?

If i save it first it is saved as an object i think? check screenshot:


I now can use a global.get command and my flow works fine. But if i restart the pi and the variables are empty it does not work anymore and i have to set them to 0 again. I tought that by adding || 0 it would add it to 0 if it does not exist. It does this if i dont have any . in the variable name.

0 Likes

#8

I' m using an object called state and store it to a global variable.

But I first get/set the whole object:

global.set('state', state);
var state = global.get('state');
...
var value = state.value;
0 Likes

#9

A context variable name must be a valid JavaScript identifier.

That means it cannot have dots in its name, nor can it have [ ] - and I'm sure some other punctuation.

When you do context.get('parts.foo') we will look up the context variable called parts and return its foo property.

If parts doesn't exist, then parts.foo will not exist.

In Node-RED 0.19.5:

  • flow.get('parts.foo') will return undefined
  • global.get('parts.foo') will throw the error seen in the original post.

This difference in behaviour is a bug that has been fixed in 0.20. They now both return undefined.

The code:

var foo = flow.get('parts.foo') || 0

will set foo to 0 if flow.get('parts.foo') returns a false-like value (undefined, 0, false "" etc). But that is just setting the local variable - it doesn't change what is in context.

So @Lost, what you are trying to do will work once we release 0.20.0. Whilst you are on 0.19.5, you can either switch to flow context rather than global to avoid the bug, or wrap it in a try/catch block.

1 Like

#10

Thanks knolleary for the detailed responce.

Here is what i am running and can confirm that it works as expected if i run it as a flow variable istead of a global.
function
Will just use the flow variable and move everything to same flow for now until 0.20.0 as i dont know how to wrap it in a try/catch block.

Thanks.

0 Likes

#11

No problem - glad you got it working. Just so you know, the try/catch block method would look like:

var TotkWh;
try {
   TotkWh = global.get("power.VK.gang.kWh.Total")
} catch(err) {
   // an error has been thrown - ignore it and move on
   TotkWh = 0;
}
0 Likes

#12

though in general it's usually best to try to keep the scope of variables as small as possible (i.e. prefer local (inside a function) - then flow - then global) to help avoid name clashes etc when you copy things around.

0 Likes

#13

Wow thanks @knolleary that worked.

Bit of topic but is it possible to save a variable to a file from a function node simular to saving to global variable? Would be handy to have some variables restart proof. I know i can change so that all the variables goes to a file instead of ram but would be nice to be able to save some variables to file and some to ram from the function node.

Edit: Never mind just found https://github.com/node-red/node-red/wiki/Design:-Persistable-Context so will study that as i think it will do what i want.

Thanks

0 Likes

#14

That's the internal development design doc. The user docs for context are here: https://nodered.org/docs/user-guide/context

0 Likes

#15

You can also read A guide to understanding 'Persistent Context'

0 Likes