Overview
Support the Growatt WIT hybrid series (WIT 4-15kW / 8000-HU / 12000-HU / 15K-HU / 100000-HU) through the wills106/homeassistant-solax-modbus integration, reusing the existing solax_modbus_growatt_min (GEN4) platform.
User-visible outcome: a user with a Growatt WIT installs solax_modbus, runs the BESS setup wizard, sees "Growatt MIN/GEN4" light up green with sensors auto-mapped, and gets a working optimized schedule — no manual sensor mapping, no cloud integration.
Reported by @tissson in #627, where a WIT was misdetected as growatt_server_sph via the cloud integration.
Background — what #627 showed
The reporter's WIT has no working control path today:
- The official
growatt_server cloud entry exposes only a plant-level device ("Malax Total"), so BESS's device resolution picks that, and every growatt_server.read_ac_charge_times call returns 500.
- Detection is elimination-based (
ha_api_controller.py:3118-3131): no -tlx_ marker → assume SPH. WIT never enters the logic.
- His actual WIT entities come from a third-party HACS integration,
0xAHA/Growatt_ModbusTCP (domain growatt_modbus), which BESS knows nothing about. He mapped all sensors by hand.
What is already true today (verified, not assumed)
solax_modbus classifies WIT as GEN4 — plugin_growatt.py:9703-9707:
# WIT
"0PE": HYBRID | GEN4 | X3, # WIT 8000-HU, 2 MPPT
"0PC": HYBRID | GEN4 | X3, # WIT 12000-HU, 2 MPPT
"0PH": HYBRID | GEN4 | X3 | MPPT10, # WIT 100000-HU, 10 MPPT
"0HU": HYBRID | GEN4 | X3, # WIT 15K-HU, 2 MPPT
Every entity in SOLAX_GROWATT_MIN_SUFFIX_MAP is reachable by a WIT. Each of the 31 mapped keys was resolved to its allowedtypes in plugin_growatt.py. All are gated on GEN4/HYBRID with no X1/X3 constraint, and the only serial blacklist in the plugin is SPF_SINGLE_MPPT_SERIAL_PREFIXES (SPF only). A HYBRID | GEN4 | X3 WIT satisfies all of them:
| BESS map group |
allowedtypes in plugin |
battery_soc, battery_charge_power, battery_discharge_power |
GEN4 | HYBRID |
total_forward_power, total_reverse_power, total_load_power, total_yield |
GEN4 |
| lifetime energy counters |
GEN4 / GEN4 | HYBRID |
ems_charging_rate, ems_discharging_rate, ems_*_stop_soc |
HYBRID | GEN4 |
charger_switch |
HYBRID | AC | GEN4 |
vpp_status, vpp_remote_control, vpp_power, vpp_time, vpp_allow_ac_charging |
GEN3 | GEN4 |
limit_grid_export, grid_export_limit |
GEN2 | GEN3 | GEN4 |
time_1..9_{enabled,begin,end,mode,update} |
HYBRID | GEN4 |
So detection already fires. _GROWATT_TOU_MARKER_SUFFIX = "time_1_enabled" (ha_api_controller.py:3801) is created for any HYBRID | GEN4 inverter, so a WIT on solax_modbus is detected as solax_modbus_growatt_min with no code change — sensors mapped, wizard pill green.
And BESS already has the control mode WIT most likely needs. SolaxModbusGrowattController supports both tou and vpp, and the Growatt VPP path (registers 30100 / 30407-30410) landed for #118. GEN4 currently defaults to tou (battery_system_manager.py:_resolve_control_mode).
The open question this issue exists to answer
Does WIT honour the GEN4 numbered-TOU registers, or is VPP the only working write path?
Evidence that TOU may be a dead end on WIT — from the other integration's WIT profile (0xAHA/Growatt_ModbusTCP, profiles/wit.py):
- Register 30476 (priority_mode) is READ-ONLY - shows TOU default, cannot be changed via Modbus
That integration drives WIT entirely through work_mode (register 202: 0 Standby / 1 Charge / 2 Discharge) plus a remote charge/discharge power rate, gated by control_authority (register 30100) — a continuous VPP-style command model, and it re-asserts work_mode before every power-rate write.
Register 30100 is the same vpp_status register BESS already writes in VPP mode. So the likely answer is: WIT works today on solax_modbus_growatt_min with control_mode: vpp, and TOU mode silently no-ops.
This cannot be settled from source. It needs one user with a WIT and solax_modbus installed.
Proposed work
Phase 1 — validate (blocking, needs hardware). Ask a WIT owner to install solax_modbus alongside their existing setup and report:
- Does the wizard show "Growatt MIN/GEN4" detected, with sensors auto-populated?
- In
control_mode: tou — does a TOU slot write actually change inverter behaviour, or is it accepted and ignored?
- In
control_mode: vpp — does a charge and a discharge command take effect?
Phase 2 — wire the outcome. Depending on Phase 1:
- VPP works, TOU doesn't (expected): make WIT resolve to
vpp rather than the GEN4 tou default. WIT is not distinguishable from a MIN by entity shape alone — both are HYBRID | GEN4 — so this needs a positive signal (serial prefix 0PE/0PC/0PH/0HU via the device/serial, or an explicit user choice). Options to weigh: a WIT-specific platform id, or keeping solax_modbus_growatt_min and steering only the control-mode default.
- Both work: documentation only.
- Neither works: close this and reopen on the
growatt_modbus path (below).
Phase 3 — document. WIT in the supported-hardware table at the correct maturity level (experimental until a real install is confirmed), plus a wizard note that WIT needs solax_modbus rather than the cloud integration.
Explicitly out of scope
Adding a growatt_modbus platform for 0xAHA/Growatt_ModbusTCP. It's a real option — it has a dedicated wit.py profile, per-model register maps, and a VPP control surface close to what BESS already drives (number.growatt_battery_remote_charge_and_discharge_power, select.growatt_battery_mode_vpp, select.growatt_grid_control_authority, number.growatt_tou_active_periods, select.growatt_work_mode) — and #627's bundle already gives the full entity vocabulary. But it is a third integration to support, and solax_modbus is the path already built. File separately if Phase 1 rules solax_modbus out.
Also out of scope: fixing the growatt_server elimination-based detection that misfires on WIT (has_tlx → SPH). That's a bug, tracked from #627.
Acceptance criteria
- A WIT on solax_modbus is auto-detected by the wizard with sensors mapped, no manual entry.
- Charge and discharge commands are confirmed to take effect on real WIT hardware (log + inverter state).
- The platform's control mode is resolved correctly for WIT without the user having to know which register family their inverter honours.
- Supported-hardware docs list WIT with an accurate maturity level.
Definition of Ready
Fails criterion 4 (approach agreed) and criterion 2 (reproduction) — Awaiting: user/hardware. Phase 1's answer decides Phase 2's design; the fork is stated above so a later attempt doesn't rediscover it. Nobody on the team has WIT hardware.
Related
Overview
Support the Growatt WIT hybrid series (WIT 4-15kW / 8000-HU / 12000-HU / 15K-HU / 100000-HU) through the wills106/homeassistant-solax-modbus integration, reusing the existing
solax_modbus_growatt_min(GEN4) platform.User-visible outcome: a user with a Growatt WIT installs solax_modbus, runs the BESS setup wizard, sees "Growatt MIN/GEN4" light up green with sensors auto-mapped, and gets a working optimized schedule — no manual sensor mapping, no cloud integration.
Reported by @tissson in #627, where a WIT was misdetected as
growatt_server_sphvia the cloud integration.Background — what #627 showed
The reporter's WIT has no working control path today:
growatt_servercloud entry exposes only a plant-level device ("Malax Total"), so BESS's device resolution picks that, and everygrowatt_server.read_ac_charge_timescall returns500.ha_api_controller.py:3118-3131): no-tlx_marker → assume SPH. WIT never enters the logic.0xAHA/Growatt_ModbusTCP(domaingrowatt_modbus), which BESS knows nothing about. He mapped all sensors by hand.What is already true today (verified, not assumed)
solax_modbus classifies WIT as GEN4 —
plugin_growatt.py:9703-9707:Every entity in
SOLAX_GROWATT_MIN_SUFFIX_MAPis reachable by a WIT. Each of the 31 mapped keys was resolved to itsallowedtypesinplugin_growatt.py. All are gated onGEN4/HYBRIDwith noX1/X3constraint, and the only serial blacklist in the plugin isSPF_SINGLE_MPPT_SERIAL_PREFIXES(SPF only). AHYBRID | GEN4 | X3WIT satisfies all of them:allowedtypesin pluginbattery_soc,battery_charge_power,battery_discharge_powerGEN4 | HYBRIDtotal_forward_power,total_reverse_power,total_load_power,total_yieldGEN4GEN4/GEN4 | HYBRIDems_charging_rate,ems_discharging_rate,ems_*_stop_socHYBRID | GEN4charger_switchHYBRID | AC | GEN4vpp_status,vpp_remote_control,vpp_power,vpp_time,vpp_allow_ac_chargingGEN3 | GEN4limit_grid_export,grid_export_limitGEN2 | GEN3 | GEN4time_1..9_{enabled,begin,end,mode,update}HYBRID | GEN4So detection already fires.
_GROWATT_TOU_MARKER_SUFFIX = "time_1_enabled"(ha_api_controller.py:3801) is created for anyHYBRID | GEN4inverter, so a WIT on solax_modbus is detected assolax_modbus_growatt_minwith no code change — sensors mapped, wizard pill green.And BESS already has the control mode WIT most likely needs.
SolaxModbusGrowattControllersupports bothtouandvpp, and the Growatt VPP path (registers 30100 / 30407-30410) landed for #118. GEN4 currently defaults totou(battery_system_manager.py:_resolve_control_mode).The open question this issue exists to answer
Does WIT honour the GEN4 numbered-TOU registers, or is VPP the only working write path?
Evidence that TOU may be a dead end on WIT — from the other integration's WIT profile (
0xAHA/Growatt_ModbusTCP,profiles/wit.py):That integration drives WIT entirely through
work_mode(register 202:0 Standby / 1 Charge / 2 Discharge) plus a remote charge/discharge power rate, gated bycontrol_authority(register 30100) — a continuous VPP-style command model, and it re-assertswork_modebefore every power-rate write.Register 30100 is the same
vpp_statusregister BESS already writes in VPP mode. So the likely answer is: WIT works today onsolax_modbus_growatt_minwithcontrol_mode: vpp, and TOU mode silently no-ops.This cannot be settled from source. It needs one user with a WIT and solax_modbus installed.
Proposed work
Phase 1 — validate (blocking, needs hardware). Ask a WIT owner to install solax_modbus alongside their existing setup and report:
control_mode: tou— does a TOU slot write actually change inverter behaviour, or is it accepted and ignored?control_mode: vpp— does a charge and a discharge command take effect?Phase 2 — wire the outcome. Depending on Phase 1:
vpprather than the GEN4toudefault. WIT is not distinguishable from a MIN by entity shape alone — both areHYBRID | GEN4— so this needs a positive signal (serial prefix0PE/0PC/0PH/0HUvia the device/serial, or an explicit user choice). Options to weigh: a WIT-specific platform id, or keepingsolax_modbus_growatt_minand steering only the control-mode default.growatt_modbuspath (below).Phase 3 — document. WIT in the supported-hardware table at the correct maturity level (experimental until a real install is confirmed), plus a wizard note that WIT needs solax_modbus rather than the cloud integration.
Explicitly out of scope
Adding a
growatt_modbusplatform for0xAHA/Growatt_ModbusTCP. It's a real option — it has a dedicatedwit.pyprofile, per-model register maps, and a VPP control surface close to what BESS already drives (number.growatt_battery_remote_charge_and_discharge_power,select.growatt_battery_mode_vpp,select.growatt_grid_control_authority,number.growatt_tou_active_periods,select.growatt_work_mode) — and #627's bundle already gives the full entity vocabulary. But it is a third integration to support, and solax_modbus is the path already built. File separately if Phase 1 rules solax_modbus out.Also out of scope: fixing the
growatt_serverelimination-based detection that misfires on WIT (has_tlx→ SPH). That's a bug, tracked from #627.Acceptance criteria
Definition of Ready
Fails criterion 4 (approach agreed) and criterion 2 (reproduction) —
Awaiting: user/hardware. Phase 1's answer decides Phase 2's design; the fork is stated above so a later attempt doesn't rediscover it. Nobody on the team has WIT hardware.Related
growatt_server_sph(source of this request)