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 ·
houser avatar image houser derrick thomas commented ·

Many thanks @derrick thomas

I do not know what "Kevin's setup helper" is. Is it this?
Another specific Victron package manager that installs from the terminal?
Are you saying this will help with handling context storage? If you have the time?
I will research it regardless, as it sounds like something I should know about..


0 Likes 0 ·
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 ·