Skip to content

Commit 597f946

Browse files
author
Maksym Shtembuliak
committed
fix: use neutral PI30 VMII identity and document Victor support
1 parent f25628c commit 597f946

11 files changed

Lines changed: 308 additions & 7 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 8 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+
- Victor NM-PRO-6.2KW in the supported model catalog, backed by the owner's
13+
PI30 archive and specifically reported working controls. It reuses the
14+
existing VMII profile; no new device commands are introduced.
15+
1216
- MUST PV3300 Support Archives can collect the optional documented BMS/SOC
1317
block with one bounded read. This is diagnostic evidence only; normal polling
1418
and sensor exposure are unchanged until firmware support is confirmed.
@@ -61,6 +65,10 @@ the GitHub release body should be rendered from the matching version section her
6165

6266
### Fixed
6367

68+
- Shared PI30 `VMII-NXPW5KW` identification no longer labels every matching
69+
inverter as PowMr 4.2kW. It displays a neutral firmware-family name while
70+
preserving the old name as a compatibility alias, the profile and entity IDs.
71+
6472
- MUST PV/PH18 controls no longer claim local write verification based only on
6573
cloud-catalog presence. All 27 document-backed controls require Full Control;
6674
the 20 previously shown in Auto are now correctly untested. No inverter

‎catalog/inverter_models/models/powmr_4_2kw.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
"The same inverter also responds to the local SmartESS 0925 Modbus map through the legacy EyeBond binary tunnel; the commercial model is PowMr 4.2kW, while the raw inverter model reports VMII-NXPW5KW."
4040
]
4141
},
42-
"known_limitations": [],
42+
"known_limitations": [
43+
"The PI30 QMN identity VMII-NXPW5KW is shared with other commercial models and powers. Automatic detection uses the neutral PI30 VMII-NXPW5KW label; it does not uniquely identify PowMr or a 4.2 kW rating."
44+
],
4345
"knowledge_summary": "PowMr 4.2KW with raw model number VMII-NXPW5KW. The inverter supports PI30 ASCII and the local SmartESS 0925 Modbus runtime through the legacy EyeBond binary tunnel.",
4446
"source_keys": [
4547
"fixture_pi30_vmii_nxpw5kw"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"schema_version": 1,
3+
"model_key": "victor_nm_pro_6_2kw",
4+
"manufacturer": "Victor",
5+
"model": "NM-PRO-6.2KW",
6+
"aliases": [],
7+
"lifecycle": "supported",
8+
"variants": [
9+
{
10+
"variant_key": "pi30_vmii_nxpw5kw",
11+
"label": "PI30 VMII-NXPW5KW, 25-field 6200 W / 48 V rating",
12+
"device_descriptor_keys": ["pi30_vmii_nxpw5kw"],
13+
"known_firmware": ["VERFW:00021.12", "VERFW2:00000.00"]
14+
}
15+
],
16+
"validation": {
17+
"hardware": "captured",
18+
"telemetry": "confirmed",
19+
"controls": "partial"
20+
},
21+
"coverage": {
22+
"runtime_control_surface": "available",
23+
"smartess_control_surface": "unknown",
24+
"vendor_register_map": "unknown",
25+
"notes": [
26+
"Uses the existing PI30 VMII profile; no new commands, transport or wider write ranges are introduced.",
27+
"The owner confirmed Output Source Priority, Charge Source Priority and battery bulk, float, cut-off and recharge voltage controls on integration 0.1.53. Other controls and firmware variants are not confirmed by this report."
28+
]
29+
},
30+
"known_limitations": [
31+
"QMN reports the shared VMII-NXPW5KW identity, not a unique Victor retail model. Automatic detection therefore displays PI30 VMII-NXPW5KW; the public catalog records the owner-reported brand separately.",
32+
"QVFW3 and optional QET/QLT/QT energy commands returned NAK in the supplied archive.",
33+
"Hardware confirmation was supplied for integration 0.1.53; replay on current code does not replace a current-version hardware test."
34+
],
35+
"knowledge_summary": "Victor NM-PRO-6.2KW is confirmed by the issue #54 owner and support archive as a 6200 W / 48 V PI30 inverter using the shared VMII-NXPW5KW profile, with working telemetry and a specifically reported subset of controls.",
36+
"source_keys": ["support_archive_issue_54_victor_nm_pro_6_2kw"]
37+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"schema_version": 1,
3+
"source_key": "support_archive_issue_54_victor_nm_pro_6_2kw",
4+
"kind": "support_archive",
5+
"title": "Victor NM-PRO-6.2KW PI30 archive and owner control report",
6+
"reference": "project-issue:54",
7+
"captured_at": "2026-09-30",
8+
"visibility": "public",
9+
"summary_publishable": true,
10+
"summary": "The owner identifies the unit as Victor NM-PRO-6.2KW and reports working telemetry on integration 0.1.53. The archive records QPI=PI30, shared QMN=VMII-NXPW5KW, a 25-field QPIRI response rated 6200 W / 48 V, live QPIGS, QFLAG, QMOD and firmware replies. The owner explicitly confirms Output Source Priority, Charge Source Priority and battery bulk, float, cut-off and recharge voltage changes. The read-only archive itself is not a write trace, and the report does not qualify every inherited control or uniquely identify the brand from QMN.",
11+
"assertions": [
12+
"commercial_identity",
13+
"runtime_compatibility",
14+
"telemetry_validation",
15+
"hardware_test",
16+
"write_validation"
17+
]
18+
}

‎custom_components/eybond_local/protocol_catalogs/inverter_catalog.json‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2689,7 +2689,10 @@
26892689
{
26902690
"entry_key": "pi30_vmii_nxpw5kw",
26912691
"surface_key": "pi30_vmii_full",
2692-
"model_name": "PowMr 4.2kW",
2692+
"model_name": "PI30 VMII-NXPW5KW",
2693+
"aliases": [
2694+
"PowMr 4.2kW"
2695+
],
26932696
"priority": 220,
26942697
"anchors": [
26952698
{
@@ -2703,7 +2706,8 @@
27032706
],
27042707
"provenance": {
27052708
"sources": [
2706-
"fixture:pi30-vmii-nxpw5kw-live-capture"
2709+
"fixture:pi30-vmii-nxpw5kw-live-capture",
2710+
"project-issue:54"
27072711
],
27082712
"confidence": "capture-derived"
27092713
}

‎docs/generated/INVERTER_MODEL_CATALOG.generated.md‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Generated by `tools/model_catalog.py`. Do not edit by hand.
44

55
- Model catalog version: 2026.08.2
66
- Runtime inverter catalog version: 2026.08.8
7-
- Models: 20 | Variants: 22
7+
- Models: 21 | Variants: 23
88

99
## How To Read This Catalog
1010

@@ -45,6 +45,7 @@ This page describes what has been confirmed for a specific inverter model or mod
4545
| MUST-compatible | PV3300 | must_pv_ph18 | anchors | full | partial | partial | captured |
4646
| Sandisolar | SD 11KP48V WIFI | modbus_smg | fingerprint | full | partial | partial | captured |
4747
| SRNE-compatible | Modbus family | srne_modbus | anchors | partial | partial | none | none |
48+
| Victor | NM-PRO-6.2KW | pi30 | anchors | full | confirmed | partial | captured |
4849
| Yingfa | YF6.2K-LEL-1B | pi30 | anchors | full | confirmed | partial | captured |
4950
| ZL Power | GSIII 12 kW | eybond_09c1 | anchors | partial | partial | none | captured |
5051

@@ -449,7 +450,8 @@ Runtime descriptors with no specific commercial model record. These are generic
449450
- Protocol: pi30 | Detection: anchors (protocol.protocol_id=PI30; identity.model_number=VMII-NXPW5KW)
450451
- Tier: full | Read-only: no | Profile: pi30_ascii/models/vmii_nxpw5kw.json | Schema: pi30_ascii/models/vmii_nxpw5kw.json
451452
- Capabilities: 20 (tested 20); support tiers: standard 20 | Telemetry: 77 measurements, 16 binary sensors
452-
- Known limitations: —
453+
- Known limitations:
454+
- The PI30 QMN identity VMII-NXPW5KW is shared with other commercial models and powers. Automatic detection uses the neutral PI30 VMII-NXPW5KW label; it does not uniquely identify PowMr or a 4.2 kW rating.
453455
- Evidence: 1 source(s)
454456

455457
### Sandisolar — SD 11KP48V WIFI (`sandisolar_sd_11kp48v_wifi`)
@@ -523,6 +525,30 @@ Runtime descriptors with no specific commercial model record. These are generic
523525
- Detection relies on the product-info register block containing an SRNE-like marker; commercial model names may need follow-up catalog entries.
524526
- Evidence: 1 source(s)
525527

528+
### Victor — NM-PRO-6.2KW (`victor_nm_pro_6_2kw`)
529+
530+
- Lifecycle: supported
531+
- Aliases: —
532+
- Validation: hardware captured, telemetry confirmed, controls partial
533+
- Coverage: runtime available, cloud unknown, vendor map unknown
534+
- Coverage notes:
535+
- Uses the existing PI30 VMII profile; no new commands, transport or wider write ranges are introduced.
536+
- The owner confirmed Output Source Priority, Charge Source Priority and battery bulk, float, cut-off and recharge voltage controls on integration 0.1.53. Other controls and firmware variants are not confirmed by this report.
537+
- Summary: Victor NM-PRO-6.2KW is confirmed by the issue #54 owner and support archive as a 6200 W / 48 V PI30 inverter using the shared VMII-NXPW5KW profile, with working telemetry and a specifically reported subset of controls.
538+
- Variants:
539+
- `pi30_vmii_nxpw5kw` — PI30 VMII-NXPW5KW, 25-field 6200 W / 48 V rating
540+
- Descriptors: pi30_vmii_nxpw5kw
541+
- Known firmware: VERFW:00021.12, VERFW2:00000.00
542+
- `pi30_vmii_nxpw5kw` → surface `pi30_vmii_full` (driver pi30, variant vmii_nxpw5kw)
543+
- Protocol: pi30 | Detection: anchors (protocol.protocol_id=PI30; identity.model_number=VMII-NXPW5KW)
544+
- Tier: full | Read-only: no | Profile: pi30_ascii/models/vmii_nxpw5kw.json | Schema: pi30_ascii/models/vmii_nxpw5kw.json
545+
- Capabilities: 20 (tested 20); support tiers: standard 20 | Telemetry: 77 measurements, 16 binary sensors
546+
- Known limitations:
547+
- QMN reports the shared VMII-NXPW5KW identity, not a unique Victor retail model. Automatic detection therefore displays PI30 VMII-NXPW5KW; the public catalog records the owner-reported brand separately.
548+
- QVFW3 and optional QET/QLT/QT energy commands returned NAK in the supplied archive.
549+
- Hardware confirmation was supplied for integration 0.1.53; replay on current code does not replace a current-version hardware test.
550+
- Evidence: 1 source(s)
551+
526552
### Yingfa — YF6.2K-2K-LEL-IF (`yingfa_yf6_2k_2k_lel_if`)
527553

528554
- Lifecycle: research

‎docs/user/RUNTIME_AND_INVERTER.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ The inverter may appear after the collector. A slow protocol or a Full scan can
1717
take more than one normal polling interval. **Poll Context** shows whether the
1818
integration is detecting an inverter, reading it, or only checking the collector.
1919

20+
### PI30 names shared by several brands
21+
22+
Some inverters report a firmware identifier instead of their retail model.
23+
For example, `VMII-NXPW5KW` appears on both PowMr and Victor units, including
24+
different power ratings. These devices appear as **PI30 VMII-NXPW5KW**; the
25+
name alone does not mean 5 kW, 4.2 kW, or a particular brand. Rated power is read
26+
separately from the inverter.
27+
28+
Older builds labelled this shared profile **PowMr 4.2kW**. The corrected name
29+
keeps the same protocol, profile, controls and entity IDs. You do not need to
30+
delete and add the device again. A custom name set in Home Assistant is retained.
31+
The model catalog lists confirmed retail models separately; it does not turn a
32+
shared firmware identifier into a unique brand match.
33+
2034
## Polling and inverter detection
2135

2236
Open **Settings → Devices & Services → EyeBond Local → Configure → Polling and

‎tests/test_compiled_detection_catalog.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,21 @@ def test_unique_persisted_model_resolves_exact_untested_control_surface(self) ->
153153
self.assertEqual(legacy_descriptor.key, descriptor.key)
154154
self.assertEqual(legacy_surface.key, surface.key)
155155

156+
def test_vmii_neutral_name_preserves_legacy_surface_without_brand_inference(self) -> None:
157+
old = resolve_unique_persisted_model_surface("PowMr 4.2kW")
158+
new = resolve_unique_persisted_model_surface("PI30 VMII-NXPW5KW")
159+
self.assertIsNotNone(old)
160+
self.assertIsNotNone(new)
161+
self.assertEqual(old[0].key, "pi30_vmii_nxpw5kw")
162+
self.assertEqual(old, new)
163+
self.assertEqual(new[1].variant_key, "vmii_nxpw5kw")
164+
self.assertEqual(new[1].profile_name, "pi30_ascii/models/vmii_nxpw5kw.json")
165+
self.assertTrue(model_names_share_catalog_identity("PowMr 4.2kW", "PI30 VMII-NXPW5KW"))
166+
# A public retail catalog entry is not a runtime identity alias. The
167+
# same QMN is shared by multiple brands and another (MAX) response shape.
168+
self.assertIsNone(resolve_unique_persisted_model_surface("Victor NM-PRO-6.2KW"))
169+
self.assertFalse(model_names_share_catalog_identity("PI30 VMII-NXPW5KW", "PI30 6200"))
170+
156171
def test_persisted_model_resolution_fails_closed_when_alias_is_ambiguous(self) -> None:
157172
catalog = load_compiled_detection_catalog()
158173
ambiguous = replace(

‎tests/test_model_catalog.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ def _ok_model(key: str = "mdl_a", descriptor: str = "smg_6200", **overrides) ->
8787

8888

8989
class RealCatalogTests(unittest.TestCase):
90+
def test_victor_6200_uses_shared_pi30_surface_with_partial_write_evidence(self):
91+
models = {m["model_key"]: m for m in load_models()}
92+
victor = models["victor_nm_pro_6_2kw"]
93+
self.assertEqual(victor["validation"], {"hardware": "captured", "telemetry": "confirmed", "controls": "partial"})
94+
self.assertEqual(_primary_resolution(victor, RUNTIME).surface_key, "pi30_vmii_full")
95+
self.assertEqual(_primary_resolution(models["powmr_4_2kw"], RUNTIME).surface_key, "pi30_vmii_full")
96+
self.assertEqual(victor["variants"][0]["device_descriptor_keys"], ["pi30_vmii_nxpw5kw"])
97+
self.assertIn("support_archive_issue_54_victor_nm_pro_6_2kw", victor["source_keys"])
98+
source = next(s for s in load_sources() if s["source_key"] == victor["source_keys"][0])
99+
self.assertIn("write_validation", source["assertions"])
100+
self.assertIn("0.1.53", source["summary"])
101+
self.assertIn("Charge Source Priority", source["summary"])
102+
90103
def test_issue_6_correction_does_not_remove_independent_issue_27_evidence(self):
91104
models = {m["model_key"]: m for m in load_models()}
92105
unresolved = models["yingfa_yf6_2k_2k_lel_if"]

‎tests/test_pi30_driver.py‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ async def test_probe_maps_vmii_model_number_to_display_name(self) -> None:
286286
inverter = await driver.async_probe(transport, target)
287287

288288
assert inverter is not None
289-
self.assertEqual(inverter.model_name, "PowMr 4.2kW")
289+
self.assertEqual(inverter.model_name, "PI30 VMII-NXPW5KW")
290290
self.assertEqual(inverter.details["model_number"], "VMII-NXPW5KW")
291291
self.assertEqual(inverter.profile_name, "pi30_ascii/models/vmii_nxpw5kw.json")
292292
self.assertEqual(inverter.register_schema_name, "pi30_ascii/models/vmii_nxpw5kw.json")
@@ -346,11 +346,42 @@ async def test_probe_selects_vmii_model_overlay_when_model_number_matches(self)
346346
inverter = await driver.async_probe(transport, target)
347347

348348
assert inverter is not None
349-
self.assertEqual(inverter.model_name, "PowMr 4.2kW")
349+
self.assertEqual(inverter.model_name, "PI30 VMII-NXPW5KW")
350350
self.assertEqual(inverter.variant_key, "vmii_nxpw5kw")
351351
self.assertEqual(inverter.profile_name, "pi30_ascii/models/vmii_nxpw5kw.json")
352352
self.assertEqual(inverter.register_schema_name, "pi30_ascii/models/vmii_nxpw5kw.json")
353353

354+
async def test_victor_6200_vmii_rating_and_telemetry_do_not_infer_retail_brand(self) -> None:
355+
"""Issue54 response shape, with a synthetic serial and no live writes."""
356+
from custom_components.eybond_local.metadata.profile_loader import load_driver_profile
357+
358+
target = ProbeTarget(devcode=0x0994, collector_addr=1, device_addr=0)
359+
replies = {
360+
"QPI": "PI30", "QID": "99000000000054", "QMN": "VMII-NXPW5KW",
361+
"QPIRI": "230.0 26.9 230.0 50.0 26.9 6200 6200 48.0 46.0 42.0 55.2 54.6 2 030 030 1 0 1 1 01 0 0 54.0 0 1",
362+
"QFLAG": "EabkuxzDjvy", "QMOD": "L",
363+
"QPIWS": "10000000000000000000000000000000",
364+
"QVFW": "VERFW:00021.12", "QVFW2": "VERFW2:00000.00",
365+
"QPIGS": "232.1 50.0 232.1 50.0 0440 0227 007 429 54.60 000 100 0043 00.0 000.0 00.00 00000 00010101 00 00 00000 110",
366+
"Q1": "00 00 00 000 042 030 043 00 00 000 0030 0000 13",
367+
"QVFW3": "NAK", "QET": "NAK", "QLT": "NAK", "QT": "NAK",
368+
}
369+
transport = _FakeTransport({(target.devcode, target.collector_addr, k): v for k, v in replies.items()})
370+
driver = Pi30Driver()
371+
inverter = await driver.async_probe(transport, target)
372+
self.assertIsNotNone(inverter)
373+
self.assertEqual(inverter.model_name, "PI30 VMII-NXPW5KW")
374+
self.assertEqual(inverter.variant_key, "vmii_nxpw5kw")
375+
self.assertEqual(inverter.details["output_rating_active_power"], 6200)
376+
self.assertEqual(inverter.details["battery_rating_voltage"], 48)
377+
profile = load_driver_profile("pi30_ascii/models/vmii_nxpw5kw.json")
378+
self.assertEqual({c.key for c in inverter.capabilities}, {c.key for c in profile.capabilities})
379+
values = await _read_values(driver, transport, inverter)
380+
self.assertEqual(values["battery_voltage"], 54.6)
381+
self.assertEqual(values["output_active_power"], 227)
382+
self.assertEqual(values["operating_mode"], "Line")
383+
self.assertTrue(all(command.startswith("Q") for command in transport.commands))
384+
354385
async def test_probe_collects_variant_detection_facts(self) -> None:
355386
driver = Pi30Driver()
356387
target = ProbeTarget(devcode=0x0994, collector_addr=0x01, device_addr=0)

0 commit comments

Comments
 (0)