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.