Context variable that (partly) references other context variables dynamically

In your example, you are setting a home assistant global to a value. I wanted to point out that doesn't work.

Not really. I don't have HA - it is simulating what HA might do to demonstrate how references work. The fact HA is mentioned is really of no relevance other than the OP uses HA and wants to reference a value that is set by HA.

And as I stated, I did that for demonstration purposes to simulate what HA does. The OP is aware of this based on their demo flow (they did not do direct setting of context values)

1 Like

You are right the OP did not ask to set a global. While they might not want to set the value of a global now, they may want to in the future. The example you posted is misleading.

Your example, you are changing a copy of HA's state machine to forward the value to a flow variable. Problem is, your flow works, in the sense will update the flow variable with the value you set to the global.

However the global was not changed and moreover the actual entity the global represents has a different value then what was set to the flow variable.

The example I posted is a simulation since I don't have HA.

even if I did have HA, I would not have the same entities and I'd still have to simulate what HA does to demonstrate how references work.

I'm failing to see how that is unclear.

No. I am setting a value of a global context variable that also has a reference set upto it's parent object in flow context. The fact it is named homeassistant is by the by in this demonstration.

Please remember I do not have HA and was simulating what HA does to global context. I merely pointed a flow variable at a global variable to demonstrate by reference changes. I never once inferred or stated this would affect the state of the HA entity. In fact I repeatedly said I was simulating of what HA might do (as in updating a value in global context).

The globals are created and updated by HA, they are a copy of all entity states in home assistant.

Lets say I have a light in HA that is off. In NR I have a global for that light and it is also off. I can change that light global in NR to on.

Now anything in NR that uses that global sees that light as on when in reality it is off. The next time the entity updates in HA, the global returns to off since the actual state of the light has not changed.

Because this was a post about using NR with HA, knowing that you do not use HA, seeing the example you posted, I understand it is perfectly valid in NR, the clarification that it is not applicable to HA globals, I felt was relevant.

Interesting question.

Can a context variable partly reference another context variable? - Yes
Does this work for the HomeAssistant WebSocket Global variable? - Yes and No.

Part One:
Since JavaScript under node.js does indeed keep objects on the heap and uses copy by reference rather than copy by value, making a copy variable 'b' of an object variable 'a' just means that a.whatever is the same as b.whatever. This is something that caught me out when first using Node-RED, and I now always tick the 'deep copy' option in a change node to prevent one variable updating another unexpectedly.

This behaviour does indeed extend to context variables, which is where I first noticed it. Holding an object in msg.payload and copying (saving) to flow.saved (without the 'deep copy' option) means that subsequent changes to msg.payload.whatever also change flow.saved.whatever. A test with Change nodes shows that this "works" the other way around: copy msg.payload to context, change the context, and msg.payload changes also, and yes, copy a context object variable to another context variable and they are indeed linked by reference.

I can see that this occurs on a simple equivalence assignment, as in a = b, but then wondered if it also operates with assignment of sub-objects and super-objects. A quick test in JS in a function node confirms that it does.

let a =
{
    "top": {
        "middle_a": {
            "end_1": 1,
            "end_2": 2
        },
        "middle_b": {
            "end_3": 3,
            "end_4": 4
        }
    }
};

let b = a;
msg.a = a;
msg.b = b;
msg.a.top.middle_a.end_2 = 42;

let c = a.top.middle_a;
msg.c = c;
msg.c.end_2 = 50;

let d = {"mycopy": a, "other": "testing"};
msg.d = d;
msg.d.mycopy.top.middle_a.end_2 = 22;

return msg;

Returns as expected, with a.top.middle_a.end_2 and b.top.middle_a.end_2 and c.end_2 and d.mycopy.top.middle_a.end_2 are all 22

Part Two:
The Home Assistant WebSocket nodes work through a WebSocket server connection back to Home Assistant, and as far as I understand the workings, the entire HA state model is read or polled as required. This is in addition to the WebSocket listening to the HA event bus, so the WebSocket nodes respond to things like state changes by listening to events, and not by checking the state record. The state record copy to Global Context is optional, and I assume that the internal copy is used primarily (only) for reference to such things as the services provided and for auto-complete of entity names in the node UI.

As such, the Context copy is for reference only, and it is unlikely that each individual entity record is updated in the copy at each state-change event. My assumption is that the context record is overwritten in entirety (or as a block) as and when either an active node responds to an event, or on a regular basis if the heartbeat option is enabled.

So, after a bit of testing, I can get this to "work". But only so far.

If I copy the homeassistant.homeAssistant.states part of the Global Context variable, to a local flow variable, then my flow copy is updated along with the HA Global (ie they both point to the same object in memory).

If I try to copy the homeassistant.HomeAssistant.states["sensor.my_entity"] part, then it does not work.

Since the copy is by reference, it could be argued that there is no extra overhead in keeping a duplication of the entire HA states in a flow context. However, getting at the actual state value still requires ["sensor.name"].state, and I am inclined to think that using a simple Events: states node to update a copy flow sub-object state value directly would be tidier.

This still leaves the question as to why the reference copy does not work on the sub-object at the sensor level, and I wonder if the ["sensor.name"] referencing disrupts the usual assignment procedure. A quick check with JSONata using $globalContext("homeassistant").ha1Pi52.states.'sensor.mb_load_power' works up to the states field as before, but not for the individual sensor field value.

Well, that was slightly more interesting than doing my SuDuko this morning.

1 Like

I will say this one more time. It extends to MEMORY BASED context variables. It DOES NOT extend to other types of context variables. Since those will all be, at some point, serialised and un-serialised. Serialising a JavaScript object will flatten a referenced object unless the context library has built in some mechanism to handle such a serialisation.

Even for file-based retained context variables, if you restart Node-RED, any reference will have been flattened.

I tried it. It works (at least for memory context, some of the time).

I'm sorry I posted. The Home Assistant forum is a lot more friendly and forgiving.

I won't bother you again.