What is causing object in context to change?

Hi all

Is there a way to somehow see what is updating an object in global context?

I am pretty sure there is not. But is there another way to monitor IF and WHEN an object is changing? Any kind of dirty hack is fine. Right now I have used inject node set to 1s to a function that writes the object to a file, but I need the resolution to be faster than 1s as I think there's a timing glitch in my dodgy programming.

I'll explain why here:

I use global context quite a lot to store information about the state of something external. In this case, it's an audio amp that is being polled periodically over RS232, to see if its settings were updated externally (i.e. from the amplifer or one of the many keypads around the house).

When the device responds to my request, I store the response in global context THEN pass a message to another flow that reads it from context and updates a Template UI node so I can see e.g. volume etc. from the dashboard.

Yes I know this sounds absurd, but my reasons (e.g. for not using MQTT and persistence to do something similar) are as follows - there is undoubtedly fault to my reasoning and I'm open to hearing about that!

  1. it's my way of being able to design a different UI later without having to remember what format the message comes in, etc.
  2. I like to be able to diagnose problems after a power outage, server reboot, etc.
  3. Also if the device hasn't been polled for a while, and I want some external display to reflect its latest status, I just need to get that display to query the context store and I can be confident it will be up to date (ok maybe MQTT persistence can do this - but can it after a server reboot?)
  4. I like to be able to use the context browser to see everything in one place, and it works nicely for me
  5. I like to add metadata such as "time updated" and additional settings e.g. zone names that don't come through from the original RS232 message...

Here's my actual problem. In the above screenshot you can see "volume" is a number. It comes in over RS232 as a string with leading zero. I use parseInt() to convert it before storing it to global context.

But sometimes, every so often, my Template UI is updated with the string, causing it to empty the data passed to the slider, and the display kind of flashes around.

I'm convinced the best way to resolve this is to somehow monitor to see if this is a timing issue caused by my code that writes to global context, or a timing issue caused by my code that reads and updates the display...

Why don't you check to make sure it is a number at this point?

It's quite hard to catch (it doesn't happen often) and I have a matrix mixer of 12 sliders, and it's difficult to see when it happens. But I did manage to catch it coming in as a string. Problem is I don't quite know why. I just wanted to confirm my suspicion that something (something I have done!) is actually writing the string to memory.

Perhaps I should just review my code a bit more rigorously - undoubtedly I could fix it by doing this. Buit thought I would ask here for guidance in case there was a clever way to attach something to an object in context....?

Could you show us the code where you pick it up as a string and convert to a number?

// Match only on incoming full single-zone response from RS232
 if (msg.payload.search(/^\#\>[1-3][1-6]\d{20}$/) != -1) {
    d = new Date().toLocaleDateString(); t = new Date().toLocaleTimeString();
    // get everything after the >
    msg.payload = msg.payload.split(">")[1];
    // set first part of topic
    obj = {
        updated_date: d,
        updated_time: t,
        pa_announcement: msg.payload.substr(2,2),
        power: msg.payload.substr(4,2),
        mute: msg.payload.substr(6,2),
        do_not_disturb: msg.payload.substr(8,2),
        volume: parseInt(msg.payload.substr(10,2)),
        treble: parseInt(msg.payload.substr(12,2)),
        bass: parseInt(msg.payload.substr(14,2)),
        balance: parseInt(msg.payload.substr(16,2)),
        source: msg.payload.substr(18,2),
        keypad_connected: msg.payload.substr(20,2)
    }
    // Store settings to memory
    global.set("home.audio.zoneamps." + msg.payload.substr(0,2), obj)

    // Return zone ID for which this message applies
    // (just so we can filter later)
    zone = msg.payload.substr(0,2)
    msg = {
        payload: zone,
        topic: ""
    }
    return msg
     
 }

Where are you declaring obj?

It is difficult to see how that can save anything other than a number or NaN.

When you say that you saw the value as a string, what was in the string?

When @zenofmud suggested checking that it is a number when you pick it up I suspect he meant checking it in the code (or a node) before making use of it. Then you could log an error to the node-red log and ignore that value.

1 Like

What I tend to do in these situations is create a single flow that handles incoming data and updates MQTT (because it allows event-drive processing) AND a global variable (for ease of access to the data).

Then I have a separate flow that responds to events (from MQTT) but uses the global variable to save faffing with complex MQTT data. The MQTT data therefore I tend to split into components such that the topics match the property structure of the global variable object. That way I get the best of both worlds.

So a similar process to you. But the key thing is to only have 1 place where all the updates come into so that you can enrich the data as needed and only process it once. That tends to eliminate the kind of problem that you are seeing.

Unfortunately, I don't know of any way (without hacking the Node-RED core code) to be able to see where updates to globals are coming from.

One other thing I tend to do with more complex data is add some additional sub-topics to things that are being updated, notably an ..../updated (timestamp) and ..../updated_by topic so that I can see what flow updated it.

image

This is brilliant! Thanks