Skip to content

Concepts

Snapshots

A snapshot is a plain JavaScript object containing everything the library knows about the inverter at a point in time. It's built from raw Modbus register values with all GivEnergy protocol quirks applied (scaling, validation, fallbacks).

ts
const snapshot = inverter.getData();
// snapshot is an InverterSnapshot — a discriminated union on `generation`

Snapshots are immutable values — each call to getData() or each 'data' event gives you a new object. The library doesn't track history or compute deltas.

What's in a Snapshot

CategoryFieldsExample
PowersolarPower, batteryPower, gridPower, loadPowerReal-time watts
BatterystateOfCharge, batteryVoltage, batteryCurrent75%, 48.0V, 2.5A
Energy todaypvEnergyTodayKwh, gridImportEnergyTodayKwh, ...Daily totals in kWh
Energy totalpvEnergyTotalKwh, batteryChargeEnergyTotalKwh, ...Lifetime totals in kWh
ConfigecoMode, timedExport, timedCharge, batteryPauseMode, chargeRatePercent, batteryReservePercentCurrent settings
TimeslotschargeSlots, dischargeSlotsScheduled periods
GridgridVoltage, gridFrequencyAC measurements
TemperatureinverterHeatsinkTemp, batteryTemperatureSensor readings in °C
Peripheralsbatteries, metersAttached battery modules and CT meters
DerivedpowerFlowsSolar→house, battery→grid, etc.

Inverter Generations

GivEnergy inverters come in three generations, each with different capabilities:

Gen2Gen3Three-phase
Charge slots1102
Discharge slots2102
Per-slot target SOCNoYesNo
Battery pause modeNoYesNo
Timed dischargeNoYesNo
Export limit controlNoYesNo

The library auto-detects the generation during connect() and returns the appropriate subclass (Gen2Inverter, Gen3Inverter, or ThreePhaseInverter). The snapshot type is a discriminated union on the generation field:

ts
const snapshot = inverter.getData();

if (snapshot.generation === 'gen3') {
  // TypeScript knows this is Gen3Snapshot
  console.log(snapshot.batteryPauseMode);          // 'disabled' | 'pause_charge' | ...
  console.log(snapshot.timedDischargeSlot);         // { start: '23:00', end: '00:01' }
  console.log(snapshot.chargeSlots[0].targetStateOfCharge);
}

Generation detection uses the device type code (HR 0) and ARM firmware version (HR 21). If those registers aren't available, it falls back to serial number prefix detection — but this is unreliable as many serial prefixes (like "FD" for Gen3) aren't in the prefix map.

Mode Toggles

Inverter operating modes are independent toggles, each controlling a single holding register. They are not mutually exclusive — multiple toggles can be active simultaneously.

ToggleRegisterDescription
ecoModeHR(27)Battery charges from solar only, discharges to meet household load
timedExportHR(59)Battery discharges to the grid on a schedule
timedChargeHR(96)Battery charges on a schedule (timeslots)
ts
await inverter.setEcoMode(true);
await inverter.setTimedExport(false);
await inverter.setTimedCharge(true);

Battery Pause Mode (Gen3)

Gen3 inverters have a battery pause mode at HR(318) that controls whether the battery is paused from charging, discharging, or both. This is closely related to timed discharge — setting pause_discharge is how the GivEnergy app enables timed discharge.

Terminology

This library follows the GivEnergy app's terminology. GivTCP calls HR(59) "enable_discharge" and doesn't model timed discharge as a separate feature. We use "timed export" for HR(59) (exporting to the grid) and "battery pause mode" / "timed discharge" for HR(318-320) (controlling battery discharge behaviour), which matches what users see in the app.

ModeValueDescription
disabled0Normal operation — battery charges and discharges freely
pause_charge1Battery will not charge
pause_discharge2Battery only discharges during the timed discharge slot
pause_both3Battery neither charges nor discharges
ts
// Full control over pause mode
await inverter.setBatteryPauseMode('pause_charge');

// Convenience: setTimedDischarge sets pause_discharge / disabled
await inverter.setTimedDischarge(true);  // same as setBatteryPauseMode('pause_discharge')
await inverter.setTimedDischargeSlot({ start: '23:00', end: '00:01' });

Power Flows

The powerFlows field breaks down where energy is flowing right now:

ts
const pf = snapshot.powerFlows;

pf.solarToHouse;    // solar panels → household load
pf.solarToBattery;  // solar panels → battery charging
pf.solarToGrid;     // solar panels → grid export
pf.batteryToHouse;  // battery → household load
pf.batteryToGrid;   // battery → grid export
pf.gridToHouse;     // grid import → household load
pf.gridToBattery;   // grid import → battery charging

All values are in watts. These are derived from the raw power readings using the same allocation logic as GivTCP.

Sign Conventions

Some power readings are signed:

FieldPositiveNegative
batteryPowerDischarging (battery → house/grid)Charging (solar/grid → battery)
gridPowerExporting (house → grid)Importing (grid → house)
inverterOutputPowerGeneratingConsuming

All other power fields (solarPower, loadPower, etc.) are unsigned.

Batteries and Meters

The batteries array contains a snapshot for each attached battery module. For LV (low-voltage) systems, each module reports its own SOC, voltage, cell voltages, temperatures, and charge/discharge totals. For HV (high-voltage) systems, pack-level data comes from the BCU (Battery Control Unit) and per-cell data from individual BMUs.

The meters array contains CT (current transformer) meter data if any are connected. Meters report per-phase voltage, current, power, power factor, frequency, and energy totals.

Stateless Design

This library is stateless — it reads the current inverter state and sends individual register writes. It does not:

  • Track what settings it has changed
  • Automatically revert settings after a timeout
  • Queue or batch writes
  • Maintain history of snapshots

Higher-level workflows (force charge for 2 hours, revert to eco at sunrise, etc.) belong in your application. See the Cookbook for patterns.