node-red-contrib-context-hook
A Node-RED extension for state management.
Architectural idea
In order for a node to decide what will be the next output, it might need to take into account several inputs.
The logic, how the output is calculated, might be a combination of multiple conditional statements.
Instead of building complex flows with switch
and other function
nodes, the extension provides nodes
that will centralize the decision-making logic into one node, therefore managing this will become easier and
less error-prone.
The approach is inspired by the React useState
hook.
Whenever there is a change in the state that is used in the function (state-hook
), the function will run again.
Compared to building Node-RED flows as usual, this approach allows better control over behaviours that have multiple inputs.
Installation
Either use the Manage Palette option in the Node-RED menu or run the following command in your Node-RED user directory
npm i node-red-contrib-context-hook
Usage
The extension provides three nodes under the category "state".
In order to use the subscribe-state
and the state-hook
nodes, the set-global-state
node must be used beforehand.
The updates to the state (Node-RED global context) are only captured if the state is set by set-global-state
node.
🔸 Node set-global-state
This node is used to set values to the global context. After the value is set, an event is emitted to the system
that the other nodes subscribe-state
and state-hook
can listen to.
As an example, there is a simple flow to set kitchen temperature. The function in the set-global-state
node extracts
the temperature value from the message payload and sets it to the global context with the property name kitchen.temperature
.
If the function returns undefined
, the value is not updated and no event is emitted.
And then the value can be seen in the global context.
🔸 Node subscribe-state
This is a node that is used for listening to changes in the global context that are saved by the set-global-state
node.
If there has been a change in the context value, the node will forward the information about the change in the following format:
{
property,
previousValue,
value,
payload: value
}
As an example, let's listen to the kitchen temperature changes that were set in the previous set-global-state
node example.
First, add the subscribe-state
node to the flow and configure it to listen to the property kitchen.temperature
,
and then debug the message that is sent after kitchen temperature change is saved to the global context.
🔸 Node state-hook
. This is the node where the magic happens 🪄
Within the function in this node a hook called useGlobal
is available.
This hook can be utilized to watch changes in the global context that were saved via set-global-state
node.
The useGlobal
function takes in two parameters: property name from the global context and the default value for it.
The second parameter is optional and is null
by default. For example, to watch changes in the kitchen.temperature
value,
the useGlobal
function should be used like this:
const temperature = useGlobal('kitchen.temperature');
The state-hook
node comes in handy when the logic for deciding the next state for an output depends on many
incoming signals. Instead of connecting the signals with switch
, function
etc. nodes, all the logic can be handled
within the state-hook
node.
Let's take the following scenario and break it down into implementation steps: a light should turn on when there is movement in the room, and it is dark in the room. In all the other cases the lamp should turn off.
- Save the brightness and movement data to the global state via
set-global-state
nodes.
- Use the
state-hook
node to listen to changes in the global context with theuseGlobal
function and calculate the output for the light.
Whenever there is a change in either bathroom.illuminance
or bathroom.occcupancy
value, the function for calculating
the lamp state is run again.
Most probably the illuminance might be affected by the light being turned on, therefore updating the state of the illuminance should take that into account.