Skip to content

Commit f25628c

Browse files
author
Maksym Shtembuliak
committed
fix: qualify MUST controls and capture optional BMS evidence
1 parent 6f1a0b0 commit f25628c

13 files changed

Lines changed: 350 additions & 37 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ the GitHub release body should be rendered from the matching version section her
99

1010
### Added
1111

12+
- MUST PV3300 Support Archives can collect the optional documented BMS/SOC
13+
block with one bounded read. This is diagnostic evidence only; normal polling
14+
and sensor exposure are unchanged until firmware support is confirmed.
15+
1216
- Advanced setup can include up to eight known collector IPs in the ordinary
1317
scan, alongside local discovery. Routed/VPN collectors no longer require a
1418
hardcoded scan target. The list is setup-flow scoped; identity and recovery
@@ -57,6 +61,11 @@ the GitHub release body should be rendered from the matching version section her
5761

5862
### Fixed
5963

64+
- MUST PV/PH18 controls no longer claim local write verification based only on
65+
cloud-catalog presence. All 27 document-backed controls require Full Control;
66+
the 20 previously shown in Auto are now correctly untested. No inverter
67+
settings are changed automatically; review automations using those controls.
68+
6069
- Manual callback identity checks now honor the configured advertised callback
6170
IP and port, separately from Home Assistant's local listener. NAT overrides
6271
no longer apply only to later recovery/runtime requests.

‎catalog/inverter_models/models/must_pv18_3024.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"vendor_register_map": "partial",
3131
"notes": [
3232
"Runtime support uses the MUST PH RS485 map (v1.4.3 and v1.4.15 xlsx, cross-checked against the third-party implementation), with per-control read-back over gap-free register blocks.",
33-
"The control surface is cross-referenced from the SmartESS cloud device_settings field catalog and the vendor 1.4.15 register map: the 20 fields the cloud exposes ship tested (visible in the default control mode); datasheet-only settings absent from the cloud catalog (grid charging and the battery-equalization block) ship untested (full-control only).",
33+
"All 27 controls are document-backed but locally unverified, available only in Full Control. Presence in the SmartESS cloud settings catalog is not a local hardware write test; the former tested flags for 20 cloud-visible fields were withdrawn.",
3434
"The register map is documentary; hardware write confirmation is still pending a tester report. The inverter validates its own writes, so a register that is absent or locked self-disables via the write-failure block."
3535
]
3636
},

‎catalog/inverter_models/models/must_pv3300.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@
2828
"smartess_control_surface": "cloud_catalog",
2929
"vendor_register_map": "partial",
3030
"notes": [
31-
"Inherits the existing MUST control profile unchanged.",
31+
"Inherits all 27 document-backed MUST controls as untested, available only in Full Control; cloud visibility does not prove local writes on this firmware.",
3232
"Whole-percent load and battery/grid direction normalization are qualified by the PV3300 local marker, not by the retail label or live power magnitude."
3333
]
3434
},
3535
"known_limitations": [
36+
"SOC is not yet exposed. Support Archives can check the vendor-documented optional BMS register block, but its availability on this firmware is unconfirmed.",
3637
"Learned maps tied to the older generic MUST schema are not automatically rebased; existing compatibility checks remain in force.",
3738
"Output Current and Inverter Load Current can remain zero on the reported unit; their meaning is still under investigation.",
3839
"Other PV/PH/EP models retain the common register interpretation because published MUST documents disagree on load-percentage units.",

‎custom_components/eybond_local/drivers/must.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""MUST PV/PH18 Modbus RTU read-only driver."""
1+
"""MUST PV/PH18 telemetry and document-backed, unverified local controls."""
22

33
from __future__ import annotations
44

@@ -31,6 +31,7 @@
3131

3232

3333
_MODEL_PREFIXES = ("PV", "PH", "EP")
34+
_BMS_DIAGNOSTIC_TIMEOUT_SECONDS = 3.0
3435

3536

3637
class MustPvPh18Driver(ModbusWriteErrorMixin, InverterDriver):
@@ -254,6 +255,21 @@ async def async_capture_support_evidence(
254255
source="PH/PV Modbus 1.4.3 and protocol 1916 current registers",
255256
purpose="support_only_zero_bulk_current_single_read_comparison",
256257
)
258+
# The vendor's 6422/1916 map describes a separate optional BMS
259+
# window (voltage/current/temperature/reserved/SOC). Its presence
260+
# on this firmware is not yet confirmed. Archive-only, one bounded
261+
# read, no runtime schema/identity/fixture promotion or SOC guess.
262+
current_status = evidence.get("current_read_diagnostics", {}).get("status", "completed")
263+
if current_status == "completed":
264+
evidence["bms_read_diagnostics"] = await capture_support_reads(
265+
session,
266+
((109, 5, "bms_soc_candidate"),),
267+
timeout_seconds=_BMS_DIAGNOSTIC_TIMEOUT_SECONDS,
268+
source="Manufacturer protocol 6422 (1916), FC03 BMS registers 109-113",
269+
purpose="support_only_optional_bms_soc_availability",
270+
)
271+
else:
272+
evidence["bms_read_diagnostics"] = {"status": "skipped_after_current_read_failure"}
257273
return evidence
258274

259275
def local_register_read_plans(

‎custom_components/eybond_local/protocol_catalogs/profiles/must_pv_ph18/base.json‎

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"provenance": "doc_backed",
4747
"support_notes": "From PH PV RS485 Modbus RTU 1.4.3 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
4848
"enabled_default": true,
49-
"tested": true
49+
"tested": false
5050
},
5151
{
5252
"key": "power_save_mode",
@@ -60,7 +60,7 @@
6060
"provenance": "doc_backed",
6161
"support_notes": "From PH PV RS485 Modbus RTU 1.4.3 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
6262
"enabled_default": true,
63-
"tested": true
63+
"tested": false
6464
},
6565
{
6666
"key": "grid_charge_enable",
@@ -107,7 +107,7 @@
107107
}
108108
],
109109
"enabled_default": true,
110-
"tested": true
110+
"tested": false
111111
},
112112
{
113113
"key": "grid_max_charge_current",
@@ -126,7 +126,7 @@
126126
"provenance": "doc_backed",
127127
"support_notes": "From PH PV RS485 Modbus RTU 1.4.3 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
128128
"enabled_default": true,
129-
"tested": true
129+
"tested": false
130130
},
131131
{
132132
"key": "max_combined_charge_current",
@@ -145,7 +145,7 @@
145145
"provenance": "doc_backed",
146146
"support_notes": "From PH PV RS485 Modbus RTU 1.4.3 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
147147
"enabled_default": true,
148-
"tested": true
148+
"tested": false
149149
},
150150
{
151151
"key": "inverter_output_voltage",
@@ -164,7 +164,7 @@
164164
"provenance": "doc_backed",
165165
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
166166
"enabled_default": true,
167-
"tested": true
167+
"tested": false
168168
},
169169
{
170170
"key": "inverter_output_frequency",
@@ -192,7 +192,7 @@
192192
}
193193
],
194194
"enabled_default": true,
195-
"tested": true
195+
"tested": false
196196
},
197197
{
198198
"key": "pv_max_charge_current",
@@ -211,7 +211,7 @@
211211
"provenance": "doc_backed",
212212
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
213213
"enabled_default": true,
214-
"tested": true
214+
"tested": false
215215
},
216216
{
217217
"key": "float_voltage",
@@ -230,7 +230,7 @@
230230
"provenance": "doc_backed",
231231
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
232232
"enabled_default": true,
233-
"tested": true
233+
"tested": false
234234
},
235235
{
236236
"key": "absorption_voltage",
@@ -249,7 +249,7 @@
249249
"provenance": "doc_backed",
250250
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
251251
"enabled_default": true,
252-
"tested": true
252+
"tested": false
253253
},
254254
{
255255
"key": "battery_type",
@@ -307,7 +307,7 @@
307307
}
308308
],
309309
"enabled_default": true,
310-
"tested": true
310+
"tested": false
311311
},
312312
{
313313
"key": "battery_stop_charge_voltage",
@@ -326,7 +326,7 @@
326326
"provenance": "doc_backed",
327327
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
328328
"enabled_default": true,
329-
"tested": true
329+
"tested": false
330330
},
331331
{
332332
"key": "battery_stop_discharge_voltage",
@@ -345,7 +345,7 @@
345345
"provenance": "doc_backed",
346346
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
347347
"enabled_default": true,
348-
"tested": true
348+
"tested": false
349349
},
350350
{
351351
"key": "battery_low_voltage",
@@ -364,7 +364,7 @@
364364
"provenance": "doc_backed",
365365
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
366366
"enabled_default": true,
367-
"tested": true
367+
"tested": false
368368
},
369369
{
370370
"key": "battery_high_voltage",
@@ -383,7 +383,7 @@
383383
"provenance": "doc_backed",
384384
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
385385
"enabled_default": true,
386-
"tested": true
386+
"tested": false
387387
},
388388
{
389389
"key": "max_discharge_current",
@@ -402,7 +402,7 @@
402402
"provenance": "doc_backed",
403403
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
404404
"enabled_default": true,
405-
"tested": true
405+
"tested": false
406406
},
407407
{
408408
"key": "energy_use_mode",
@@ -442,7 +442,7 @@
442442
}
443443
],
444444
"enabled_default": true,
445-
"tested": true
445+
"tested": false
446446
},
447447
{
448448
"key": "grid_protect_standard",
@@ -482,7 +482,7 @@
482482
}
483483
],
484484
"enabled_default": true,
485-
"tested": true
485+
"tested": false
486486
},
487487
{
488488
"key": "solar_use_aim",
@@ -510,7 +510,7 @@
510510
}
511511
],
512512
"enabled_default": true,
513-
"tested": true
513+
"tested": false
514514
},
515515
{
516516
"key": "discharge_to_grid_enable",
@@ -524,7 +524,7 @@
524524
"provenance": "doc_backed",
525525
"support_notes": "From PH1800/PV1800/EP1800/PV3500/EP3500 RS485 Modbus RTU 1.4.15 xlsx; the SmartESS cloud exposes this control in its device settings for this model, awaiting on-hardware write confirmation.",
526526
"enabled_default": true,
527-
"tested": true
527+
"tested": false
528528
},
529529
{
530530
"key": "battery_equalization_enable",

‎docs/generated/INVERTER_MODEL_CATALOG.generated.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ Runtime descriptors with no specific commercial model record. These are generic
381381
- Coverage: runtime available, cloud cloud_catalog, vendor map partial
382382
- Coverage notes:
383383
- Runtime support uses the MUST PH RS485 map (v1.4.3 and v1.4.15 xlsx, cross-checked against the third-party implementation), with per-control read-back over gap-free register blocks.
384-
- The control surface is cross-referenced from the SmartESS cloud device_settings field catalog and the vendor 1.4.15 register map: the 20 fields the cloud exposes ship tested (visible in the default control mode); datasheet-only settings absent from the cloud catalog (grid charging and the battery-equalization block) ship untested (full-control only).
384+
- All 27 controls are document-backed but locally unverified, available only in Full Control. Presence in the SmartESS cloud settings catalog is not a local hardware write test; the former tested flags for 20 cloud-visible fields were withdrawn.
385385
- The register map is documentary; hardware write confirmation is still pending a tester report. The inverter validates its own writes, so a register that is absent or locked self-disables via the write-failure block.
386386
- Summary: MUST PV18-3024 support is based on issue #5 support archives and a MUST PV/PH18 Modbus register map (v1.4.3 and v1.4.15). The user archives showed Auto/PI30 probe timeouts, SmartESS protocol asset "02FF,0,0," and collector serial baudrate 19200-8-1-0. The runtime accepts both the PV18 marker and a numeric PV1800 marker at Modbus register 20001, and binds a full MUST PV/PH18 Modbus surface using slave address 4. It exposes a writable control surface cross-referenced from the SmartESS cloud device_settings catalog and the 1.4.15 register map (output voltage/frequency, energy use mode, grid protection standard, charge-source priority, battery type, charge/discharge current limits, and battery voltage windows) with per-control read-back; hardware write confirmation is pending.
387387
- Variants:
@@ -391,7 +391,7 @@ Runtime descriptors with no specific commercial model record. These are generic
391391
- `must_pv18_3024` → surface `must_pv_ph18_full` (driver must_pv_ph18, variant pv_ph18)
392392
- Protocol: must_pv_ph18 | Detection: anchors (protocol.protocol_id=MUST_PV_PH18; identity.model_number=['PV18', 'PV1800'])
393393
- Tier: full | Read-only: no | Profile: must_pv_ph18/base.json | Schema: must_pv_ph18/base.json
394-
- Capabilities: 27 (tested 20, untested 7); support tiers: standard 27 | Telemetry: 37 measurements, 0 binary sensors
394+
- Capabilities: 27 (untested 27); support tiers: standard 27 | Telemetry: 37 measurements, 0 binary sensors
395395
- Known limitations:
396396
- Requires collector serial settings compatible with Modbus RTU 19200 8N1 and inverter slave address 4.
397397
- Control writes are derived from the SmartESS cloud settings catalog and the vendor register map, not yet independently confirmed on hardware; setpoint windows are left wide because the documented ranges are battery-voltage-grade dependent and the inverter validates its own writes.
@@ -405,7 +405,7 @@ Runtime descriptors with no specific commercial model record. These are generic
405405
- Validation: hardware captured, telemetry partial, controls partial
406406
- Coverage: runtime available, cloud cloud_catalog, vendor map partial
407407
- Coverage notes:
408-
- Inherits the existing MUST control profile unchanged.
408+
- Inherits all 27 document-backed MUST controls as untested, available only in Full Control; cloud visibility does not prove local writes on this firmware.
409409
- Whole-percent load and battery/grid direction normalization are qualified by the PV3300 local marker, not by the retail label or live power magnitude.
410410
- Summary: PV3300 keeps the common MUST map and controls, with a scoped telemetry override: whole-percent load, charge-positive battery current/power and import-positive grid power. Native AC converter power retains its vendor sign.
411411
- Variants:
@@ -415,8 +415,9 @@ Runtime descriptors with no specific commercial model record. These are generic
415415
- `must_pv3300` → surface `must_pv3300_full` (driver must_pv_ph18, variant pv3300)
416416
- Protocol: must_pv_ph18 | Detection: anchors (protocol.protocol_id=MUST_PV_PH18; identity.model_number=PV3300)
417417
- Tier: full | Read-only: no | Profile: must_pv_ph18/base.json | Schema: must_pv_ph18/pv3300.json
418-
- Capabilities: 27 (tested 20, untested 7); support tiers: standard 27 | Telemetry: 37 measurements, 0 binary sensors
418+
- Capabilities: 27 (untested 27); support tiers: standard 27 | Telemetry: 37 measurements, 0 binary sensors
419419
- Known limitations:
420+
- SOC is not yet exposed. Support Archives can check the vendor-documented optional BMS register block, but its availability on this firmware is unconfirmed.
420421
- Learned maps tied to the older generic MUST schema are not automatically rebased; existing compatibility checks remain in force.
421422
- Output Current and Inverter Load Current can remain zero on the reported unit; their meaning is still under investigation.
422423
- Other PV/PH/EP models retain the common register interpretation because published MUST documents disagree on load-percentage units.

‎docs/user/RUNTIME_AND_INVERTER.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,32 @@ load, create a Support Archive: for PV3300 it compares the combined response
133133
with individual reads. The integration does not replace a reported zero with
134134
an estimated current.
135135

136+
### MUST controls and battery percentage
137+
138+
The MUST profile, including PV3300, contains 27 document-backed controls such as
139+
**Energy Use Mode**, **Charge Source Priority**, charging limits and **Off-Grid
140+
Output**. They are **untested** locally: a setting appearing in the cloud app is
141+
not proof that the local command works on every firmware.
142+
143+
To opt in, open **Configure → Polling and inverter detection → Control mode → Full Control**.
144+
The controls belong to the inverter device's configuration section, not the
145+
collector. Selecting Full Control does not send a command or change inverter
146+
settings. **Off-Grid Output** is specifically the off-grid output enable; it is
147+
not a general inverter power switch or the separate cloud **Ongrid Switch**.
148+
149+
**Upgrade note:** older builds incorrectly treated 20 cloud-listed controls as
150+
tested. They are no longer exposed in **Auto**. Use Full Control if you choose
151+
to test them; review automations that referenced the earlier controls. Telemetry
152+
and the selected control mode are not changed. No local hardware qualification
153+
is implied by the existing broad setpoint limits; use only settings appropriate
154+
to your exact model and battery.
155+
156+
There is no verified PV3300 SOC sensor yet. Its manufacturer protocol describes
157+
an optional BMS block containing SOC. Creating a Support Archive now checks that
158+
block once, read-only, with a three-second limit. Unsupported registers or a
159+
timeout are recorded in the archive; they do not add a zero-percent sensor or
160+
change normal polling. Battery capacity in Ah is a setting, not remaining charge.
161+
136162
## SRNE partial battery / PV readings
137163

138164
Some SRNE firmware rejects a combined battery/PV request while answering smaller
@@ -196,6 +222,18 @@ Rated values are diagnostics, disabled by default. **Rated Power** is not a
196222
measurement of actual output power. **AC Charger Enabled** describes the charger
197223
state, not a measured charging current or power.
198224

225+
**Grid Fault Voltage** is a separate field named that way in the manufacturer's
226+
protocol, kept under diagnostics and disabled by default. Enabling it makes its
227+
reading available for dashboards too; the diagnostic category does not change
228+
the value. It is not automatically substituted for Grid Voltage based on one
229+
comparison with a meter.
230+
231+
The current PV readings come from a single `PV?` reply. They are not verified as
232+
a sum of two MPPT inputs, and separate PV1/PV2 channels are not yet mapped. Raw
233+
replies to those commands can help establish a model-specific extension.
234+
Polling uses the normal adaptive scheduler; check **Current Poll Interval** on
235+
the collector for the current value rather than assuming a fixed refresh rate.
236+
199237
If a PV, output-frequency or rated-values request fails, that group's old values
200238
become unavailable while basic readings can continue. If the main status request
201239
fails, normal connection recovery applies. No inverter serial, battery percentage,

0 commit comments

Comments
 (0)