Node-RED knows something – combining JSON messages without forgetting previously known values
Generally speaking this is how I learned to cache a JSON object in a Node-RED flow with the Change node by setting a flow based variable.
Specifically this handles a problem with HomeAssistants light card backed by a MQTT light (defined in the configuration.yaml) only emitting partial values of the light setting when updating. New values would overwrite old ones as expected, but also delete key / value pairs not contained in the latest message payload.
The MQTT light has some basic values like an on / off state, a brightness value, the RGB color (array of three integers) and an effect with a string containg its name.
If the brightness is changed in the light card in HomeAssistant, the JSON published at the MQTT server contains the state on and the new brightness. The Node-RED flow handling the topic to set the light values published the update at the light state topic and did not just update its containing values but effectively overwrote the whole light settings. The key /value pairs for color and effect were now deleted.
I tried fixing it in HomeAssistant’s configuration by forcing to always send all values (by removing the if-clauses -> see below), but the light card just did not send values for color or effect when changing the brightness. After debugging the use cases with Node-RED I saw that the light card never sent all values for the light. As I didn’t want multiple topics for the light but one containing all of them I had to move to Node-RED and make sure that issue is caught there. I would still prefer to change it in HomeAssistant (where it happens), but I couldn’t find a solution for it atm. Please write a comment if you know how to!
mqtt:
light:
- name: "Office Atmo Light"
unique_id: "office_atmo_light"
schema: "template"
effect_list:
- standard
- nightlight
command_topic: "office/atmo/set"
command_on_template: >
{"state": "on"
{%- if brightness is defined -%}
, "brightness": {{ brightness }}
{%- endif -%}
{%- if red is defined and green is defined and blue is defined -%}
, "color": [{{ red }}, {{ green }}, {{ blue }}]
{%- endif -%}
{%- if effect is defined -%}
, "effect": "{{ effect }}"
{%- endif -%}
}
command_off_template: '{"state": "off"}'
state_template: '{{ value_json.state }}'
brightness_template: '{{ value_json.brightness }}'
red_template: '{{ value_json.color[0] }}'
green_template: '{{ value_json.color[1] }}'
blue_template: '{{ value_json.color[2] }}'
effect_template: '{{ value_json.effect }}'
After searching for a while I found an old post on the Node-RED forum that did what I wanted. Even containing a working example! Thank Steve-Mcl!
It takes the new message, adds the cached payload to it and combines it with Object.assign(target, source, source). It caches the new updated and accumulated values and publishes them. To make sure all values are present from the beginning, an Inject node is used filling the cache with default values when the flow initialized.
Inject node “initial value” contains a complete set of setting values in a JSON object and is set to inject it once.
{
"state": "on",
"effect": "standard",
"brightness": 128,
"color": [
180,
70,
0
]
}
The Change node “save cache” defines a variable in the flow named flow.setting and stores the JSON payload in it.
if(!msg.payload) return null;
msg.payload = Object.assign({}, msg.setting, msg.payload);
return msg;
The Function node “combine” takes the two JSON object in the new msg and merges them using the Object.assign method. Parameters are target, source1, source2. “target” it is an empy JSON that will be filled with the key / value pairs of the following source JSON object. The old cached values (“source1”) are copied in there first. The values from the new message (“source2”) are copied in there second to overwrite outdated ones. With this method missing values in the new message are filled from the cache.
Debugging the nodes shows that a message at the set-topic of the light containing only some setting values leads to a publish with all setting values at the status-topic.
Again what learned. Have a nice day and have fun experimenting!
And please don’t forget who scored four touchdowns in one game if you are learning something new. 😀
One Comment