fix(marstek): let a Venus D read outlast the battery's five-minute stall - #504
Conversation
A Venus D stops answering for about four seconds on a five-minute clock: 138 stalls over eleven hours, mean 4.05 s, longest 4.46 s, measured at the Modbus proxy between Home Assistant and the battery, so it is the device and not the link. The per-attempt timeout was three seconds, chosen so a pymodbus retry - which re-sends the same transaction id - would land inside such a stall and consume the late reply. Against a four second stall it does not: the attempt expires every single time, the retry duplicates the request, and the battery answers both. One of the two replies then arrives with nothing waiting for it and is discarded, either as "received pdu without a corresponding request" or as a transaction id mismatch against the request that followed. Twelve times an hour. Six seconds covers the stall with room over the longest one seen. The cost is that a battery that is genuinely gone gets longer before the attempt is abandoned - the outer safety net grows from 11 s to 20 s. No value was being lost either way, the retry saw to that (one failed key in eleven hours, unrelated). What goes is the wasted round trip and a log that reports a fault twelve times an hour for a battery that is merely four seconds late. v3 and vA keep their three seconds: same family, but the measurement is from a Venus D on EMS v150 and nowhere else.
|
On whether the same six seconds belong on a vA and a v3 — I would rather not claim that, and here is what I can and cannot say. What I measured is a Venus D on EMS v150, over eleven hours at the proxy. I have no A and no E v3 to point an analyser at, so for those the five-minute stall is a guess, however plausible the shared lineage makes it. What does not depend on the guess is the asymmetry of the change. A longer per-attempt timeout only ever does something when the device has not answered yet. On a battery that never stalls, nothing changes at all — same traffic, same latency, same values. The only price is paid when a battery is genuinely gone: the outer safety net in That price lands differently per model, which is why I would treat them separately rather than as one family:
Checking it takes half an hour and no equipment: set If those appear on a five-minute cadence, it is the same thing. Happy to widen the PR if an A or an E v3 owner reports back. |
fix(marstek): let a Venus D read outlast the battery's five-minute stall Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> # Conflicts: # CHANGELOG.md
Closes #503.
A Venus D stops answering for about four seconds every five minutes. Measured at the Modbus proxy between Home Assistant and the battery, over eleven hours:
Everything else answers in under 250 ms, mean 81 ms. There is nothing in between the two groups, so this is one behaviour of the device rather than a tail of the normal distribution. Since it is measured on the proxy's own socket to the battery, neither Home Assistant nor the client library is in the path.
Why three seconds does not survive it
The comment above
READ_TIMEOUT_Ssays why the v3 family was kept short:That reasoning holds for a stall the attempt can sit out. It does not hold for a four second one: the attempt expires in every single stall, the retry puts a duplicate request on the wire, and the battery answers both. The first answer then arrives with nothing waiting for it:
122 of them in eleven hours — one per stall.
The change
READ_TIMEOUT_S["vD"]3 → 6, and the comment rewritten to say what was measured.Six rather than five: the longest stall seen is 4.46 s, and a second request queued behind the stalled one waits a little longer still (4.53 s worst case at the proxy, out of 141.054 client requests). Five would have covered every case in the sample; six leaves room for the sample to be wrong.
The cost is that a battery that is genuinely gone gets longer before an attempt is abandoned: the outer safety net in
infra/modbus_client.pyistimeout * (retries + 1) + 2, so 11 s becomes 20 s. For a device whose observed failure mode is being four seconds late, that seems the right way round, but it is your call — the same relief is available at 5 s if you would rather keep the worst case tighter.v3 and vA are left at three seconds. Same family, but I have only a Venus D (EMS v150) to measure. If someone confirms the same five-minute stall on a v3 or a vA, it is a one-line follow-up.
What this does not change
No values were being lost. The retry fetched them; in eleven hours exactly one key failed outright, and not during a stall. What goes is the duplicate request and a log that reports a fault twelve times an hour for a battery that always came back.
Verification
hass-fixture tests the workflow runs separately.tests/test_marstek_driver.py: the Venus D attempt must outlast the measured stall (fails with the old value), and each version's timeout must reach the client it is built with.