Affected area
Battery communication or availability
Omnibattery version
release/v1.5.0 (89d3a96) with local commits, built as 1.5.0-blockread.1. The value below is unchanged since it was introduced.
Home Assistant version
2026.9.3 (HA OS 18.3)
Installation details
Marstek Venus D, EMS v150, seven packs, Modbus TCP through a modbus-proxy add-on. A second integration reads the same battery through the same proxy, which is what let me measure the device separately from the clients: the proxy logs every frame in both directions, so the times below are the battery's own, not Home Assistant's.
What happened?
A Venus D stops answering for about four seconds, on a five-minute clock. Measured at the proxy over eleven hours:
210.077 requests
138 stalls longer than 1.5 s
mean stall 4.05 s
longest 4.46 s
interval 297.9 s on average, very regular
everything else answers in under 250 ms (mean 81 ms)
There is nothing in between: a request is either answered in under a quarter second or it waits four seconds. It is not load — the same picture at 300 requests per minute and at a tenth of that — and it is not a specific register, the stall catches whichever request happens to be in flight.
READ_TIMEOUT_S gives a Venus D three seconds per attempt, so every one of those stalls expires an attempt. The comment above the map explains the intent: pymodbus resends the same transaction id, so a late reply should still match the retry and be consumed. Against a four second stall that does not happen. The attempt expires, the retry puts a duplicate request on the wire, and the battery answers both — the first reply arrives with nothing waiting for it:
ERROR: received pdu without a corresponding request, IGNORING
ERROR: request ask for transaction_id=9490 but got id=9489, Skipping.
122 of those in the eleven hours, one per stall, twelve an hour.
Steps to reproduce
- Run a Venus D (EMS v150) and log
pymodbus at debug.
- Leave it polling for half an hour.
- Every five minutes, one read attempt expires and a reply is discarded as unmatched or mismatched.
With a Modbus proxy in front, its debug log shows the same thing from the other side: the battery taking four seconds to answer, once every five minutes.
Expected behavior
A per-attempt timeout longer than the stall the battery is known to take, so the attempt simply waits it out. The retry machinery then has nothing to do and no reply is orphaned.
Relevant Home Assistant logs
00:22:15,859 proxy -> battery request sent
00:22:19,909 proxy <- battery answer, 4.20 s later
00:22:20 ERROR [pymodbus.logging] ERROR: request ask for transaction_id=9490 but got id=9489, Skipping.
And the whole distribution, so it is clear this is one phenomenon and not a tail:
2.0 - 2.5 s : 2
3.0 - 3.5 s : 15
3.5 - 4.0 s : 9
4.0 - 4.5 s : 68
above 4.5 s : 0
Additional context
No data is lost: the retry fetches the value, and in eleven hours exactly one key failed outright (unrelated to a stall). So this is not a correctness bug, it is a wasted round trip and a log that reports a fault twelve times an hour for a battery that is merely late.
The other integration reading the same battery had the same three second timeout and the same symptom, plus a connection rebuild on top. Raising it to five seconds there removed every timeout and every rebuild: zero in 6.6 hours across roughly 80 stalls, on the same battery, same proxy, same load.
I have not seen this on a v3 or a vA and would not change those without someone measuring one.
PR follows.
Confirmations
Affected area
Battery communication or availability
Omnibattery version
release/v1.5.0(89d3a96) with local commits, built as 1.5.0-blockread.1. The value below is unchanged since it was introduced.Home Assistant version
2026.9.3 (HA OS 18.3)
Installation details
Marstek Venus D, EMS v150, seven packs, Modbus TCP through a modbus-proxy add-on. A second integration reads the same battery through the same proxy, which is what let me measure the device separately from the clients: the proxy logs every frame in both directions, so the times below are the battery's own, not Home Assistant's.
What happened?
A Venus D stops answering for about four seconds, on a five-minute clock. Measured at the proxy over eleven hours:
There is nothing in between: a request is either answered in under a quarter second or it waits four seconds. It is not load — the same picture at 300 requests per minute and at a tenth of that — and it is not a specific register, the stall catches whichever request happens to be in flight.
READ_TIMEOUT_Sgives a Venus D three seconds per attempt, so every one of those stalls expires an attempt. The comment above the map explains the intent: pymodbus resends the same transaction id, so a late reply should still match the retry and be consumed. Against a four second stall that does not happen. The attempt expires, the retry puts a duplicate request on the wire, and the battery answers both — the first reply arrives with nothing waiting for it:122 of those in the eleven hours, one per stall, twelve an hour.
Steps to reproduce
pymodbusat debug.With a Modbus proxy in front, its debug log shows the same thing from the other side: the battery taking four seconds to answer, once every five minutes.
Expected behavior
A per-attempt timeout longer than the stall the battery is known to take, so the attempt simply waits it out. The retry machinery then has nothing to do and no reply is orphaned.
Relevant Home Assistant logs
And the whole distribution, so it is clear this is one phenomenon and not a tail:
Additional context
No data is lost: the retry fetches the value, and in eleven hours exactly one key failed outright (unrelated to a stall). So this is not a correctness bug, it is a wasted round trip and a log that reports a fault twelve times an hour for a battery that is merely late.
The other integration reading the same battery had the same three second timeout and the same symptom, plus a connection rebuild on top. Raising it to five seconds there removed every timeout and every rebuild: zero in 6.6 hours across roughly 80 stalls, on the same battery, same proxy, same load.
I have not seen this on a v3 or a vA and would not change those without someone measuring one.
PR follows.
Confirmations