BETA: Turn surplus solar into useful work with GX Opportunity Loads

Many thanks, everyone, for all your interest and engagement.

I’m happy to announce that GX Opportunity Loads was released last week as part of Venus OS v3.80. You can find the release slide deck here: public- Venus OS v3.80 release - Google Slides

As promised, I’d also like to share how to do custom integrations using Node-RED. This part is still waiting for its official release, but since this is a Beta topic, I’m sharing it here early.

Opportunity Loads uses S2, an open standard for energy flexibility, to talk to the loads it controls. We’ve built a Node-RED palette for it: node-red-contrib-s2 (node) - Node-RED

To install it, go to Manage palette → Install in Node-RED and search for node-red-contrib-s2. The install takes a moment. When it’s done, restart Node-RED by setting it to Disabled and then Enabled in the GX user interface.

Example: SG Ready heat pump on GX relay 2

To get you started, here’s a simple flow that lets Opportunity Loads switch a heat pump’s SG Ready input using the GX’s built-in relay 2. Here’s how it works:

  • The S2 node registers a new load on the GX. It then shows up in the Opportunity Loads priority list, just like an EVCS or a Shelly.
  • The load has two modes: Off (0 W) and On (500 W). Minimum on and off times of 10 minutes keep the heat pump from switching too often.
  • When Opportunity Loads sends an instruction, the flow switches relay 2. It reads the relay’s actual state back and confirms that to Opportunity Loads, so both always agree.
  • The flow reports power based on the active mode. This is an estimate, so if you have a meter on the heat pump, sending its real readings works better.

To use it, set relay 2’s function to Manual and change the 500 W timers to match your heat pump / SG Ready enabled device. The wiring to the heat pump should be done by a qualified installer, of course.

You can use the same pattern for almost anything Node-RED can control: receive an instruction, switch the device, confirm, report power.

[
    {
        "id": "acec6839059f8b07",
        "type": "tab",
        "label": "SG Ready – S2",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "762a91dbe4a48936",
        "type": "group",
        "z": "acec6839059f8b07",
        "name": "SG Ready with GX internal relay 2",
        "style": {
            "label": true
        },
        "nodes": [
            "6dea37d6a02773f2",
            "4ea15d6995d9e0be",
            "7686459219a91013",
            "7677764cc8fb661c",
            "fce25638b35b344d",
            "f7a3bd9cac11d565",
            "40ba6d6b8921e775",
            "65419a19d0140415",
            "2e741aa4f439eef1",
            "ead62b74bb516261",
            "790ac2fd7545b1b4",
            "a268fcd99c6133f2",
            "8c130686e84c8fa3",
            "3ed5a3a520dafc81",
            "18f1670ef6a1577d"
        ],
        "x": 34,
        "y": 59,
        "w": 812,
        "h": 322
    },
    {
        "id": "6dea37d6a02773f2",
        "type": "switch",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Switch by mode",
        "property": "payload.label",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "On",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Off",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 380,
        "y": 320,
        "wires": [
            [
                "4ea15d6995d9e0be"
            ],
            [
                "7686459219a91013"
            ]
        ]
    },
    {
        "id": "4ea15d6995d9e0be",
        "type": "change",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Turn on",
        "rules": [
            {
                "t": "set",
                "p": "powerValues",
                "pt": "flow",
                "to": "payload.power",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 300,
        "wires": [
            [
                "f7a3bd9cac11d565"
            ]
        ]
    },
    {
        "id": "7686459219a91013",
        "type": "change",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Turn off",
        "rules": [
            {
                "t": "set",
                "p": "powerValues",
                "pt": "flow",
                "to": "payload.power",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "0",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 580,
        "y": 340,
        "wires": [
            [
                "f7a3bd9cac11d565"
            ]
        ]
    },
    {
        "id": "7677764cc8fb661c",
        "type": "change",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Confirm On",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"label\":\"On\"}",
                "tot": "json"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "ModeConfirmation",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 550,
        "y": 160,
        "wires": [
            [
                "8c130686e84c8fa3"
            ]
        ]
    },
    {
        "id": "fce25638b35b344d",
        "type": "switch",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Switch by relay state",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "1",
                "vt": "num"
            },
            {
                "t": "eq",
                "v": "0",
                "vt": "num"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 320,
        "y": 180,
        "wires": [
            [
                "7677764cc8fb661c"
            ],
            [
                "65419a19d0140415"
            ]
        ]
    },
    {
        "id": "f7a3bd9cac11d565",
        "type": "victron-output-relay",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "outputs": 0,
        "service": "com.victronenergy.system",
        "path": "/Relay/1/State",
        "serviceObj": {
            "service": "com.victronenergy.system",
            "name": "Venus device"
        },
        "pathObj": {
            "path": "/Relay/1/State",
            "type": "enum",
            "name": "Venus relay 2 state",
            "enum": {
                "0": "Open",
                "1": "Closed"
            },
            "mode": "both"
        },
        "name": "GX relay 2",
        "onlyChanges": false,
        "roundValues": "no",
        "rateLimit": 0,
        "conditionalMode": false,
        "condition1Operator": ">",
        "condition1HysteresisEnabled": false,
        "condition1HysteresisThreshold": "",
        "condition2Enabled": false,
        "condition2Service": "",
        "condition2Path": "",
        "condition2Operator": ">",
        "logicOperator": "AND",
        "outputTrue": "true",
        "outputFalse": "false",
        "outputOnChange": false,
        "debounce": 2000,
        "x": 750,
        "y": 320,
        "wires": []
    },
    {
        "id": "40ba6d6b8921e775",
        "type": "victron-input-relay",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "outputs": 1,
        "service": "com.victronenergy.system",
        "path": "/Relay/1/State",
        "serviceObj": {
            "service": "com.victronenergy.system",
            "name": "Venus system",
            "communityTag": "relay"
        },
        "pathObj": {
            "path": "/Relay/1/State",
            "type": "enum",
            "name": "Venus relay 2 state",
            "enum": {
                "0": "Open",
                "1": "Closed"
            },
            "mode": "both"
        },
        "name": "GX relay 2",
        "onlyChanges": false,
        "roundValues": "no",
        "rateLimit": 0,
        "conditionalMode": false,
        "condition1HysteresisEnabled": false,
        "condition1HysteresisThreshold": "",
        "outputTrue": "true",
        "outputFalse": "false",
        "debounce": "2000",
        "x": 120,
        "y": 180,
        "wires": [
            [
                "18f1670ef6a1577d",
                "fce25638b35b344d"
            ]
        ]
    },
    {
        "id": "65419a19d0140415",
        "type": "change",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Confirm Off",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\"label\":\"Off\"}",
                "tot": "json"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "ModeConfirmation",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 550,
        "y": 200,
        "wires": [
            [
                "8c130686e84c8fa3"
            ]
        ]
    },
    {
        "id": "2e741aa4f439eef1",
        "type": "switch",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "On Instruction",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "ModeInstruction",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 200,
        "y": 320,
        "wires": [
            [
                "6dea37d6a02773f2"
            ]
        ]
    },
    {
        "id": "ead62b74bb516261",
        "type": "s2-resource",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "SG Ready (Node-RED)",
        "rmName": "SG Ready (Node-RED)",
        "resourceId": "ebaefcdf-5b45-4efb-abe7-73a9496f737d",
        "roles": "ENERGY_CONSUMER",
        "controlTypes": "NOT_CONTROLABLE,OPERATION_MODE_BASED_CONTROL",
        "providesPowerMeasurement": "3_PHASE_SYMMETRIC",
        "providesForecast": false,
        "manufacturer": "Custom (Node-RED)",
        "model": "",
        "serialNumber": "",
        "firmwareVersion": "",
        "instructionPollIntervalMs": 2000,
        "skipInstructionStatus": false,
        "transport": "dbus",
        "cem": "",
        "reconnectInterval": 5,
        "dbusConfig": "b84278a84a44e1f9",
        "debug": false,
        "controlType": "ombc",
        "systemDescription": "{\"operationModes\":[{\"id\":\"5c5b5d80-d1d0-476a-9108-a648ad24b66f\",\"diagnostic_label\":\"Off\",\"power_ranges\":[{\"commodity_quantity\":\"ELECTRIC.POWER.L1\",\"start_of_range\":0,\"end_of_range\":0},{\"commodity_quantity\":\"ELECTRIC.POWER.L2\",\"start_of_range\":0,\"end_of_range\":0},{\"commodity_quantity\":\"ELECTRIC.POWER.L3\",\"start_of_range\":0,\"end_of_range\":0}],\"abnormal_condition_only\":false},{\"id\":\"f65ce0ee-405e-4c91-96d4-4110f5496d7e\",\"diagnostic_label\":\"On\",\"power_ranges\":[{\"commodity_quantity\":\"ELECTRIC.POWER.L1\",\"start_of_range\":500,\"end_of_range\":500},{\"commodity_quantity\":\"ELECTRIC.POWER.L2\",\"start_of_range\":0,\"end_of_range\":0},{\"commodity_quantity\":\"ELECTRIC.POWER.L3\",\"start_of_range\":0,\"end_of_range\":0}],\"abnormal_condition_only\":false}],\"transitions\":[{\"id\":\"21fd3599-8a6e-40ed-b6fb-e7078fd0cf34\",\"from\":\"5c5b5d80-d1d0-476a-9108-a648ad24b66f\",\"to\":\"f65ce0ee-405e-4c91-96d4-4110f5496d7e\",\"start_timers\":[\"cdcd33d8-aaab-459e-8b36-abf75dafab08\"],\"blocking_timers\":[\"8ed031a6-777c-4f95-b4aa-2da871e43ece\"],\"abnormal_condition_only\":false},{\"id\":\"4619ebd3-bf65-4435-a343-db6d2558ee70\",\"from\":\"f65ce0ee-405e-4c91-96d4-4110f5496d7e\",\"to\":\"5c5b5d80-d1d0-476a-9108-a648ad24b66f\",\"start_timers\":[\"8ed031a6-777c-4f95-b4aa-2da871e43ece\"],\"blocking_timers\":[\"cdcd33d8-aaab-459e-8b36-abf75dafab08\"],\"abnormal_condition_only\":false}],\"timers\":[{\"id\":\"8ed031a6-777c-4f95-b4aa-2da871e43ece\",\"duration\":600000},{\"id\":\"cdcd33d8-aaab-459e-8b36-abf75dafab08\",\"duration\":600000}]}",
        "defaultOperationModeId": "",
        "autoConfirmInstructions": false,
        "outputs": 1,
        "x": 220,
        "y": 100,
        "wires": [
            [
                "790ac2fd7545b1b4"
            ]
        ]
    },
    {
        "id": "790ac2fd7545b1b4",
        "type": "link out",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "link out 10",
        "mode": "link",
        "links": [
            "a268fcd99c6133f2"
        ],
        "x": 375,
        "y": 100,
        "wires": []
    },
    {
        "id": "a268fcd99c6133f2",
        "type": "link in",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "link in 13",
        "links": [
            "790ac2fd7545b1b4"
        ],
        "x": 75,
        "y": 320,
        "wires": [
            [
                "2e741aa4f439eef1"
            ]
        ]
    },
    {
        "id": "8c130686e84c8fa3",
        "type": "link out",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "link out 20",
        "mode": "link",
        "links": [
            "3ed5a3a520dafc81"
        ],
        "x": 695,
        "y": 200,
        "wires": []
    },
    {
        "id": "3ed5a3a520dafc81",
        "type": "link in",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "link in 15",
        "links": [
            "8c130686e84c8fa3"
        ],
        "x": 75,
        "y": 100,
        "wires": [
            [
                "ead62b74bb516261"
            ]
        ]
    },
    {
        "id": "18f1670ef6a1577d",
        "type": "change",
        "z": "acec6839059f8b07",
        "g": "762a91dbe4a48936",
        "name": "Send power measurements from flow context",
        "rules": [
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "PowerMeasurement",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "{\t  \"values\": $exists($flowContext(\"powerValues\"))\t    ? $flowContext(\"powerValues\")\t    : 0\t}",
                "tot": "jsonata"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 240,
        "wires": [
            [
                "8c130686e84c8fa3"
            ]
        ]
    },
    {
        "id": "b84278a84a44e1f9",
        "type": "s2-dbus-config",
        "name": "SG Ready (Node-RED)",
        "connectionMode": "auto",
        "tcpAddress": "",
        "deviceType": "acload",
        "measurementType": "L1_L2_L3",
        "nrOfPhases": "1",
        "position": "1",
        "phaseSetting": 1,
        "autoCalculateEnergy": true
    },
    {
        "id": "6eefb9fdcd20119d",
        "type": "global-config",
        "env": [],
        "modules": {
            "@victronenergy/node-red-contrib-victron": "1.7.26",
            "node-red-contrib-s2": "0.9.5"
        }
    }
]

My colleague @dfaber also just opened a dedicated topic about GX Opportunity Loads and Node-RED with more details and as a place for discussions.

Now that Opportunity Loads has been released, I’ll mark this thread as solved. For questions, feedback or anything else you’d like to discuss, please open a new topic in the opportunity_loads category. That keeps things easier to find for everyone. Thanks again for all your help during the Beta!