I live aboard and wanted an easy way to flip my lithium bank between 80% for
daily use and 100% before a trip, without going into the settings every time.
The flow sets DVCC max charge voltage to 28.0 for the 80% mode and 29.0 for
100%. Current stays at 100A either way. I can trigger it from a virtual switch
on the Cerbo screen, an inject node in Node-RED, or an HTTP post from a phone
shortcut.
There’s a second virtual switch for shore power. It just puts the Multi into
Inverter Only so the MPPTs are the only thing charging. It’s manual, nothing
actually reads the shore state.
Setup:
MultiPlus-II 24/3000/70-50 120V, firmware v559
SmartSolar MPPT v1.74
Cerbo GX, Venus OS v3.80~42 (Large)
4x Torqeedo Power 24-3500, about 15kWh
node-red-contrib-victron 1.7.22
Flow below. My Multi shows up as vebus/276 so you’ll need to pick your own
device in that node after you import it.
One thing I haven’t worked out: DVCC max charge current is system wide, so I
can’t limit what the Multi pulls from shore without throttling the MPPTs too.
Inverter Only is my workaround. Is there a better way?
[
{
"id": "charge_mode_tab",
"type": "tab",
"label": "Charge Mode",
"disabled": false,
"info": ""
},
{
"id": "set_mode_fn",
"type": "function",
"z": "charge_mode_tab",
"name": "Resolve preset → DVCC values",
"func": "const mode = (msg.payload && msg.payload.mode ? msg.payload.mode : msg.payload || (msg.req && msg.req.params && msg.req.params.name) || '').toString().toLowerCase().trim();\n\nconst presets = {\n longevity: { dvccV: 28.00, dvccA: 100, label: 'Longevity (80%)' },\n full: { dvccV: 29.00, dvccA: 100, label: 'Full (100%)' }\n};\n\nconst p = presets[mode];\nif (!p) {\n node.warn('Unknown mode: ' + mode);\n node.status({ fill: 'red', shape: 'ring', text: 'unknown: ' + mode });\n return [null, null, null, { statusCode: 400, payload: 'unknown mode' }];\n}\n\nflow.set('chargeMode', p.label);\nnode.status({ fill: 'green', shape: 'dot', text: p.label });\n\nreturn [\n { payload: p.dvccV },\n { payload: p.dvccA },\n { payload: p.label },\n { payload: { ok: true, mode: p.label } }\n];",
"outputs": 4,
"noerr": 0,
"libs": [],
"x": 770,
"y": 380,
"wires": [
[
"out_cerbo_dvcc_max_voltage"
],
[
"out_cerbo_dvcc_max_current"
],
[
"ui_status_label"
],
[
"http_response"
]
]
},
{
"id": "set_shore_fn",
"type": "function",
"z": "charge_mode_tab",
"name": "Shore switch → MultiPlus mode",
"func": "const raw = msg.payload;\nconst shoreOn = (raw === 1 || raw === '1' || raw === true || raw === 'on' || raw === 'On');\nconst multiMode = shoreOn ? 3 : 2;\nnode.status({ fill: 'green', shape: 'dot', text: shoreOn ? 'Multi: On' : 'Multi: Inverter Only' });\nreturn { payload: multiMode };",
"outputs": 1,
"noerr": 0,
"libs": [],
"x": 780,
"y": 220,
"wires": [
[
"out_multiplus_switch_mode"
]
]
},
{
"id": "handle_battery_switch",
"type": "function",
"z": "charge_mode_tab",
"name": "Battery switch → mode string",
"func": "const state = msg.payload;\nlet mode;\nif (state === 1 || state === '1' || state === true) {\n mode = 'longevity';\n} else {\n mode = 'full';\n}\nnode.status({ fill: 'blue', shape: 'dot', text: 'switch ' + (state ? 'ON' : 'OFF') + ' → ' + mode });\nreturn { payload: mode };",
"outputs": 1,
"noerr": 0,
"libs": [],
"x": 780,
"y": 100,
"wires": [
[
"set_mode_fn"
]
]
},
{
"id": "trigger_longevity",
"type": "inject",
"z": "charge_mode_tab",
"name": "▶ Trigger: Longevity (80%) — daily",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "longevity",
"payloadType": "str",
"x": 410,
"y": 400,
"wires": [
[
"set_mode_fn"
]
]
},
{
"id": "trigger_full",
"type": "inject",
"z": "charge_mode_tab",
"name": "▶ Trigger: Full (100%) — pre-trip",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "full",
"payloadType": "str",
"x": 410,
"y": 460,
"wires": [
[
"set_mode_fn"
]
]
},
{
"id": "http_endpoint",
"type": "http in",
"z": "charge_mode_tab",
"name": "HTTP POST /charge-mode/:name",
"url": "/charge-mode/:name",
"method": "post",
"x": 430,
"y": 740,
"wires": [
[
"set_mode_fn"
]
]
},
{
"id": "http_response",
"type": "http response",
"z": "charge_mode_tab",
"name": "Reply 200 OK",
"statusCode": "",
"headers": {},
"x": 1060,
"y": 740,
"wires": []
},
{
"id": "out_cerbo_dvcc_max_voltage",
"type": "victron-output-custom",
"z": "charge_mode_tab",
"service": "com.victronenergy.settings",
"path": "/Settings/SystemSetup/MaxChargeVoltage",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "com.victronenergy.settings"
},
"pathObj": {
"path": "/Settings/SystemSetup/MaxChargeVoltage",
"name": "/Settings/SystemSetup/MaxChargeVoltage",
"type": "number",
"value": 28
},
"name": "Cerbo GX • DVCC • Max Charge Voltage (V)",
"onlyChanges": false,
"conditionalMode": false,
"outputTrue": "",
"outputFalse": "",
"debounce": "",
"x": 1110,
"y": 340,
"wires": []
},
{
"id": "out_cerbo_dvcc_max_current",
"type": "victron-output-custom",
"z": "charge_mode_tab",
"service": "com.victronenergy.settings",
"path": "/Settings/SystemSetup/MaxChargeCurrent",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "com.victronenergy.settings"
},
"pathObj": {
"path": "/Settings/SystemSetup/MaxChargeCurrent",
"name": "/Settings/SystemSetup/MaxChargeCurrent",
"type": "number",
"value": 100
},
"name": "Cerbo GX • DVCC • Max Charge Current (A)",
"onlyChanges": false,
"conditionalMode": false,
"outputTrue": "",
"outputFalse": "",
"debounce": "",
"x": 1110,
"y": 400,
"wires": []
},
{
"id": "out_multiplus_switch_mode",
"type": "victron-output-vebus",
"z": "charge_mode_tab",
"service": "com.victronenergy.vebus/276",
"path": "/Mode",
"serviceObj": {
"service": "com.victronenergy.vebus/276",
"name": "MultiPlus-II 24/3000/70-50 120V",
"communityTag": "vebus"
},
"pathObj": {
"path": "/Mode",
"type": "enum",
"name": "Switch Position",
"enum": {
"1": "Charger Only",
"2": "Inverter Only",
"3": "On",
"4": "Off"
},
"mode": "both"
},
"name": "MultiPlus-II 24/3000 • Switch Mode",
"onlyChanges": false,
"conditionalMode": false,
"outputTrue": "",
"outputFalse": "",
"debounce": "",
"x": 1180,
"y": 220,
"wires": []
},
{
"id": "ui_status_label",
"type": "debug",
"z": "charge_mode_tab",
"name": "Mode Label (status)",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 1040,
"y": 560,
"wires": []
},
{
"id": "virtual_switch_charge_mode",
"type": "victron-virtual-switch",
"z": "charge_mode_tab",
"name": "Virtual: Battery Mode",
"outputs": 2,
"switch_1_type": 1,
"switch_1_min": 0,
"switch_1_max": "",
"switch_1_initial": 1,
"switch_1_label": "Battery Mode: Full=OFF / Longevity=ON",
"switch_1_unit": "",
"switch_1_step": 1,
"switch_1_customname": "Battery Mode: Full=OFF / Longevity=ON",
"switch_1_group": "Charge Profile (Full=100%, Longevity=80%)",
"switch_1_include_measurement": false,
"switch_1_rgb_color_wheel": false,
"switch_1_cct_wheel": false,
"switch_1_rgb_white_dimmer": false,
"x": 450,
"y": 160,
"wires": [
[],
[
"handle_battery_switch"
]
]
},
{
"id": "virtual_switch_shore_power",
"type": "victron-virtual-switch",
"z": "charge_mode_tab",
"name": "Virtual: Shore Power",
"outputs": 2,
"switch_1_type": 1,
"switch_1_min": 0,
"switch_1_max": "",
"switch_1_initial": 1,
"switch_1_label": "Shore Power",
"switch_1_unit": "",
"switch_1_step": 1,
"switch_1_customname": "Shore Power",
"switch_1_group": "Charge Profile (Full=100%, Longevity=80%)",
"switch_1_include_measurement": false,
"switch_1_rgb_color_wheel": false,
"switch_1_cct_wheel": false,
"switch_1_rgb_white_dimmer": false,
"x": 450,
"y": 280,
"wires": [
[],
[
"set_shore_fn"
]
]
},
{
"id": "3bf96643c83c4253",
"type": "global-config",
"env": [],
"modules": {
"@victronenergy/node-red-contrib-victron": "1.7.22"
}
}
]