Hi Tomquist,
What's the issue?
When only DC-only batteries (B2500 / HMJ-2) are present and the smart meter briefly reports a negative value (grid surplus), all batteries are immediately steered to 0 W via steer_to_zero. This causes a full output collapse even when the surplus is tiny and transient, and even though no AC-chargeable battery is present that would need to be protected from back-feeding.
This occurs in two distinct scenarios (see steer_to_zero_szenario-1.png and steer_to_zero_szenario-2.png):
Scenario 1 — sudden load drop (steer_to_zero_szenario-1.png): A consumer switches off abruptly (e.g. a washing machine, dishwasher, or EV charger finishing a cycle). The batteries cannot react instantly, so for a brief moment they feed back into the grid. The screenshot shows stable operation at ~1.8 kW, followed by a complete output collapse to near 0 W at the right edge when a large load drops off.
Scenario 2 — ramp-up overshoot (steer_to_zero_szenario-2.png): The batteries are ramping up to meet a load. Due to control latency and the inherent lag of the inverters, they briefly overshoot and push slightly more power than currently consumed. The screenshot shows the repeated blue spikes (grid feedin) caused by batteries trying to meet the load, but overshoot and collapsing to 0 W and having to ramp up again from scratch.
In both cases the response is disproportionate: a −15 W transient causes a ~760 W output collapse (two batteries at ~380 W each), which then requires a full ramp-up recovery cycle.
Confirmed workaround: patching in_charge_territory = False directly in balancer.py inside the running container eliminates the issue completely. See pre-patch-behaviour.png vs post-patch-behaviour.png for a direct before/after comparison: the constant grid-draw spikes caused by repeated 0 W collapses disappear entirely after the patch, and the batteries regulate smoothly through transients.
Your setup
- Installation method: Docker Compose (latest
develop image)
- Storage system: 2× B2500 (device_type: HMJ-2)
- Power meter source: CT002 via ioBroker
Configuration
[GENERAL]
DEVICE_TYPE = ct002
SKIP_POWERMETER_TEST = False
THROTTLE_INTERVAL = 1
[IOBROKER]
IP = 192.168.178.20
PORT = 8087
POWER_CALCULATE = True
POWER_INPUT_ALIAS = alias.0.power_in
POWER_OUTPUT_ALIAS = alias.0.power_out
[CT002]
UDP_PORT = 12345
WIFI_RSSI = -50
CONSUMER_TTL = 120
DEBUG_STATUS = True
ACTIVE_CONTROL = True
SMOOTH_TARGET_ALPHA = 0.95
FAIR_DISTRIBUTION = True
BALANCE_GAIN = 0.3
Error messages or logs
# Both batteries actively discharging ~380-395 W, positive consumption
2026-05-14 19:14:27 INFO:astrameter:CT002 status: consumer 18cedf984f1b | meter 48W | phases A:0W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:382 B:0 C:0 | consumers 18cedf98@A:0 18cedf98@A:382
2026-05-14 19:14:28 INFO:astrameter:CT002 status: consumer 18cedf983ee3 | meter 34W | phases A:83W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:395 B:0 C:0 | consumers 18cedf98@A:13 18cedf98@A:382
2026-05-14 19:14:29 INFO:astrameter:CT002 status: consumer 18cedf984f1b | meter 20W | phases A:18W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:395 B:0 C:0 | consumers 18cedf98@A:13 18cedf98@A:382
# Load drops, meter briefly goes negative (-15 W) → both batteries immediately commanded to 0 W
2026-05-14 19:14:30 INFO:astrameter:CT002: 15 W surplus but no AC-chargeable battery reporting — holding all at 0 W. Reporting device_types: ['HMJ-2']
2026-05-14 19:14:30 INFO:astrameter:CT002 status: consumer 18cedf983ee3 | meter -15W | phases A:-59W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:441 B:0 C:0 | consumers 18cedf98@A:59 18cedf98@A:382
2026-05-14 19:14:30 INFO:astrameter:CT002 status: consumer 18cedf984f1b | meter -15W | phases A:-382W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:441 B:0 C:0 | consumers 18cedf98@A:59 18cedf98@A:382
2026-05-14 19:14:31 INFO:astrameter:CT002 status: consumer 18cedf983ee3 | meter -15W | phases A:-40W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:40 B:0 C:0 | consumers 18cedf98@A:40 18cedf98@A:0
2026-05-14 19:14:31 INFO:astrameter:CT002 status: consumer 18cedf984f1b | meter -15W | phases A:0W B:0W C:0W | chrg A:0 B:0 C:0 | dchrg A:40 B:0 C:0 | consumers 18cedf98@A:40 18cedf98@A:0
Additional info
Root cause — in balancer.py, _compute_auto_target:
in_charge_territory = grid_total < 0 or (grid_total == 0 and ac_charging)
charge_blind = (
{cid for cid, r in reports.items() if not _is_ac_chargeable(r.get("device_type", ""))}
if in_charge_territory
else set()
)
in_charge_territory becomes True on any negative grid reading, regardless of whether an AC-chargeable battery is present. The entire motivation for this logic (preventing B2500s from interfering with a Venus charging from surplus) does not apply when no Venus is in the pool.
Proposed fix — only activate charge_blind when at least one AC-chargeable battery is actually reporting:
any_ac_chargeable = any(
_is_ac_chargeable(r.get("device_type", ""))
for r in reports.values()
)
in_charge_territory = any_ac_chargeable and (
grid_total < 0 or (grid_total == 0 and ac_charging)
)
This preserves existing behaviour for mixed setups (B2500 + Venus) while making pure DC-only setups immune to the false trigger.
Best regards and thank you for your nice work on all of your Marstek/B2500 Projects
Philipp
Hi Tomquist,
What's the issue?
When only DC-only batteries (B2500 / HMJ-2) are present and the smart meter briefly reports a negative value (grid surplus), all batteries are immediately steered to 0 W via
steer_to_zero. This causes a full output collapse even when the surplus is tiny and transient, and even though no AC-chargeable battery is present that would need to be protected from back-feeding.This occurs in two distinct scenarios (see
steer_to_zero_szenario-1.pngandsteer_to_zero_szenario-2.png):Scenario 1 — sudden load drop (
steer_to_zero_szenario-1.png): A consumer switches off abruptly (e.g. a washing machine, dishwasher, or EV charger finishing a cycle). The batteries cannot react instantly, so for a brief moment they feed back into the grid. The screenshot shows stable operation at ~1.8 kW, followed by a complete output collapse to near 0 W at the right edge when a large load drops off.Scenario 2 — ramp-up overshoot (
steer_to_zero_szenario-2.png): The batteries are ramping up to meet a load. Due to control latency and the inherent lag of the inverters, they briefly overshoot and push slightly more power than currently consumed. The screenshot shows the repeated blue spikes (grid feedin) caused by batteries trying to meet the load, but overshoot and collapsing to 0 W and having to ramp up again from scratch.In both cases the response is disproportionate: a −15 W transient causes a ~760 W output collapse (two batteries at ~380 W each), which then requires a full ramp-up recovery cycle.
Your setup
developimage)Configuration
Error messages or logs
Additional info
Root cause — in
balancer.py,_compute_auto_target:in_charge_territorybecomesTrueon any negative grid reading, regardless of whether an AC-chargeable battery is present. The entire motivation for this logic (preventing B2500s from interfering with a Venus charging from surplus) does not apply when no Venus is in the pool.Proposed fix — only activate
charge_blindwhen at least one AC-chargeable battery is actually reporting:This preserves existing behaviour for mixed setups (B2500 + Venus) while making pure DC-only setups immune to the false trigger.
Best regards and thank you for your nice work on all of your Marstek/B2500 Projects
Philipp