Saving a single value to context

I'm saving a JSON object to file context similar to;

{
  "192-168-1-10": [
    {
      "last_on": 1622751269764
    },
    {
      "last_off": 1622751272068
    }
  ],
  "192-16-1-13": [
    {
      "last_on": 1622751772269
    },
    {
      "last_off": 1622751774284
    }
  ]
}

The data is constructed as; "192-168-1-10 which represents the IP address of a connected device, and last_on & last_off are self explanatory timestamps.
Presently, when any of the status's change within each of the array's, I need to reconstruct the whole array, and then write it to context.
global.set(mystore.192-16-1-13, newarray);
The size of the arrays are likely to increase as new features are added, and ideally I would like to just update one value directly, leaving the other values in the respective array unchanged.
Is there a way to write a revised timestamp directly to one of the values, without writing the whole array?

Must your data be in that format (in particular the array with objects)? If you use object properties you can simply update the value.

Using my data above, can you give an example of what structure you recommend pls.

Sure. Ginnie a couple of mins.

global.set("mystore.192-16-1-13[0].last_on", 16227512720888);
should work, But as Steve said would be best with objects.

something like this, will allow you to add mre properties as and when.

{
    "192-168-1-10": {
        "last_on": {
            "time": 1622751269764
        },
        "last_off": {
            "time": 1622751272068
        }
    },
    "192-16-1-13": {
        "last_on": {
            "time": 1622751772269
        },
        "last_off": {
            "time": 1622751774284
        }
    }
}
1 Like
{
  "192.168.1.10": {
      "last_on": 1622751269764,
      "last_off": 1622751272068
    },
    "192.16.1.13": {
      "last_on": 1622751772269,
      "last_off": 1622751774284
    }
}

Working demo updating the values of the objects properties...

[{"id":"4d40b13c.c8309","type":"function","z":"1bd2b382.4dd4f4","name":"setup demo data","func":"msg.payload = {\n  \"192.168.1.10\": {\n      \"last_on\": 1622751269764,\n      \"last_off\": 1622751272068\n    },\n    \"192.16.1.13\": {\n      \"last_on\": 1622751772269,\n      \"last_off\": 1622751774284\n    }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":648,"y":256,"wires":[["2263910a.99b7ae"]]},{"id":"d96e022b.81642","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":412,"y":256,"wires":[["4d40b13c.c8309"]]},{"id":"2263910a.99b7ae","type":"change","z":"1bd2b382.4dd4f4","name":"","rules":[{"t":"set","p":"deviceMonitor","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":882,"y":256,"wires":[[]]},{"id":"22b4dbc7.70a144","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.10","payload":"on","payloadType":"str","x":432,"y":304,"wires":[["b241fd6f.3bc79"]]},{"id":"b241fd6f.3bc79","type":"function","z":"1bd2b382.4dd4f4","name":"update value","func":"\nvar deviceMonitor = flow.get(\"deviceMonitor\") || {};\nvar device = deviceMonitor[msg.topic];\nif(!device) {\n    deviceMonitor[msg.topic] = {\n        last_on: null,\n        last_off: null,\n    }\n    device = deviceMonitor[msg.topic]\n}\nswitch(msg.payload) {\n    case \"on\": \n        device.last_on = Date.now();\n        break;\n    case \"off\": \n        device.last_off = Date.now();\n        break;\n}\n\n//update flow context.\n//NOTE: This only sets the reference (doesnt re-create the object\n//      and is infact not even needed.  E.g.  if you comment the \n//      next line out, everything still works!\nflow.set(\"deviceMonitor\", deviceMonitor);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":646,"y":304,"wires":[[]]},{"id":"a9d4c512.088868","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.10","payload":"off","payloadType":"str","x":432,"y":352,"wires":[["b241fd6f.3bc79"]]},{"id":"aa35fac0.f475f8","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.11","payload":"on","payloadType":"str","x":432,"y":400,"wires":[["b241fd6f.3bc79"]]},{"id":"7f4af8f9.d842e8","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.11","payload":"off","payloadType":"str","x":432,"y":448,"wires":[["b241fd6f.3bc79"]]},{"id":"fbc0bb0c.1c9d98","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.99","payload":"on","payloadType":"str","x":432,"y":496,"wires":[["b241fd6f.3bc79"]]},{"id":"c176579b.3d9bd8","type":"inject","z":"1bd2b382.4dd4f4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"192.168.1.99","payload":"off","payloadType":"str","x":432,"y":544,"wires":[["b241fd6f.3bc79"]]}]
1 Like

Thanks, but I tried that earlier, but it just crashes node-RED.

That's a really helpful example, thanks Steve.
That will improve my code by a mile, as currently, I have to retrieve the values from context first, only to have to reconstruct the array with one changed value, and then write the whole lot back again.
Also, I won't have to replace the dots in the IP for dashes, and it will also cut down on the lines of code!!
:+1:

1 Like

Works for me on node-red 1.2.9

How would you add to the last_on if it is not an object or an array?
Never mind I was thinking you wanted to store more values under last_on, etc

1 Like

I'm using v1.3.5
It was intuitive to format it that way, but I tried it twice and it failed for me.
Regardless, I think I need to change the structure, and get rid of the arrays.
Thanks.

I blame Julian (@TotallyInformation ) it's all his fault :wink::wink::wink:

I am that virus! :mage:

Actually, I think we had a discussion previously on the forum about retrieving/saving context variables and the decision then is that you might was well get/set the whole object. Because JavaScript passes by reference it may actually be more efficient (at least for memory and file based variables). It is certainly easier to get your head around than messing with a string reference - well it is for my little brain anyway.

1 Like

How does this work???
Even with the flow.set command commented out (as per the comment), the device.last_on & device.last_off context values still get updated... despite there not being a command to do so!!
A wild guess is that it's due to javascript pass-by-reference, but I'm probably wrong :grimacing:

var deviceMonitor = flow.get("deviceMonitor") || {};
var device = deviceMonitor[msg.topic];
if(!device) {
    deviceMonitor[msg.topic] = {
        last_on: null,
        last_off: null,
    }
    device = deviceMonitor[msg.topic]
}
switch(msg.payload) {
    case "on": 
        device.last_on = Date.now();
        break;
    case "off": 
        device.last_off = Date.now();
        break;
}
//update flow context.
//NOTE: This only sets the reference (doesnt re-create the object
//      and is infact not even needed.  E.g.  if you comment the 
//      next line out, everything still works!
//flow.set("deviceMonitor", deviceMonitor);
return msg;

No, you are correct(ish) :wink:

The objects retrieved by these 2 lines...

var deviceMonitor = flow.get("deviceMonitor") || {};
var device = deviceMonitor[msg.topic];

are getting a reference to the stored object. (deviceMonitor is an object & device is a sub property object)

When you update the values of the property, you are modifying the actual object (by reference).

The reason to include flow.set("deviceMonitor", deviceMonitor); is to make things play nice when you have an alternative storage (e.g. file backed context)

1 Like

If you were to use file-based storage, I think you would get a slightly different result :slight_smile:

While JavaScripts pass by reference is super efficient, it is also a pain from time-to-time. And doubly confusing since core data types (string, number, etc) are treated differently to complex objects.

Let's face it, JavaScript is a mess! Oh for those good old days of COBOL :rofl: and APL :scream:

Here is a typical section of "help" regarding APL: "The reduce and scan operators expect a dyadic function on their left, forming a monadic composite function applied to the vector on its right." :mage:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.