Skip to content

feat(saj): replace the untested stub with H2-Protocol battery telemetry - #111

Merged
frahlg merged 1 commit into
mainfrom
feat/saj-h2-battery
Sep 8, 2026
Merged

frahlg merged 1 commit into
mainfrom
feat/saj-h2-battery

Conversation

@frahlg

@frahlg frahlg commented Sep 8, 2026

Copy link
Copy Markdown
Member

SAJ 1.2.0 replaces the untested input-0x10xx stub with the H2-Protocol holding map used by evcc saj-h2 and 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:

  • reads identity from 0x8F00, live PV/battery/grid power from 0x40A5/0x40A6/0x40AD, per-phase meter from 0x4031, and BMS from 0xA000
  • emits battery only when BatNum and BatOnline report a pack
  • treats a missed live-power read as silence, not a zero-watt site
  • negates vendor battery power at the boundary (SAJ is discharge-positive)
  • leaves control off until a named H2/HS2 proves held zero

Validation:

  • make test-driver ID=saj and drivers/tests/test_saj.py
  • full pytest: 4047 passed, 1308 skipped
  • absent-register and refused-write probes clean for saj
  • harness: saj [PASS] SAJ=modbus ders=[battery, meter, pv]

No signed-channel release in this PR. Load drivers/lua/saj.lua as 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
saj 1.2.0 replaces the untested input-register (0x10xx) stub with the community H2-Protocol holding map (evcc saj-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 when 0xA000 reports a pack (BatNum and BatOnline both 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 bounded probe_read after three failures. Battery power/current are negated at the boundary (vendor discharge-positive). read_only stays true and driver_command refuses battery writes.

Catalog/docs move SAJ from stub to telemetry: devices.yaml notes, INTEGRATION_OVERVIEW.md, setup: [device_ui], upstream_docs, and index.yaml / manifest version + hash. New drivers/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.

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>
@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T14:15:03.034359Z 9fc2a4f PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@frahlg
frahlg merged commit f18ceef into main Sep 8, 2026
7 checks passed
@frahlg
frahlg deleted the feat/saj-h2-battery branch September 8, 2026 14:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread drivers/lua/saj.lua
Comment on lines +148 to +149
local raw = host.decode_u32_be(hi, lo)
return finite(raw * 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant