Skip to content

Add new generic Sunspec module - #4252

Merged
LKuemmel merged 4 commits into
openWB:masterfrom
seaspotter:sunspec-generic-module
Nov 12, 2026
Merged

LKuemmel merged 4 commits into
openWB:masterfrom
seaspotter:sunspec-generic-module

Conversation

@seaspotter

@seaspotter seaspotter commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

UI: openWB/openwb-ui-settings#1154

Neues generisches SunSpec-Gerät (generic/sunspec) für Wechselrichter, Zähler und Speicher – herstellerunabhängig, im Gegensatz zum bestehenden fronius_sunspec (bleibt unverändert).

Liest das Gerät per SunSpec-Modellkette aus (offizielle Modelldefinitionen von sunspec.org, nicht hartkodierte Adressen) und erkennt automatisch, ob int+SF- oder Float-Modelle vorliegen. Speicher ist aktuell nur Telemetrie (Leistung/SoC über Model 802) – für aktive Steuerung gibt es im Standard kein Modell, das sowohl Telemetrie als auch Regelung abdeckt, ohne auf herstellerspezifische Register zurückzugreifen.

Summary by Sourcery

Introduce a generic SunSpec integration for vendor-independent inverter, meter, and battery telemetry.

New Features:

  • Add a vendor-neutral generic SunSpec device supporting inverter, meter, and battery components over Modbus TCP.
  • Support automatic SunSpec model-chain discovery and selection of standard integer/scale-factor or floating-point model variants.
  • Expose inverter telemetry, meter measurements and energy counters, and battery power and state-of-charge telemetry through the standard component interfaces.

Enhancements:

  • Handle SunSpec scaling factors, floating-point values, manufacturer-specific meter power direction, sentinel values, and incomplete phase data consistently.
  • Keep battery control disabled while providing read-only battery telemetry in accordance with the supported SunSpec model.

Tests:

  • Add unit coverage for SunSpec discovery, point decoding, scaling, sentinel handling, and generic inverter, meter, and battery behavior.

@seaspotter
seaspotter requested a review from ndrsnhs November 6, 2026 12:09
@LKuemmel
LKuemmel requested a balanced review from Copilot and removed request for ndrsnhs November 11, 2026 09:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved battery register offsets, missing-phase handling, and Fronius power-direction errors can produce incorrect telemetry, crash meter updates, and misdirect surplus charging.

5 open issues
What changed in this PR

Adds a manufacturer-independent SunSpec device integration for inverters, meters, and battery telemetry alongside the unchanged Fronius-specific module.

Changes:

  • Introduces shared model-chain discovery and readers for integer-plus-scale-factor and floating-point measurements.
  • Adds configurable Modbus TCP components for inverter, meter, and model 802 battery telemetry; battery control remains unsupported.
  • Adds tests for discovery, decoding, and component updates.
File Description
packages/​modules/​devices/​generic/​sunspec/​inverter.py Reads inverter power and energy.
packages/​modules/​devices/​generic/​sunspec/​inverter_test.py Tests inverter formats and missing-model errors.
packages/​modules/​devices/​generic/​sunspec/​device.py Registers components and coordinates Modbus updates.
packages/​modules/​devices/​generic/​sunspec/​counter.py Reads meter totals and per-phase measurements.
packages/​modules/​devices/​generic/​sunspec/​counter_test.py Tests integer and floating-point meter readings.
packages/​modules/​devices/​generic/​sunspec/​config.py Defines device and component configuration.
packages/​modules/​devices/​generic/​sunspec/​bat.py Reads battery power and charge level.
packages/​modules/​devices/​generic/​sunspec/​bat_test.py Tests battery telemetry and non-controllable behavior.
packages/​modules/​devices/​generic/​sunspec/​__init__.py Marks the device package.
packages/​modules/​common/​sunspec/​reader.py Decodes and scales SunSpec points.
packages/​modules/​common/​sunspec/​reader_test.py Tests decoding, scaling, and unavailable values.
packages/​modules/​common/​sunspec/​points.py Defines supported models and register offsets.
packages/​modules/​common/​sunspec/​discovery.py Discovers model chains and selects supported models.
packages/​modules/​common/​sunspec/​discovery_test.py Tests discovery fallbacks, limits, and model selection.
packages/​modules/​common/​sunspec/​__init__.py Marks the shared SunSpec package.

Review effort: Balanced

@seaspotter

Copy link
Copy Markdown
Collaborator Author

@LKuemmel alle Copilot Review Kommentare sind gefixt.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Active moderate findings show that valid SunSpec sentinel values can flow into arithmetic and peak-filter logic as None, causing TypeError instead of explicit invalid-update handling.

3 open issues
5 resolved since last review

Review effort: Lite

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Three active moderate findings can cause unavailable SunSpec readings to publish None and crash downstream battery, frequency, or PV aggregation instead of preserving safe defaults or simulation values.

No open issues 🎉

3 previously missed issues

In code that hasn't changed since last review

Medium severity Handle unavailable battery SoC before building BatState

packages/​modules/​devices/​generic/​sunspec/​bat.py:59

read_point returns None for the standard SoC not-implemented sentinel, but this value is passed directly into BatState. bat_all.calc_power_for_all_components unconditionally adds each battery's soc, so configuring a device that reports an unavailable SoC makes the aggregate update raise TypeError instead of publishing a usable state (the generic Modbus battery leaves the default SoC at 0 when its optional read is absent). Normalize the missing value before constructing BatState (or otherwise mark the component faulty).

Medium severity Keep unavailable frequency readings from becoming None

packages/​modules/​devices/​generic/​sunspec/​counter.py:74

read_point() deliberately converts an unimplemented Hz sentinel to None, but this value is passed explicitly into CounterState and reaches consumers that multiply/compare the frequency. An unavailable optional reading should leave the state default (50 Hz), or be rejected like the mandatory power reading; do not publish frequency=None.

Medium severity Use the simulated export counter when WH is unavailable

packages/​modules/​devices/​generic/​sunspec/​inverter.py:55

read_point maps an unsupported/NaN SunSpec WH value to None, but the simulated exported value returned by sim_count is discarded here. That publishes InverterState.exported=None; control/pv_all.py later adds this field to the PV total and aborts aggregation. Use the simulated exported counter when the device does not provide WH (as the generic HTTP inverter does).

3 resolved since last review

Review effort: Lite

@LKuemmel
LKuemmel merged commit 70709a1 into openWB:master Nov 12, 2026
2 checks passed
@seaspotter
seaspotter deleted the sunspec-generic-module branch November 12, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants