Skip to content

VPP remote power control works on MOD TL3-XH (DTC 5400) — the WIT-only gate in select.py excludes a family that supports it #373

Description

@KevlarD-67

Summary

select.py:51 gates all VPP control entities behind a hardcoded register-map name
check:

is_wit = str(register_map_name).upper() in ("WIT_4000_15000TL3", "WIT_29900_50000TL3_XHU")

A MOD 10KTL3-XH (MOD_6000_15000TL3_XH) therefore gets no VPP controls.
Measured on hardware: this inverter does support remote power control. #143
describes the WIT gate as a conservative choice to avoid regressions elsewhere; it
contains no evidence that other families lack the capability.

30407 alone does nothing

With 30409=5, 30408=2, 30407=1 but 30100=0: battery charge power did not
move for three minutes (2600 W, flat), and the duration timer did not run (30407
still 1 after 180 s). This is presumably why the capability is easy to miss.

With control_authority it works

Write order 30409 → 30408 → 30407=1 → 30100=1, so the documented hazard state
(30100 enabled without 30407) never occurs:

12:22   charge 2608 W   to grid 1117 W    before
12:23   charge  303 W   to grid 3452 W    VPP active (5 %)
12:24   charge  302 W   to grid 3461 W
12:25   charge 2609 W   to grid 1149 W    after disabling

Transfer function is linear

Three points, each held ~90 s:

30409 battery charge power
5 % 302 W
10 % 700 W
20 % 1466 W

77.6 W per percentage point, offset −86 W. Predicting 20 % from the first two
points gave 1496 W against a measured 1466 W.

The reference is the battery, not a setting. Raising 3311
(uw_ac_charging_max_power_limit) from 7.5 kW to 15.0 kW left the 20 % result
unchanged (1466 → 1463 W). Full scale is ≈7.25 kW, consistent with a 15 kWh
battery at 0.5C and with the highest charge power ever recorded on this system
(7247 W, when the classic charge power rate was set to 100 %).

Three findings that belong in any implementation

1. ⚠️ The power command is a target, not a limit — and it overrides
allow_grid_charge.
At 30409=100 with insufficient PV:

+0s   charge 2592 W   from grid   0 W
+5s   charge 4767 W   from grid 912 W   <<< aborted by our safety threshold

The inverter actively climbed toward the commanded power and imported from the
grid while allow_grid_charge (3049) was 0. At 5/10/20 % only downward
limiting is visible, which gives a misleading impression of a cap. An
implementation should bound the commanded percentage against available PV unless
grid import is explicitly intended.

2. The duration expires but the registers do not clear. With 30408=2
(minutes), the power constraint released after ~128 s and normal self-consumption
resumed — but 30407, 30409 and 30100 all remained set for the full 240 s
observation window. Active/inactive cannot be inferred from the register
values.

3. Register 30474 mirrors the last commanded setpoint. It tracked 5 → 10 → 20
→ 100 across four runs, does not revert on disable, and a direct write is accepted
but ignored (echo returns the written value, read-back keeps the old one). It was
the only unexpected change in a full 1150-register before/after snapshot.

Suggested change

Replace the hardcoded name check with a capability probe, or extend the list to
MOD. A safe probe exists: FC6 to 30408 with its own current value returns
exception 2 on inverters that lack the address space — verified, since this
inverter answers exception 2 for genuinely unimplemented addresses such as 1090
and 1092 — and succeeds where the range is present.

Given finding 1, MOD support should ship with a guard against commanding more
power than PV can supply, unless the user opts into grid import.

Offer

Happy to run further measurements. The harness used here has an explicit
allowlist, a hard deny list on 30100/30407/30410, abort thresholds on grid import,
and full-register before/after snapshots — the 30474 side effect was only found
because of the snapshot diff.

Environment

Integration v1.5.4
Inverter Growatt MOD 10KTL3-XH (3-phase hybrid, backup box, 15 kWh battery)
DTC 5400 (VPP reg 30000 and legacy reg 43)
Protocol V2.02
Firmware DN1.0
Detected profile mod_6000_15000tl3_xh_v201 (auto-detection, accepted, "Very High" confidence)
Transport Elfin EW41 RS485→WiFi gateway, Modbus gateway mode, TCP 502, 9600 8N1

Measurements were made with a standalone zero-dependency MBAP client using the
same framing as pymodbus, to rule out integration-side handling. Diagnostics file
and Universal Register Scanner CSVs available on request.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions