0.8.7
Changes Made by @pvandenh
Protocol Discovery
-
Reverse-engineered the 3-phase
Field | Phase 1 | Phase 2 | Phase 3 | Sum ÷100 | Z-Box -- | -- | -- | -- | -- | -- solar_power | 0x006f | 0x0070 | 0x006f | 3.34 kW | 3.31 kW ✅ ev_power | 0x0000 | 0x0000 | 0x0000 | 0.00 kW | 0.00 kW ✅ house_power | 0x0002 | 0x0004 | 0x002a | 0.48 kW | 0.46 kW ✅ grid_power | 0xff93 | 0xff94 | 0xffbb | −2.86 kW | −2.85 kW ✅SEND_DLBpacket structure — The 3-phase charger sends a 33-byte response (msg_len=0x21) versus the 1-phase charger's 17-byte response (msg_len=0x11). The extra 16 bytes are exactly 8 additional 16-bit fields: each of the four power values (solar, EV, house, grid) is split into three per-phase readings rather than a single total. Sensor totals are the sum of all three phases. Confirmed from BCP-AT1N-L hardware capture vs Z-Box ground truth:
New Message Definition
SEND_DLB_3Padded toSERVER_MESSAGEinconst.py— defines all 12 per-phase slice positions (solar_phase1–3,ev_phase1–3,house_phase1–3,grid_phase1–3) with full documentation of the confirmed byte layout and hardware evidence.
Routing Fix
get_message_type()inconversions.py—msg_int == 33withmessage_type == '7B'now routes toSERVER_MESSAGE.SEND_DLB_3P. Previously this path fell into the genericSEND_DLBbranch. The 1-phase path (msg_int == 17) is unchanged.
Decode Logic
read_message()incommunication.py— newSEND_DLB_3Pdecode block sums the three phases for each field to produce thesolar_power,ev_power,house_power, andgrid_powerkeys the coordinator and sensor entities already expect — no changes required incoordinator.pyorsensor.py.- Grid phases decoded as signed 16-bit two's complement (negative = exporting to grid), matching the existing 1-phase handling.
- Sentinel logic (
0xFF00+) applied per-phase on solar/EV/house: if any single phase returns a sentinel value the entire field is set toNonefor that cycle, allowing the coordinator to retain the last valid reading rather than producing a corrupt total.
Implementation Notes
- No changes to
coordinator.py,sensor.py, or any entity files — the fix is entirely in the protocol layer. The coordinator's existing sentinel-handling logic (skipNonevalues, retain last valid) works correctly for 3-phase totals without modification. - 1-phase behaviour is completely unaffected. The
SEND_DLBpath in bothconversions.pyandcommunication.pyis unchanged. - The
SEND_DLB_3Psentinel check is conservative: a sentinel on any one phase of a given field (e.g.solar_phase2 = 0xFF10) marks that whole fieldNonerather than attempting a partial sum. This matches the 1-phase policy and avoids underreporting during DLB module state transitions.
Testing
- Hardware: Beny Charger BCP-AT1N-L (3-phase, firmware 1.27) with Solar DLB module
- Protocol verification: PCAPdroid UDP capture on port 3333, decoded byte-for-byte against Z-Box app readings. Two consecutive packets verified; both match ground truth to within 0.05 kW on all four fields.
- 1-phase regression: 1-phase
SEND_DLBrouting and decode verified unchanged against the original DLB.txt capture (solar=3.05 kW, house=1.95 kW, grid=−1.10 kW, EV=0.00 kW — all exact matches).
Breaking Changes
None. 1-phase chargers are entirely unaffected. 3-phase charger users will see their DLB sensors change from incorrect values to correct ones — this is the intended fix.