Skip to content

Multiple inverters and sources ​

The plant registers are the plant totals, read on the plant unit (247). A Sigenergy system can have more behind those totals: more than one inverter, several PV strings per inverter, AC-coupled third-party PV, EV chargers, and a generator. The protocol separates these by Modbus unit ID, not by register offset; each device answers on its own slave ID over the same TCP connection. The plant unit aggregates them (its pvPower already sums every inverter, its loadPower already includes any EV charger), so the dashboard's panels stay correct no matter how many devices are present. What the plant totals don't give you is the breakdown, and that's what server/devices.js adds.

Inverter discovery and reads ​

On each connect the bridge sweeps unit IDs 1–4, reading the inverter model string (30500) with a short timeout; any that answer are recorded as inverters (server/devices.js, discoverInverters). It's the same read-only fingerprint the gateway scan uses, just against slave IDs instead of hosts, and it costs about a second of extra connect time on a single-inverter system. Every poll then reads each discovered inverter on its own unit ID: running state (30578), active power (30587), PCS temperature (31003), the per-inverter SoC and SoH (30601/30602), and the PV strings. The string count comes from 31025; the bridge reads the contiguous voltage/current block from 31027 (up to four strings, one per MPP tracker on a residential SigenStor) and computes each string's DC power as volts times amps. The result lands in state.devices and rides the existing SSE stream and /api/snapshot (the devices array); the plant totals are untouched, so this is purely additive. Each string also carries a name, String N by default and renamable in Settings → Solar; like the Smart Port names below, the label is stamped server-side on every poll (server/modbus.js) so it streams to every client live with no restart. Because a string's index is only positional within its inverter, the names are keyed per inverter serial (solar.stringNames is { [serial]: { [index]: name } }), so a second inverter never collides on String 1.

Smart Port loads ​

The gateway's Smart Port loads ride the same array. The plant unit publishes up to 24 smart-load slots as contiguous register blocks, live power (30146, signed watts) and lifetime energy (30098, ÷100 kWh), which the bridge bulk-reads in two requests per poll (readSmartLoads in server/devices.js). A slot counts as a real device once its lifetime counter or live power is non-zero, so unused slots never appear and a newly commissioned load shows up the moment it first draws. The protocol carries no names and no relay state: loads are named in Settings → Smart Port (defaulting to Smart load N, applied live on the next poll), and a load reading zero watts is shown as Idle rather than off, because "relay open" and "on but not drawing" are indistinguishable over Modbus. There's no write path either; the published protocol has no register for switching a Smart Port load, so control stays with the mySigen app and the gateway's own schedules. Those schedules run on the gateway itself, which means a Smart Port load keeps working, and these readings keep flowing, when the internet is down. Each poll also sums the detected loads into a plant-wide smartPortPower (null while none are detected), which streams to the dashboard, appears in /api/snapshot as power.smartPort, and is recorded with every history sample; the same settings section can surface it across the dashboard (see Dashboard internals).

What isn't read yet ​

The scope is deliberately narrow. Only inverters and Smart Port loads are read today, and only verified against a single inverter and a single smart load; multi-inverter plants, third-party PV as its own node, EV AC and DC chargers (the DC module is bidirectional and reports vehicle-to-home and vehicle-to-grid power), and generators all exist in Sigenergy's published map but aren't read here yet. The plant totals already fold third-party PV into pvPower and EV charging into loadPower, so nothing is missing from the headline figures; what's missing is the per-device detail, which is the obvious thing to add in a fork if your system has that hardware.

MIT licensed. Not affiliated with or endorsed by Sigenergy, Apple, Google, or Cloudflare.