When a value of global variable in node-red changes, changes the appeareance of a button on the front

Hello everyone!

I'm quite new to node-red ui-builder and I have a question that I don't know how to solve

I'll give you some context. I'm making a button on the front, so that when the user clicks on it, it turns a light on or off and the button on the screen changes its state appearance to on or off.

In order for the front to communicate with the back, I'm using uibuilder.onTopic.

My problem now is in the other direction: from the back to the front. When the user physically goes to the button in reality, the user presses the button and the light turns on or off, that change is not reflected in the front through the front button.

An idea that has come to me to solve it is to create a global node-red variable and, depending on the value of that variable, one state or another of the button is displayed on the front.

Therefore, currently, I have the current state of the light saved in a global node-red variable. This structure actually shows the state of the light, as it is updated with any change in the light, whether through the screen, or through the person physically going there and pressing the button, etc. However, I don't know how to make it so that when certain values ​​in this structure change, the state of the light is displayed on the front end properly.

Is it possible to make it so that when a particular value in the structure changes (which is a node-red global variable) the appearance of the button on the front end is also updated?

Any help and/or recommendations would be appreciated.

Thank you in advance!

When the user physically presses the button, make NodeRed send a message to your UIBUILDER instance, and then (from frontend) get the message with

uibuilder.onChange('msg', (msg) => {
        if (msg.topic == "myButton") { 
           console.log(msg) 
        }
  }

Send the current status of the switch in msg.payload and consequently change the switch in frontend.
The global variable could still be needed to set the switch state when you first display the page in the browser.

1 Like

Unfortunately, Node-RED variables do not have events and therefore you cannot use updates to them to trigger a flow which would be the ideal.

You have a couple of options:

  • Wherever you have a flow that updates that variable, have a split that also triggers the update message to uibuilder.
  • Decouple the flows by outputting changes to MQTT and use an MQTT in node to trigger the action flows.

Thank you so much @TotallyInformation !

I am trying to do the first one, however, it does not work. I am pretty sure that it is something I don't quite understand.

In the html, I have for the button the "onTopic": lunchRoomLightStateOn and lunchRoomLightStateOff. In this way, when msg.topic is equal to one of these, the function "showButtonState(msg)" should be triggered:

  <input type="checkbox" id="lunchRoomLightState" onchange = "showLoadingVisuals();changeLightState(`lunchRoom`);uibuilder.onTopic('lunchRoomLightState', (msg) => {console.log('>> onTopic `mytopic` >>', this, msg); hideLoadingVisuals(msg)});uibuilder.onTopic('lunchRoomLightStateOn', (msg) => {console.log('>> onTopic `mytopic` >>', this, msg); showButtonState(msg)});uibuilder.onTopic('lunchRoomLightStateOff', (msg) => {console.log('>> onTopic `mytopic` >>', this, msg); showButtonState(msg)});uibuilder.eventSend(event)" data-type = "eventSend">

In the script, I have:

function showButtonState(msg){
    console.log(`I am into showButtonState`);
    // Verificar el valor de msg.topic y actualizar el estado del checkbox
    const checkbox = document.getElementById('lunchRoomLightState');
    if (msg.topic === 'lunchRoomLightStateOn') {
        checkbox.checked = true; // Marcar el checkbox
        } 
        
    if (msg.topic === 'lunchRoomLightStateOff') {
        checkbox.checked = false; // Desmarcar el checkbox
    }

}

And the msg that the uibuilder received is: lunchRoomLightStateOff or lunchRoomLightStateOn

Moreover, this msg arrives to uibuilder node:

Thank you in advance!

No! That is a really bad idea and causes a memory leak that could eventually crash the browser tab.

Basically you are telling the browser to set up another onTopic listener every time the input changes. You should only ever set up a specific onTopic or onChange once - generally on page load by loading the provided index.js script in the provided head link in index.html.

OnTopic listens for messages from Node-RED that contain a msg.topic of lunchRoomLightState. When the browser page gets one, it runs the provided callback function passing in the msg object for you to use.

Good practice is to have your on..... HTML attributes just call a single function. That function will inherit an object called event which contains the details of the HTML element that triggered the event. So either call your own custom function that does everything else or just call eventSend and hand everything off back to Node-RED, whichever is more convenient for you.


In addition, since you want to monitor 3 topic but really you aren't doing 3 different actions, only 1 action - turn on/off the checkbox - I would personally use a single uibuilder.onChange( 'msg', (msg) => { ... } ) handler instead of the three onTopic handlers.

You can simply use an if statement in the callback to decide what state to set.

I am afraid that's wrong @EBA ...
Try my suggestion, as @TotallyInformation suggests too in last paragraph.

1 Like