In Node-Red: how to address a global variable with a dot in the name?

Hi,
In Node-Red, I want to read the value of a global variable that has a dot in the name
As an example, I want to:
global.get("homeassistant.MyHomeAssistant.states.sun.sun.state");
The problem is the name of the variable sun.sun
I tried several ways to escape that dot, but without any luck.
Any tips are welcome,
thanks,
chrisV

Problem solved:
Iā€™m re-posting the answer here in case someone else has the same issue.
I had also posted this to the Node-Red forum and user @knolleary suggested to use an alternate syntax like this:
global.get("homeassistant.MyHomeAssistant.states['sun.sun'].state")
ā€¦which solved the issue
Learned 2 things here:

  1. there is an alternate syntax to address the ā€œnodesā€ in a JSON object
  2. single quotes can also be used and they donā€™t terminate the double quotes
    chrisV
1 Like

I have same issue , want to access state of specific element but I cant do that from the flow.

Tried your code (adjusted to my needs)
var haCtx = global.get(ā€œhomeassistant.Home_Assistant.states[ā€˜switch.control_ac_emillyā€™].stateā€);

but no results like its coming null.

How can I debug/check ? @chrisV

Canā€™t get your example to work @chrisV

Trying two ways. Function and Switch.

Function

var item = global.get("homeassistant.homeAssistant.states['input_select.livingroom_mode'].state");
node.warn("find it 1 "+ item);

var item1 = global.get("homeassistant").homeAssistant.states['input_select.livingroom_mode'].state;
node.warn("find it 2 "+ item1);

return msg;

This will log find it 1 undefined and "TypeError: Cannot read property 'state' of undefined".

Switch

Here I donā€™t get any error, nor does the debug node run. Iā€™m trying to check global and the value

homeassistant.homeAssistant.states['input_select.livingroom_mode'].state

Looking at the vars they should be there. Would be really nice to run a switch to grab existing value instead of having to setup a current state node everytime.

image of context.

Figured it out

# get global variable
var g = global.get("homeassistant");
# get states variable
var states = g.homeAssistant.states;
# get the actual entity that we want
var player = states["media_player.livingroom"];
1 Like

My apologies, @Evgeny_Golubov and @fbacker I must have missed your earlier replies.
@fbacker Glad you got it working
@Evgeny_Golubov to troubleshoot, you can add a function node and send output to a debug node (see also the commented lines in the function node)

[{"id":"3c0ba1d6.c8ad9e","type":"tab","label":"dummy test","disabled":false,"info":""},{"id":"9b57ce79.ae3158","type":"function","z":"3c0ba1d6.c8ad9e","name":"","func":"\n\n// msg=global.get(\"homeassistant.homeAssistant.states['sun.sun'].state\");\n//msg=global.get(\"homeassistant.homeAssistant\");\n\n\nmsg=global.get(\"homeassistant.homeAssistant.states['sun.sun']\");\nreturn msg;","outputs":1,"noerr":0,"x":272,"y":271,"wires":[["6e5e08ee.1fec3"]]},{"id":"6e5e08ee.1fec3","type":"debug","z":"3c0ba1d6.c8ad9e","name":"Debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":507,"y":273,"wires":[]},{"id":"7f2e8e7d.82e158","type":"inject","z":"3c0ba1d6.c8ad9e","name":"now","topic":"","payload":"now","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":124,"y":127,"wires":[["9b57ce79.ae3158"]]}]

hope this helps,
ChrisV

1 Like

Hi, I copy all. Then no happen. but
I change like this

//msg.payloadmsg=global.get(ā€œhomeassistant.homeAssistant.states[ā€˜sun.sunā€™].stateā€);
//msg.payload=global.get(ā€œhomeassistant.homeAssistantā€);

msg.payload=global.get(ā€œhomeassistant.homeAssistant.states[ā€˜sun.sunā€™]ā€);
return msg;

-> return: ā€œundefinedā€

Where am I wrong?

Hi @Kydan
Iā€™m trying to understand what you did.
Were you able to:

  1. Import the flow (that I posted), in Node-Red?
  2. Can you share a print-screen of the debug message that you get? Click on the square witch is attached to the left of the Inject Node, to trigger an event. And then open the debug output to display the debug message.

Here is a screenshot of what you should see

hope this helps,
chris

Thanks. I just fix problem. Just rename ā€œserverā€ to any other name, in Node red. Thatā€™s all.

Hi @Kydan
Iā€™m sorry, but I donā€™t understand what you mean with ā€œserverā€
Anyway, Iā€™m glad that you got it working
chris


This

yes, that entry has to match with your home assistant deployment
chrisV

hello! Iā€™m trying to set the AC temperature to the value of a helper.

I know how to make it through a node function, but I would like (in order to learn a bit more) to do it directly with a call service node. Something like this:
temperature: global.get(homeassistant.homeAssistant.states[ā€œinput_number.t_ac_max_bnh_0ā€].state)

Is it possible? Where is my error? Thanks!

let tv = global.get(ā€œhomeassistant.homeAssistant.states[ā€˜media_player.sony_kdl_55w800cā€™].stateā€);

This works for me. This is defining tv before all the if statements.

Hello! thanks for your anwer, but it if not working for me. At the beginning I was getting an error with the ā€™ (expecting "), I changed them and tried different combinations but nothing.

Final try has been this one, but nothing:

{ā€œtemperatureā€: global.get(ā€œhomeassistant.homeAssistant.states[input_number.t_ac_max_bnh_0"].state)}

I get this error:

msg : error
ā€œHomeAssistantError: must contain at least one of temperature, target_temp_high, target_temp_low.ā€
but I dont understand what you mean by " This is defining tv before all the if statements." I am trying to use a ā€œcall service nodeā€.

You are missing some quotes. One at the start of the entity and one at the very end.

global.get('homeassistant.homeAssistant.states["input_number.t_ac_max_bnh_0"].state')

That only works in NR nodes like a function node. Inside a HA node use

$entities('input_number.t_ac_max_bnh_0').state

Edit You are using pretty quotes, they donā€™t work in NR.

Use standard double or single quotes.

if you get an error with that it likely needs to be converted to a number, in that case use

{
   "temperature": $number($entities('input_number.t_ac_max_bnh_0'))
}

thank you all for all the replies!
I have been using the ā€œprettyā€ quotes beacuse they worked when using the following:

{ā€œtemperatureā€:22}
PS: The ā€œprettyā€ quotes are the default ones for my language configuration, Spanish: Shift + 2.

Anyway, I have been trying your suggestions (and variations) but nothing, still doesnā€™t work.
If use this:

{ā€œtemperatureā€: global.get(ā€˜homeassistant.homeAssistant.states[ā€œinput_number.t_ac_max_bnh_0ā€].stateā€™)}

The error shown is ā€œHomeAssistantError: must contain at least one of temperature, target_temp_high, target_temp_low.ā€

On the other hand, I have being checking how do I do these kind of calls for other flows with a Function Node:

msg.payload = global.get(ā€˜homeassistantā€™).homeAssistant.states[ā€œinput_number.t_ac_max_bnh_0ā€].state

This way the Function Node works. There are no { } (logically) and the structure is slighly different, but I have no idea yet.

Thank you all again for your time. As I said, I can cover my needs with a Function Node but, in order to learn a bit more and have a tidier and nicer whiteboard, I would like to make it work with a Service Node, thanks!

JSON (JavaScript Object Notation) structures with special characters in the field names require non-standard referencing.

The referencing syntax depends on the language you are using.
For JavaScript, the usual path reference notation is msg.payload.myfield

Where the field name is a Home Assistant entity, HA make the entity name a combination of the domain and the actual name with a . in the middle. Hence input_number.my_value. This is a bit of a challenge as you canā€™t use msg.payload.input_number.my_value as it will not work!

For JavaScript (ie code within a function node) the referencing format is

[ā€œinput_number.my_valueā€]

which makes the name (including the errant .) a string, and references the object field using the [ ] notation.

In NR the JavaScript function global.get('homeassistant') returns the value from the global context variable ā€˜homeassistantā€™, which is an object, and therefore

global.get('homeassistant').homeAssistant.states["input_number.my_value"].state

does the job of both getting the context varaiable and walking down the path to the input entity state value.

Should you wish to do this directly in the HomeAssistant WebSocket nodes, the most useful approach is to use JSONata (that J: expression option). The fun here is that JSONata is another quite different language, and has a different set of requirements for the same job.

First, we need the JSONata function $globalContext() to get the global context variable. This is a special JSONata function that is added to Node-RED, and will work in any JSONata enabled node (such as the change node), therefore

$globalContext("homeassistant").homeAssistant.states

will get the HA states object. Note that the function call string parameter, as a string literal, can be enclosed by " or by ' as long as you are consistent.

Second, we need to get at that tricky field name. In JSONata this is done directly in the path expression but using back ticks to constrain the JSON path reference field. The full reference path is something like

$globalContext("homeassistant").homeAssistant.states.`input_number.t_ac_max_bnh_0`.state

Third, once we have the state value, this is most likely to be a string, and therefore where the service call is expecting temperature to be a number, we need to convert the state value.

JSONata has a $number() function, and we could enclose the above as a parameter to this function. However, since things are getting long and complex, the use of the JSONata function chaining operator ~> can help provide visual clarity.

"70" ~> $number() chains the result of, in this case a string literal, with the following function, passing the output of the left hand side as context and then replacing the optional input parameter to the $number function. It works!

Therefore your Service Node - Data field, using JSONata, would be (if I have typed everything correctly)

{"temperature": $globalContext("homeassistant").homeAssistant.states.`input_number.t_ac_max_bnh_0`.state ~> $number()}

This works nicely, and since it uses functions that are available throughout Node-RED, this JSONata code can be used in change nodes and inject nodes.

All of this works by getting an entity state value using the HA global context variable. As has been said above, within the WebSocket nodes (and only these nodes) there is another special function $entities() that can be used to return any entity. In your case this would be a bit simpler, and would be

{"temperature": $number($entities('input_number.t_ac_max_bnh_0').state)}

where the entity name (including the errant .) is passed as a string literal.

2 Likes

oh wow! i was literally impressed, thank you for this very comprehensive post!! I do understand way better how this new language (for me) works, thanks a lot!

Sorry by the delay, I canā€™t dedicate to HA as much time as I would like, but yes! it does work! :slight_smile:

Unlickily, the Beko integration is not working since 2024.05 update, so I will have to wait to test the final loop, but the important thing is that I am being able to send the value, thanks a lot to all and, specially, to @Biscuit !

Jose