Skip to content

Match the real CT on zero-MAC probes and normalize CT_MAC - #728

Merged
tomquist merged 1 commit into
developfrom
zero-mac-ct-probe
Oct 14, 2026
Merged

tomquist merged 1 commit into
developfrom
zero-mac-ct-probe

Conversation

@tomquist

@tomquist tomquist commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #727 (merged).

Why

#727 (@kvanbiesen) fixed a Marstek Venus E Mini (VNSEM-0) failing to pair with the emulated CT when CT_MAC is set. An unpaired battery probes with CT MAC 000000000000, and the emulator used to ignore it. This follow-up brings that fix in line with what the real CT firmware does and closes a related pairing gap.

Verified against decompiled firmware (images from rweijnen/marstek-firmware-archive and sphings79/marstek-firmware-archiv):

  • CT side. Checked in CT002 (HME-4 v120/v124) and CT003 (HME-3 v116/v117/v118/v122):
  • Battery side. Checked in Venus E 3.0 (VNSE3-0 v151) and Venus E 2.0 (HMG-50 v156):
    • With no CT bound and none learned yet, the battery sends 000000000000. It accepts any reply and puts the reply's CT MAC into its next requests.
    • Once a CT MAC is bound, it strcmps the reply's CT MAC against it, so case and separators must match exactly.
    • There is no public VNSEM-0 image, so the Mini's own handshake is unverified.

Changes on top of #727:

  • Wildcard (both stacks). Only 000000000000 is a wildcard (WILDCARD_CT_MAC in Python and C++); an empty CT MAC is ignored again, as on a real CT.
  • CT_MAC normalization (Python). CT_MAC is normalized to lowercase hex with no separators, as the ESPHome validator already does. Before this, a CT_MAC like AA:BB:CC:DD:EE:FF could never pair. Cloud reporting uses the normalized MAC too.
  • Docs. The "CT MAC behavior" section in docs/ct002-ct003-protocol.md, the CT_MAC help in config.ini.example and docs/ct002.md, and the add-on option description are updated.
  • Tests:
    • tests/test_ct002_mac_policy.py covers the wildcard, empty-MAC and normalization cases.
    • test.host.yaml now binds a ct_mac. The new test_ct_mac_policy in test_host_e2e.py checks the ESPHome host binary over real UDP. I confirmed it fails with the C++ wildcard line removed.
    • test_ct_settings_wiring's string sentinel no longer contains a -, which the normalization would strip.

Checklist

  • Base branch is develop, not main
  • uv run ruff format . && uv run ruff check . && uv run mypy src/ && uv run pytest: ruff, ruff format and mypy are clean. Results:
  • Python ↔ ESPHome parity held for shared CT002 behaviour (WILDCARD_CT_MAC on both sides; ESPHome already normalized ct_mac)
  • web/ changes: none, so no bundle rebuild
  • User-visible change: one bullet under ## Next in CHANGELOG.md, covering Answer zero-MAC CT probes with the configured CT MAC (VNSMINI V301) #727 and this PR

🤖 Generated with Claude Code

https://claude.ai/code/session_01GdNTZmPUm4RsPv96EUvWYq

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Marstek Venus E Mini pairing with a configured CT MAC, including uppercase and colon-separated MAC addresses.
    • Batteries that are not yet paired can now connect using the all-zero MAC probe. Requests with an empty or unrelated MAC are not answered when a CT MAC is configured.
  • Documentation
    • Clarified CT MAC matching and pairing behavior in the configuration and protocol guides.

tomquist added a commit that referenced this pull request Oct 13, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdNTZmPUm4RsPv96EUvWYq
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b4c21110-d662-485b-bc47-48b039b051c1
📥 Commits

Reviewing files that changed from the base of the PR and between 4ebec27 and a51716c.

📒 Files selected for processing: 12
  • CHANGELOG.md
  • config.ini.example
  • docs/ct002-ct003-protocol.md
  • docs/ct002.md
  • esphome/components/ct002/ct002.cpp
  • ha_addon/translations/en.yaml
  • src/astrameter/ct002/ct002.py
  • src/astrameter/main.py
  • tests/components/ct002/test.host.yaml
  • tests/components/ct002/test_host_e2e.py
  • tests/test_ct002_mac_policy.py
  • tests/test_ct_settings_wiring.py

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

Walkthrough

CT002 now accepts unpaired-battery requests addressed to the all-zero MAC when a CT MAC is configured. AstraMeter normalizes configured MACs and returns the configured MAC in its reply. Cloud reporting now falls back to the running device’s CT MAC.

Changes

CT MAC pairing compatibility

Layer / File(s) Summary
AstraMeter MAC normalization and matching
src/astrameter/ct002/ct002.py, tests/test_ct002_mac_policy.py, tests/test_ct_settings_wiring.py
AstraMeter normalizes configured MACs by trimming whitespace, removing separators, and lowercasing. It accepts wildcard requests when a CT MAC is configured, and its tests cover empty requests and normalized reply MACs.
ESPHome matching and policy verification
esphome/components/ct002/ct002.cpp, tests/components/ct002/*, docs/ct002-ct003-protocol.md, docs/ct002.md, config.ini.example, ha_addon/translations/en.yaml, CHANGELOG.md
ESPHome CT002 accepts wildcard MAC requests and no longer automatically accepts empty request MACs. Host tests check which requests receive replies and the response MAC. Documentation and the changelog describe the pairing behavior and MAC rules.

Cloud report ID selection

Layer / File(s) Summary
Cloud report ID fallback
src/astrameter/main.py
Cloud reporting continues to prefer marstek_mac and now falls back to device.ct_mac instead of ct.ct_mac.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to a5171

No actionable issue is established for the CT MAC pairing or cloud-reporting changes. Merge after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 47.37% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 6 files. (6 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title is concise and accurately describes the two main changes: zero-MAC probe handling and CT_MAC normalization.
Description check Passed The description explains the user impact, implementation details, firmware findings, tests, documentation updates, and checklist status. It follows the required template and is complete.
Full details: Docstring Coverage

Explanation

Docstring coverage is 47.37% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 6 files. (6 skipped: 6 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tomquist tomquist changed the title Answer zero-MAC CT probes with the configured CT MAC Match the real CT on zero-MAC probes and normalize CT_MAC Oct 13, 2026
Follow-up to #727, checked against decompiled firmware.

The CT002/CT003 images (HME-4 v120/v124, HME-3 v116/v117/v118/v122)
answer CT MAC 000000000000 as a wildcard next to their own MAC and always
reply with their own type and MAC. An empty CT MAC field fails the CT's
sscanf and is dropped, so only 000000000000 is a wildcard here too, on
both stacks (WILDCARD_CT_MAC).

The battery side (VNSE3-0 v151, HMG-50 v156) compares the reply's CT MAC
to its bound one with strcmp, so Python now normalizes CT_MAC to
lowercase hex without separators, as the ESPHome config validator already
does; cloud reporting uses the normalized MAC too.

Adds Python tests, a host-binary e2e test for the C++ side (test.host.yaml
now binds a ct_mac), docs and a changelog entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdNTZmPUm4RsPv96EUvWYq
@tomquist
tomquist merged commit 5561b45 into develop Oct 14, 2026
77 checks passed
@tomquist
tomquist deleted the zero-mac-ct-probe branch October 14, 2026 04:06
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