I’m using the Modbus interface for my Kostal Plenticore Plus 10 (BYD 10kWh).
So, using external battery management you have to send the commands as long the timeout (60s configured) is not met (similar to expire in OH).
What I did last night was:
tell the inverter to charge my battery from the grid
tell the inverter (later) to have a maximum discharge
First command was running for some time up until 6:00 sharp. openHAB then stopped sending send any commands regarding chargePower (or modbus #1034). But starting with 6:00 sharp, openHAB started sending dischargeLimit (or modbus #1040). The change was simultaneous, meaning there was a constant “external batterylimit”-flow. But what I discovered was, that while the dischargeLimit was sent, the inverter still followed the “old” chargePower command, which wasn’t sent anymore.
I paused the rule for a minute and started it again, and after the external Batterymanagement was completely abandonend, the inverter then did what was expected: only followed the maximus discharge limit.
So, my question is:
is there some kind of “delete”-command for a specific modbus register neccessary? My impression was, that if I don’t send commands within the time-out interval, that specific command would be discarded from the inverter anyways - even though another register within the external batterymanagement receives commands?
funny, that you asked this today!
I literally got an answer today to my ticket from November 2023 (!!) I opened at Kostal! They’re simply saying “We won’t support you”.
What I did months ago is to pause all communication for the timeout plus some seconds more, if there’s a change in commands. There seems to be no “delete”-register available, only thing possible is letting the timeout do its magic. For me this is a bug, but obviously not for Kostal.
could you post your rules? What I try to do is an active discharge into the grid, when there is solarforecast, that exceeds the “feed in” limits of our DSO.
Without you knowing what happens in the background it’s kinda pointless!
But I’ll try to strip it down a bit.
Basic understanding:
I have a bunch of situations, in which I want to address one of the five:
** MinSoc
** MaxSoC
** BatteryChargePower
** BatteryMaxCharge
** BatteryMaxDischarge
So I created a Group-Item for each of them, which is either the MIN-values or MAX-values of multiple items for each of the five depending on the situation.
So, e.g. one situation says: set MinSoC to 10% and another says MinSoC to 50%, then the group-Item will assume 50% (max-value). Other way round with the group-item BatteryMaxDischarge, this will then be set to the Min-value.
basically I have up to five group-items, which can assume an value. The child items of each of the five group-items get constantly updated via rules. If there’s no update, the expiration time for the group-items is 1min. After that they’re reset to UNDEF
With that in mind, I do have a rule, that runs every 20secs with my Kostal Timeout being 60secs. Basically the rule sends an update to the respective register every 20secs to keep it alive, if the gorup-item has an value.
So, as you also encountered, if you add a new command it works, but if you “remove” a command, the last known entry gets stuck in the Kostal, if there’s another command constantly updated within the timeout.
So what I do is simply count the group-items with values and write that into a global variable (or cache Map object) in my JS Scripting rules (should be similar with DSL?). On each run I compare the actual number of items with the cached one and if they differ I’ll pause the rule for 80seconds to be sure the timeout is met and then start again with updates every 20secs.
(sorry, posting my rule won’t help, as they’re tailored to my configuration and pretty messed up, I didn’t take time to restructure them as I still hoped to get a more straight forward solution.)
Hi Thomas,
thanks a Lot for your advice. I’m currently stuck in the function “discharge to grid”. I send the following two commands every 50 Seconds to my Plenticore Plus:
Sync of timeout
As you can see, the timeout set here is 60secs (Don’t know the default tbh).
if you are not “in-sync” with that timeout, that could be one reason.
“Discharge Limit” is Register 1040, which is only a “limit” (you tell the inverter it could only discharge until 2000W) and not the actual (dis-)charge power. for that you have to use Register 1034, which is the absolute DC Charge Power (Negative values will charge the battery, positive values will discharge the battery)
Thanks a lot for your hints. I managed to discharge the battery with setting register 1036 (Relative AC) to i.e. 35. My problem is, that I can not reset the discharge to that the Kostal Plenticore is taking over the settings. How do you manage this? Setting MaxSoc works, MinSoc is always reset to the default value of 5%.
tbh, I don’t use MinSoc or MaxSoC much. What I do is either tell a specific charge/discharge setpoint (1034), or a maxChargeLimit (1038) oder maxDischargeLimit (1040).
e.g. I use 1038 to limit charging on days of plenty of sun and e.g. no electric car charging (bc they’re already charged), so the battery doesn’t charge too fast.
or I use 1040 to limit discharging to preserve some Wh in the battery for a time of high costs from tibber.
I use explicit charging in cases of low tibber prices or e.g. a big price increase, which makes it cheaper to charge beforehand, if the battery is not yet in a high SoC.
=> presently mostly important for winter time.
but also in summer, if you would like to not power-charge your battery or even act “network-friendly” as in: don’t feed-in on midday.
Do you have 24/7 the rules in Openhab or how do you pass over the logic to the Kostal? If I set a value i.e.in 1036 and then I stop sending a value even for more than 30 Minutes, the last sent value is kept alive resulting in for example discharging the battery till 5% even if the rule stoped sending via modbus at 20%. The only solution is to physically switch off the Kostal…
the registers 1026 until 1044 are dependent on a “keep alive” signal. Thing is - as explained earlier - that if once a value is set, the “keep alive”-signal of a second/third value still holds the register at the last known state - even if said register is not in the “keep alive” signals anymore.
So, be sure (last paragraph of my February post) to pause any and all “keep alive” signals for the timeout you configured in the Kostal. After that, you can resume the external battery management again with all the current setpoints you’d like. That’s if you want to “remove” a command. Adding a command is without a problem. I contacted Kostal because of this (I’d describe it as a bug), but the support is only willing to answer questions from registered partners, which I as a enduser am not - and it costs a lot of money, too
If you initiate battery discharging via Modbus, you’ll notice that you can’t hand control back to the Kostal inverter afterwards.
In the Photovoltaikforum, user ch.eick pointed me in the right direction:
If you instead use the API call (see kostal-RESTAPI.py) and set the following fields:
{"value": "1", "id": "Battery:ComMonitor:Enable"} // enables external control
{"value": "60.0", "id": "Battery:ComMonitor:Time"} // after 60 seconds, Kostal regains control
{"value": "50", "id": "Battery:ExternControl:DcPowerRel"} // discharges at 50% of max DC capacity
These values must be resent every 60 seconds—until, for example, the State of Charge (SOC) reaches 20 %. Once the updates stop, Kostal automatically takes back control.
This approach never worked for me via Modbus alone—but it works reliably via the API.