feat(saj): replace the untested stub with H2-Protocol battery telemetry - #111
Conversation
The previous driver read an untested input-0x10xx map, fabricated zeros when registers were missing, and always emitted a battery — including on AS2 string inverters with no pack. Switch to the H2 holding map used by evcc saj-h2 and the community Home Assistant integration: identity, live PV/battery/grid power, per-phase meter, and BMS-gated battery telemetry. Control stays off until a named H2/HS2 proves held zero. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_a35fea4f-e9b3-4e04-840b-295c0b7875bf) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9fc2a4fb78
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| local raw = host.decode_u32_be(hi, lo) | ||
| return finite(raw * 10) |
There was a problem hiding this comment.
Keep lifetime decoding out of 32-bit integer arithmetic
On the repository's LUA_32BITS=1 runtime, both operations here use integer arithmetic: once a counter exceeds 214,748,364 raw units (about 2.147 GWh), raw * 10 wraps, and at 0x80000000 the decoder itself becomes negative. Long-lived H2 installations can therefore emit negative or otherwise corrupted PV and battery lifetime totals. Compute the scaled value from the two 16-bit halves using floating-point arithmetic instead.
Useful? React with 👍 / 👎.
SAJ 1.2.0 replaces the untested input-0x10xx stub with the H2-Protocol holding map used by evcc
saj-h2and the community Home Assistant integration.The old driver fabricated zeros when registers were missing and always emitted a battery, including on AS2 string inverters with no pack. This version:
0x8F00, live PV/battery/grid power from0x40A5/0x40A6/0x40AD, per-phase meter from0x4031, and BMS from0xA000BatNumandBatOnlinereport a packValidation:
make test-driver ID=sajanddrivers/tests/test_saj.pysajsaj [PASS] SAJ=modbus ders=[battery, meter, pv]No signed-channel release in this PR. Load
drivers/lua/saj.luaas a local user-driver on an FTW box to try it before the pin moves.Note
Medium Risk
Large telemetry rewrite on an experimental, not-yet FTW-field-verified map changes what the planner sees (especially battery presence and power signs), though control remains disabled.
Overview
saj1.2.0 replaces the untested input-register (0x10xx) stub with the community H2-Protocol holding map (evccsaj-h2/ stanus74 HA integration), bumps catalog metadata, and adds focused tests.The driver now reads identity, live power, per-phase meter, strings, and BMS over Modbus holding (FC 0x03) instead of input registers. PV and grid come from
0x40A5/0x40AD; battery telemetry is emitted only when0xA000reports a pack (BatNumandBatOnlineboth non-zero), so AS2 / PV-only hybrids no longer publish a fake empty battery. A failed live-power read yields no PV/meter/battery emissions rather than zero-watt placeholders; optional blocks use boundedprobe_readafter three failures. Battery power/current are negated at the boundary (vendor discharge-positive).read_onlystays true anddriver_commandrefuses battery writes.Catalog/docs move SAJ from stub to telemetry:
devices.yamlnotes,INTEGRATION_OVERVIEW.md,setup: [device_ui],upstream_docs, andindex.yaml/ manifest version + hash. Newdrivers/tests/test_saj.py(plus harness BMS fixture) locks sign conventions, identity, silence-on-missing-power, and no-battery when the pack is absent.Reviewed by Cursor Bugbot for commit 9fc2a4f. Bugbot is set up for automated code reviews on this repo. Configure here.