question

m-richards avatar image
m-richards asked

Node Red - Enable Persistent Context Store

Hello,

**on edit: for some reason posting inline links are not working for me, so I posed the links as plain text**

I'm trying to enable persistent context stores on a Cerbo GX for Node Red, per this article.

https://nodered.org/docs/user-guide/context#:~:text=Node%2DRED%20provides%20a%20way,This%20is%20called%20'context'.

Apparently, there are a couple (few?) different settings.js files in the Cerbo and I was modifying the default one at the default NR path:

/usr/lib/node_modules/node-red

Modifying that file broke NR. I had to reload the regular then large firmware to get NR working again. Restoring my changes did not seem to work.

I then saw this article

https://community.victronenergy.com/questions/139691/where-did-my-node-red-flows-go-after-firmware-upda.html

which indicates that the correct path to settings.js should be:

/data/home/nodered/.node-red/

Modifying that file by un-commenting the contextStorage section as follows:

/** Context Storage
      * The following property can be used to enable context storage. The confi                                                                                                                                                             >
      * provided here will enable file-based context that flushes to disk every                                                                                                                                                             >
      * Refer to the documentation for further options: https://nodered.org/doc                                                                                                                                                             >
      */
     contextStorage: {
         default: {
             module:"localfilesystem"
         },
     },

worked and my context values survive a reboot.

I then tried to modify settings.js to have memory and file storage options (also per the above article) like so:

contextStorage: {
   default: "memoryOnly",
   memoryOnly: { module: 'memory' },
   file: { module: 'localfilesystem' }
}

When I do that, NR won't launch, throwing the error:

Error: remote installation does not seem to have a running Node Red service.

Any Idea why this won't work?

The reason for all of this is I have setpoints to run fans, battery heat, etc. that I can adjust via a NR dashboard. I want the last settings to be saved in the event of a reboot. I thought the second method would be nice so only the contexts that I explicitly need would be written to disk, but I suppose it's no big deal to write them all.

Is this the best way to do this in a Cerbo NR deployment? Any issues writing the files to the Cerbo file system? I understand that because I'm writing to the /data partition, the files should be safe from a firmware upgrade.


Thanks.

Node-RED
1 comment
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

andy156 avatar image andy156 commented ·

I am nervous of hitting the cerbo file system too much I so am writing state changes to an external SD card. I also have some code that copies my flows to the SD card each time I do a deploy in case the whole thing goes down

0 Likes 0 ·
4 Answers
Jeremy Albrecht avatar image
Jeremy Albrecht answered ·

I've been writing files direct to disk and then reading them back in for persistence. I found I have to specify the full path for it to work: "/data/home/nodered/<filename>".

I'd love to get persistence working though. I'm picking up my RV from storage today and I'll have it for the next week or so. I'll see if I can get it working and report back on my results.

2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

m-richards avatar image
m-richards answered ·

Hi Jeremy,

To clarify, un-commenting the default code in settings.js appears to work fine.

It's the optional code that allows using both volatile and non-volatile context that I can't get working:

contextStorage: {
   default: "memoryOnly",
   memoryOnly: { module: 'memory' },
   file: { module: 'localfilesystem' }
}

Here is a test flow that i have working...

[
    {
        "id": "13a3313d51d2619b",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "0ed30d6017aac1c8",
        "type": "ui_gauge",
        "z": "13a3313d51d2619b",
        "name": "",
        "group": "a6babdcd9a57392e",
        "order": 3,
        "width": 0,
        "height": 0,
        "gtype": "donut",
        "title": "Display Bay Temp",
        "label": "units",
        "format": "{
  {value}}°F",
        "min": 0,
        "max": "125",
        "colors": [
            "#00b500",
            "#e6e600",
            "#ca3838"
        ],
        "seg1": "80",
        "seg2": "95",
        "className": "",
        "x": 550,
        "y": 200,
        "wires": []
    },
    {
        "id": "15fdc9518cb95639",
        "type": "ui_led",
        "z": "13a3313d51d2619b",
        "order": 2,
        "group": "a6babdcd9a57392e",
        "width": 0,
        "height": 0,
        "label": "Bay Fan On",
        "labelPlacement": "left",
        "labelAlignment": "left",
        "colorForValue": [
            {
                "color": "#808080",
                "value": "false",
                "valueType": "bool"
            },
            {
                "color": "#008000",
                "value": "true",
                "valueType": "bool"
            }
        ],
        "allowColorForValueInMessage": false,
        "shape": "circle",
        "showGlow": true,
        "name": "Fan on LED",
        "x": 990,
        "y": 180,
        "wires": []
    },
    {
        "id": "00f93453e0c7e248",
        "type": "function",
        "z": "13a3313d51d2619b",
        "name": "Test of overtemp",
        "func": "\n\n\nif (msg.payload.Temperature_F >= msg.payload.Bay_Setpoint) {\n    msg.payload=true\n}\nelse\n{\n    msg.payload=false\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 730,
        "y": 280,
        "wires": [
            [
                "15fdc9518cb95639",
                "e272348354fb28b1",
                "b740c08d62ee0ac5",
                "4113b208ec63e66b"
            ]
        ]
    },
    {
        "id": "975834e01d5a8d88",
        "type": "ui_numeric",
        "z": "13a3313d51d2619b",
        "name": "",
        "label": "Bay Fan Setpoint",
        "tooltip": "",
        "group": "b5700349a85c198f",
        "order": 1,
        "width": 0,
        "height": 0,
        "wrap": false,
        "passthru": true,
        "topic": "Bay_Setpoint",
        "topicType": "str",
        "format": "{
  {value}} °F",
        "min": "70",
        "max": "110",
        "step": 1,
        "className": "",
        "x": 430,
        "y": 400,
        "wires": [
            [
                "9d490d3b561ff5dc",
                "d07b2bfb11e632da",
                "75157a75c8feb8a8"
            ]
        ]
    },
    {
        "id": "2dcf874dcc968513",
        "type": "debug",
        "z": "13a3313d51d2619b",
        "name": "after join",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 700,
        "y": 320,
        "wires": []
    },
    {
        "id": "9d490d3b561ff5dc",
        "type": "debug",
        "z": "13a3313d51d2619b",
        "name": "after setpoint",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 710,
        "y": 480,
        "wires": []
    },
    {
        "id": "d07b2bfb11e632da",
        "type": "join",
        "z": "13a3313d51d2619b",
        "name": "",
        "mode": "custom",
        "build": "object",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": true,
        "timeout": "",
        "count": "2",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "",
        "reduceFixup": "",
        "x": 510,
        "y": 280,
        "wires": [
            [
                "2dcf874dcc968513",
                "00f93453e0c7e248"
            ]
        ]
    },
    {
        "id": "e272348354fb28b1",
        "type": "debug",
        "z": "13a3313d51d2619b",
        "name": "LED",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 950,
        "y": 400,
        "wires": []
    },
    {
        "id": "f3dc72aeeb2fa96f",
        "type": "victron-input-temperature",
        "z": "13a3313d51d2619b",
        "service": "com.victronenergy.temperature.adc_builtin0_7",
        "path": "/Temperature",
        "serviceObj": {
            "service": "com.victronenergy.temperature.adc_builtin0_7",
            "name": "Bay",
            "paths": [
                {
                    "path": "/Offset",
                    "type": "float",
                    "name": "Temperature offset"
                },
                {
                    "path": "/ProductId",
                    "type": "float",
                    "name": "Product ID"
                },
                {
                    "path": "/Scale",
                    "type": "float",
                    "name": "Temperature scale factor"
                },
                {
                    "path": "/Status",
                    "type": "enum",
                    "name": "Sensor status",
                    "enum": {
                        "0": "Ok",
                        "1": "Disconnected",
                        "2": "Short circuited",
                        "3": "Reverse polarity",
                        "4": "Unknown"
                    }
                },
                {
                    "path": "/Temperature",
                    "type": "float",
                    "name": "Temperature (C)"
                },
                {
                    "path": "/TemperatureType",
                    "type": "enum",
                    "name": "Sensor type",
                    "enum": {
                        "0": "Battery",
                        "1": "Fridge",
                        "2": "Generic"
                    }
                }
            ]
        },
        "pathObj": {
            "path": "/Temperature",
            "type": "float",
            "name": "Temperature (C)"
        },
        "name": "Get Bay Temp C",
        "x": 140,
        "y": 280,
        "wires": [
            [
                "7d9a7d8587944d9e"
            ]
        ]
    },
    {
        "id": "9e39f3c72a11c896",
        "type": "inject",
        "z": "13a3313d51d2619b",
        "name": "Read Setpoint",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "Bay_Setpoint",
        "payload": "Setpoint.Bay",
        "payloadType": "flow",
        "x": 160,
        "y": 400,
        "wires": [
            [
                "975834e01d5a8d88",
                "c9a86c36bf9b3dba",
                "d07b2bfb11e632da"
            ]
        ]
    },
    {
        "id": "7d9a7d8587944d9e",
        "type": "function",
        "z": "13a3313d51d2619b",
        "name": "Convert to F",
        "func": "var tempc = msg.payload;\n    tempf = tempc * 9/5 + 32;\n        msg.payload = tempf;\n        msg.topic = \"Bay_Temp_F\"\n    return msg;\n    \n    \n   ",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 310,
        "y": 280,
        "wires": [
            [
                "0ed30d6017aac1c8",
                "d07b2bfb11e632da"
            ]
        ]
    },
    {
        "id": "75157a75c8feb8a8",
        "type": "change",
        "z": "13a3313d51d2619b",
        "name": "Save Setpoint",
        "rules": [
            {
                "t": "set",
                "p": "Setpoint.Bay",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 720,
        "y": 400,
        "wires": [
            []
        ]
    },
    {
        "id": "c9a86c36bf9b3dba",
        "type": "debug",
        "z": "13a3313d51d2619b",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 410,
        "y": 460,
        "wires": []
    },
    {
        "id": "b740c08d62ee0ac5",
        "type": "victron-output-relay",
        "z": "13a3313d51d2619b",
        "service": "com.victronenergy.system",
        "path": "/Relay/0/State",
        "serviceObj": {
            "service": "com.victronenergy.system",
            "name": "Venus device",
            "paths": [
                {
                    "path": "/Relay/0/State",
                    "type": "enum",
                    "name": "Venus relay 1 state",
                    "enum": {
                        "0": "Open",
                        "1": "Closed"
                    },
                    "writable": true,
                    "disabled": true
                },
                {
                    "path": "/Relay/1/State",
                    "type": "enum",
                    "name": "Venus relay 2 state",
                    "enum": {
                        "0": "Open",
                        "1": "Closed"
                    },
                    "writable": true
                }
            ]
        },
        "pathObj": {
            "path": "/Relay/0/State",
            "type": "enum",
            "name": "Venus relay 1 state",
            "enum": {
                "0": "Open",
                "1": "Closed"
            },
            "writable": true,
            "disabled": true
        },
        "initial": "",
        "name": "Bay Fan Relay",
        "x": 1000,
        "y": 120,
        "wires": []
    },
    {
        "id": "837f89f9e1a024a2",
        "type": "e-mail",
        "z": "13a3313d51d2619b",
        "server": "smtp.gmail.com",
        "port": "465",
        "secure": true,
        "tls": true,
        "name": "foo@gmail.com",
        "dname": "Send Email",
        "credentials": {},
        "x": 1330,
        "y": 340,
        "wires": []
    },
    {
        "id": "4113b208ec63e66b",
        "type": "rbe",
        "z": "13a3313d51d2619b",
        "name": "One Shot",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": false,
        "property": "payload",
        "topi": "topic",
        "x": 960,
        "y": 340,
        "wires": [
            [
                "c490038baa95d891",
                "f25f4910beacbc89"
            ]
        ]
    },
    {
        "id": "c490038baa95d891",
        "type": "function",
        "z": "13a3313d51d2619b",
        "name": "Assemble Email",
        "func": "var bool = msg.payload;\n\nif (bool == true) {\n  msg.payload = \"The camper fan has switched on\";\n  msg.topic = \"Camper Fan On\";\n} \n//else if (payload == \"something\") {\n//  msg.payload = \"foo\";\n//} \n\nelse if (bool == false) {\n  msg.payload = \"The camper fan has switched off\";\n  msg.topic = \"Camper Fan Off\";\n}\n\nelse {\n  msg.payload = \"Camper fan logic recieved unexpected input\";\n  msg.topic = \"Camper Fan Logic Fault\";\n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1140,
        "y": 340,
        "wires": [
            [
                "837f89f9e1a024a2"
            ]
        ]
    },
    {
        "id": "f25f4910beacbc89",
        "type": "debug",
        "z": "13a3313d51d2619b",
        "name": "Email Send",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1130,
        "y": 400,
        "wires": []
    },
    {
        "id": "2bcde45c529c07ac",
        "type": "comment",
        "z": "13a3313d51d2619b",
        "name": "",
        "info": "Pulls saved setpoint from file to restore value on bootup",
        "x": 140,
        "y": 440,
        "wires": []
    },
    {
        "id": "09de93657ed47527",
        "type": "comment",
        "z": "13a3313d51d2619b",
        "name": "",
        "info": "Saves setpoint to file to allow restore on bootup",
        "x": 700,
        "y": 440,
        "wires": []
    },
    {
        "id": "89fb2f6391b56114",
        "type": "inject",
        "z": "13a3313d51d2619b",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 950,
        "y": 280,
        "wires": [
            [
                "c490038baa95d891"
            ]
        ]
    },
    {
        "id": "a6babdcd9a57392e",
        "type": "ui_group",
        "name": "Data",
        "tab": "4984060331cd8198",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "b5700349a85c198f",
        "type": "ui_group",
        "name": "Controls",
        "tab": "4984060331cd8198",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false,
        "className": ""
    },
    {
        "id": "4984060331cd8198",
        "type": "ui_tab",
        "name": "Camper",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    }
]
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

houser avatar image
houser answered ·

Hello,
I want the text fields in the node red dashboard to be persistent across devices and users
and also survive reboots if possible.

I am planning to give the method in this thread a try on a few systems I maintain, as I hope it will do exactly this. Am I on the right path and will this do the trick?
This is on Venus GX and Cerbo GX. Is there anything else I should think about before proceeding, or is it pretty much a case of un-commenting the contextStorage section as described above, reboot and it should work? I assume I will need to rework some nodes to use this functionality as I go.

Grateful for any heads-up or warnings before I try this.

2 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

derrick thomas avatar image derrick thomas commented ·

If you already have Kevin's setup helper installed, I have a package that will make it really easy.


Add the following package to the package manager:

packagename:ContextStorage

username:drtinaz

branch:main

Minimum Venus OS v2.92

-1 Like -1 ·
Show more comments
derrick thomas avatar image
derrick thomas answered ·

Setup helper is a package manager for managing Venus os modifications such as guimods and others.

Setup helper is here https://github.com/kwindrem/SetupHelper

Blind install is the easiest method

Guimods is here https://github.com/kwindrem/GuiMods


3 comments
2 |3000

Up to 8 attachments (including images) can be used with a maximum of 190.8 MiB each and 286.6 MiB total.

houser avatar image houser commented ·

Ok. Will have a look for sure. Can you say what that "ContextStorage" package does for this? Many thanks.

0 Likes 0 ·
derrick thomas avatar image derrick thomas houser commented ·
ContextStorage package runs a patch file against the node red config file to enable context storage. A benefit of using the package is that it is automated and will survive a Venus os firmware update.
-1 Like -1 ·
houser avatar image houser derrick thomas commented ·

So it will basically uncomment:

      */
     contextStorage: {
         default: {
             module:"localfilesystem"
         },
     },

?

This one site I have in mind is offgrid at an island and I will visit in a few weeks.
I will do my research and if I feel confident I will apply this package. Many thanks.

0 Likes 0 ·