Hi, I’d like to have the option to isolate my ESS from grid when grid is not required get or inject energy.
This would solve the marginal but constant grid consumption or injection a little above or under 0, which is always present when inverter is in “ON” mode. Of course, toogling on “ON” mode would be trigerred when DESS needs to get energy or inject energy from / to grid.
What do you think ?
(I couldn’t find a way to acheive this through Node-red, as I couldn’t find DESS intentions exposed)
That would be very nice. Ignoring ACin would be even better. (Opening the relays.)
Yes, why not… Could you explain the difference between ignoring AC in and inverter mode ?
Hi
I actually use Node Red to control the MP2 mode, and switches to “Inverter Only” when appropriate to stop any marginal but constant grid in/out (I am using ESS, but not DESS).
Node is the VEBus System Control one, selecting the Multiplus, then the Switch Position setting as “Measurement” field.
The difference between ACinIgnore & “inverter only” are a bit technical and I am not exactly the best person to explain (I understood they do not act on the same internal relays), but at the end, they achieve the same regarding the need to “disconnect” from grid.
However, a big difference is that “inverter only” can be controlled from Node Red, while “AC Ignore” needs to go through the less flexible VE Config way. So “inverter only” is a suitable way ![]()
Note: one interesting thing with inverter only is that even if “disconnected” from ACin, the Multiplus still probe the voltage on ACIn (which I use to monitor potential grid shutdown, to send me a warning). Unsure the ACInIgnore would allow to keep this voltage monitoring on ACIN.
Matt
Any chance you could share your flow somehow? I’m also in the process of putting together such an algorithm on node-RED, and I am currently struggling with A.I.-supported programming…
Hi
the flow that was in place at my last post time is for now on hold, since I’m giving a try to DESS.
Anyway, it was very specific to my needs, home & grid subscription type (French EDF Tempo service, which is a one of its king thing ;)).
I can however share with you what I have in place (only for 3 weeks now), since I’m testing a recently released DESS for my grid provider.
It is based on the DESS reactive strategy, which is a better trigger to swithc between “inverter only” and “on” mode (the past test I did, based on the ESS strategy (sole data available from node red available at that time), was not satisfactory).
I get DESS reactive mode from a MQTT node on localhost, reading N/[ID OF SYSTEM]/system/0/DynamicEss/ReactiveStrategy
What is read from this key is a value giving the reactive mode. The “dictionary” (value => full name) can be found in the Victron source code (class ReactiveStrategy(int, Enum) in dbus-systemcalc-py/delegates/dynamicess.py at master · victronenergy/dbus-systemcalc-py · GitHub), which is currently:
1: ‘SCHEDULED_SELFCONSUME’,
2: ‘SCHEDULED_CHARGE_ALLOW_GRID’,
3: ‘SCHEDULED_CHARGE_ENHANCED’,
4: ‘SELFCONSUME_ACCEPT_CHARGE’,
5: ‘IDLE_SCHEDULED_FEEDIN’,
6: ‘SCHEDULED_DISCHARGE’,
7: ‘SELFCONSUME_ACCEPT_DISCHARGE’,
8: ‘IDLE_MAINTAIN_SURPLUS’,
9: ‘IDLE_MAINTAIN_TARGETSOC’,
10: ‘SCHEDULED_CHARGE_SMOOTH_TRANSITION’,
11: ‘SCHEDULED_CHARGE_FEEDIN’,
12: ‘SCHEDULED_CHARGE_NO_GRID’,
13: ‘SCHEDULED_MINIMUM_DISCHARGE’,
14: ‘SELFCONSUME_NO_GRID’,
15: ‘IDLE_NO_OPPORTUNITY’,
16: ‘UNSCHEDULED_CHARGE_CATCHUP_TARGETSOC’,
17: ‘SELFCONSUME_INCREASED_DISCHARGE’,
18: ‘KEEP_BATTERY_CHARGED’,
19: ‘SCHEDULED_DISCHARGE_SMOOTH_TRANSITION’,
92: ‘DESS_DISABLED’,
93: ‘SELFCONSUME_UNEXPECTED_EXCEPTION’,
94: ‘SELFCONSUME_FAULTY_CHARGERATE’,
95: ‘UNKNOWN_OPERATING_MODE’,
96: ‘ESS_LOW_SOC’,
97: ‘SELFCONSUME_UNMAPPED_STATE’,
98: ‘SELFCONSUME_UNPREDICTED’,
99: ‘NO_WINDOW’
Basically, and from my analysis, the following reactive strategies of DESS can run on Inverter Only mode:
‘SCHEDULED_SELFCONSUME’,
‘SCHEDULED_CHARGE_ALLOW_GRID’,
‘SCHEDULED_CHARGE_ENHANCED’,
‘SELFCONSUME_ACCEPT_CHARGE’,
‘SELFCONSUME_NO_GRID’,
‘SELFCONSUME_ACCEPT_DISCHARGE’,
‘SELFCONSUME_INCREASED_DISCHARGE’,
‘IDLE_MAINTAIN_SURPLUS’,
‘IDLE_NO_OPPORTUNITY’,
‘SCHEDULED_MINIMUM_DISCHARGE’,
‘SCHEDULED_DISCHARGE’,
‘SCHEDULED_DISCHARGE_SMOOTH_TRANSITION’,
‘KEEP_BATTERY_CHARGED’
The following are “non decision” type of situation (ie: the current mode must be kept):
‘SCHEDULED_CHARGE_SMOOTH_TRANSITION’,
‘SCHEDULED_CHARGE_FEEDIN’,
‘DESS_DISABLED’,
‘NO_WINDOW’,
‘SELFCONSUME_UNEXPECTED_EXCEPTION’,
‘UNKNOWN_OPERATING_MODE’
From the two above, any other reactive modes seems needing ON mode.
Matt
I was under the impression that dESS required a grid connection all the time, so it would not be used at the same time with any “inverter only” - mode - friendly implementation. I could be mistaken. FWIW, I find dESS a lot less capable compared to what a code-savvy person would be able to set up in node-RED.
Anyway, I spent a solid 4 hours yesterday night with Grok3 and came up with my own strategy to disconnect my MPII from the grid. I am sharing my final setup:
My MP-II 48/5000 was until now set up as an ESS system, with a grid set-point of 100W to minimise energy outflow to the grid. However, completely disconnecting the grid makes much more sense, when you’re running off-grid most of the time.
My new node-RED algorithm switches the Inverter to “Inverter only” mode when SoC is over 20% and consumption has stayed under 3700W for 3 minutes. It will then switch to “ON” mode (connecting the AC-in) as soon as consumption goes over 3700W or if the battery SoC drops below 20%. It then follows the ESS assistant’s strategy as before. All consumption values, time delay and SoC are set as constants on the code and are easily editable.
I have two input nodes (a “Battery Monitor” node renamed to “Battery SoC” and a “VE.bus System” node set to output phase 1 renamed to “Output AC Power”). These nodes link to a function node, which contains the attached below javascript code. The function node’s output is a single msg.payload value that goes to the VE.bus Control node that switches the Inverter between “ON” and “Inverter only” modes.
So far, it seems to be working as intended.
function getTimestamp() {
return Math.floor(Date.now() / 1000); // Current time in seconds
}
// Configuration variables
const MAX_POWER_THRESHOLD = 3700; // Power threshold for switching to ON (W)
const LOW_POWER_THRESHOLD = 3700; // Power threshold for starting low power timer (W)
const MIN_SOC_THRESHOLD = 20; // Minimum battery SoC threshold (%)
const LOW_POWER_TIMER_SECONDS = 180; // Timer duration for low power condition (seconds)
// Initialize context variables
let lowPowerStartTime = context.get('lowPowerStartTime') || null;
let lastMode = context.get('lastMode') || 2; // Default to Inverter only (2) if no last mode
// Get input values
let soc = null;
let power = null;
// Check which input is provided
if (msg.topic === "Battery SoC") {
soc = msg.payload;
context.set('lastSoc', soc);
} else if (msg.topic === "Output AC Power") {
power = msg.payload;
context.set('lastPower', power);
}
// Retrieve the value from context if needed
soc = soc !== null ? soc : context.get('lastSoc');
power = power !== null ? power : context.get('lastPower');
// Only process if both values are available
if (soc !== null && soc !== undefined && power !== null && power !== undefined) {
let mode;
const currentTime = getTimestamp();
// Condition 1: If power > MAX_POWER_THRESHOLD or soc < MIN_SOC_THRESHOLD, set mode to ON (3)
if (power > MAX_POWER_THRESHOLD || soc < MIN_SOC_THRESHOLD) {
mode = 3; // ON
lowPowerStartTime = null; // Reset timer
} else {
// Check if power has been < LOW_POWER_THRESHOLD
if (power < LOW_POWER_THRESHOLD) {
if (lowPowerStartTime === null) {
// Start the timer and maintain current mode
lowPowerStartTime = currentTime;
mode = lastMode || 2; // Default to Inverter only
} else {
// Check if timer has elapsed
if (currentTime - lowPowerStartTime >= LOW_POWER_TIMER_SECONDS && soc > MIN_SOC_THRESHOLD) {
mode = 2; // Inverter only
} else {
mode = lastMode || 2; // Default to Inverter only
}
}
} else {
// Power is >= LOW_POWER_THRESHOLD, reset timer and set to ON
lowPowerStartTime = null;
mode = 3; // ON
}
}
// Only send message if mode has changed
if (mode !== lastMode) {
msg = { payload: mode }; // Create new message with only payload
context.set('lastMode', mode);
context.set('lowPowerStartTime', lowPowerStartTime);
return msg;
}
}
// Store the current state
context.set('lowPowerStartTime', lowPowerStartTime);
// Return null if no mode change or insufficient data
return null;
Above javascript was composed by Grok3, with a few back-and-forths by me. Since javascript is not my thing, please comment if you see anything awful. All in all, the code works.
Just to add another (even simpler) Node-RED approach I’ve been using in the field:
Instead of evaluating AC load/power, I only use ESS/BatteryLife state as the decision input, and switch the Multi/Quattro /Mode accordingly:
-
When ESS is in any state where it needs the grid (recharge/sustain/SoC below limit etc.) → set Mode = ON
-
When ESS is in self-consumption type states (grid not required) → set Mode = Inverter only
So the Multi basically runs like a normal ESS with BatteryLife, but as soon as grid energy isn’t needed, it goes into Inverter only and stays disconnected most of the time (AC-in voltage is still visible of course).
Why I do this:
-
I’ve deployed this on multiple installations where the grid is present but extremely unstable (voltage fluctuating heavily).
-
I don’t want that unstable grid voltage being coupled/forwarded to AC-out when it’s not required.
-
This gives a “best of both worlds”: ESS logic stays intact, but AC-in is only connected when the system truly needs it.
Important limitation:
-
In this setup, when AC-in is disconnected you also lose PowerAssist, so this is not a good strategy for an underpowered inverter that frequently relies on the grid to provide extra power headroom.
-
It assumes the inverter is sized to handle typical loads in inverter-only most of the time.
So far it’s been working very reliably, and it’s trivial to implement (just a mapping table from ESS state → /Mode), no timers, no thresholds, no power averaging.
[
{
"id": "947556659cabc1e0",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "victron-client-id",
"type": "victron-client",
"showValues": true,
"contextStore": true
},
{
"id": "e7076e068b2160f8",
"type": "victron-output-vebus",
"z": "947556659cabc1e0",
"service": "com.victronenergy.vebus/276",
"path": "/Mode",
"serviceObj": {
"service": "com.victronenergy.vebus/276",
"name": "Inversor"
},
"pathObj": {
"path": "/Mode",
"type": "enum",
"name": "Switch Position",
"enum": {
"1": "Charger Only",
"2": "Inverter Only",
"3": "On",
"4": "Off"
},
"mode": "both"
},
"name": "",
"onlyChanges": false,
"x": 530,
"y": 140,
"wires": []
},
{
"id": "f64d79ee0258e2ad",
"type": "victron-input-ess",
"z": "947556659cabc1e0",
"service": "com.victronenergy.settings",
"path": "/Settings/CGwacs/BatteryLife/State",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "Venus settings"
},
"pathObj": {
"path": "/Settings/CGwacs/BatteryLife/State",
"type": "enum",
"name": "ESS state",
"enum": {
"1": "BatteryLife enabled (GUI controlled)",
"2": "Optimized Mode /w BatteryLife: self consumption",
"3": "Optimized Mode /w BatteryLife: self consumption, SoC exceeds 85%",
"4": "Optimized Mode /w BatteryLife: self consumption, SoC at 100%",
"5": "Optimized Mode /w BatteryLife: SoC below dynamic SoC limit",
"6": "Optimized Mode /w BatteryLife: SoC has been below SoC limit for more than 24 hours. Charging the battery (5A)",
"7": "Optimized Mode /w BatteryLife: Inverter/Charger is in sustain mode",
"8": "Optimized Mode /w BatteryLife: recharging, SoC dropped by 5% or more below the minimum SoC",
"9": "'Keep batteries charged' mode is enabled",
"10": "Optimized mode w/o BatteryLife: self consumption, SoC at or above minimum SoC",
"11": "Optimized mode w/o BatteryLife: self consumption, SoC is below minimum SoC",
"12": "Optimized mode w/o BatteryLife: recharging, SoC dropped by 5% or more below minimum SoC"
}
},
"name": "ESS state",
"onlyChanges": true,
"x": 100,
"y": 140,
"wires": [
[
"7349969f35e05bbb"
]
]
},
{
"id": "7349969f35e05bbb",
"type": "change",
"z": "947556659cabc1e0",
"name": "State Switcher",
"rules": [
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "1",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "2",
"fromt": "num",
"to": "2",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "3",
"fromt": "num",
"to": "2",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "4",
"fromt": "num",
"to": "2",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "5",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "6",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "7",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "8",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "9",
"fromt": "num",
"to": "3",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "10",
"fromt": "num",
"to": "2",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "11",
"fromt": "num",
"to": "2",
"tot": "num"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "12",
"fromt": "num",
"to": "3",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 300,
"y": 140,
"wires": [
[
"e7076e068b2160f8"
]
]
}
]
My interest in this is purely theoretical. That was my understanding too. In my mind, speaking of inverter only mode in ESS is a contradiction. Likely possible but I don’t know if it’s officially supported.
The literature I have seen is confusing and often contradictory. An off-grid system shouldn’t use ESS (though some people do, even if this is not officially supported). An “off-grid” grid system can nevertheless be connected to the grid. I don’t think it’s helpful to define an “off-grid” system as one that can function independent of the grid. Many grid-tied systems also can do that. Some muddy the waters even more by introducing the category of”hybrid” systems.
I used a Node-Red flow with the grid as a generator (Inverter mode und on mode). I switch to on mode on low SOC or on hugh power usage and switch back when the condition no longer applies.
This works great for 2 years now.
