Skip to content

Adding Sensors to an Existing Profile

Use this guide when you want to expose a register that is already present in the inverter's Modbus map but not yet appearing in Home Assistant — either because the register was never wired up, or because a firmware update added new data.

If your inverter model is not supported at all, see Creating a New Profile first.


Before you start

Check that the register actually responds on your hardware using the diagnostic scanner (growatt_modbus.read_register service or the Universal Register Scanner). Many registers listed in the protocol spec are model-specific or firmware-gated and return 0 or error on hardware that doesn't support them. Confirm a live non-zero reading before wiring up a sensor.

Also search the codebase to make sure the sensor doesn't already exist under a different name:

grep -r "your_register_name" custom_components/growatt_modbus/

Six steps — all required

Step 1 — Add the register definition

File: custom_components/growatt_modbus/profiles/<family>.py

Add an entry to input_registers or holding_registers:

# Single 16-bit register
93: {'name': 'inverter_temp', 'scale': 0.1, 'unit': '°C', 'signed': True},

# 32-bit value — both words required, pointing at each other via 'pair'
1: {'name': 'pv_total_power_high', 'scale': 1, 'unit': '', 'pair': 2},
2: {'name': 'pv_total_power_low',  'scale': 1, 'unit': '', 'pair': 1,
    'combined_scale': 0.1, 'combined_unit': 'W'},

Key fields:

Field Required Notes
name Yes Snake-case string. Must match the GrowattData field name exactly.
scale Yes Multiplier for the raw 16-bit value. Check the protocol spec carefully — scale errors produce values 10× or 100× off.
unit Yes Human-readable string ('V', 'W', 'kWh', '%', '' for dimensionless).
signed No True for registers that hold signed (two's complement) values — e.g. battery current, temperature. Omitting this causes large negative readings to appear as huge positive numbers.
pair No Address of the other word in a 32-bit pair. Both words must have pair set.
combined_scale No Applied after combining high/low words into a 32-bit integer. Usually on the _low register. Can be negative to invert sign (SPF battery convention).
combined_unit No Unit string for the combined value.
desc No Free text — useful for scan evidence references.

VPP shared blocks: If the register address is in the 30000–31999 range and the same register exists identically across multiple families, check profiles/vpp_v201.py first. If it already defines the register, your profile gets it automatically via the **VPP_V201_* unpacking — no duplicate definition needed.


Step 2 — Add a field to GrowattData

File: custom_components/growatt_modbus/growatt_modbus.py

Add a field to the @dataclass GrowattData class:

@dataclass
class GrowattData:
    # ... existing fields ...

    inverter_temp: float = 0.0       # °C — inverter heat sink temperature
    battery_soc:   float = 0.0       # % — state of charge
    fault_code:    int   = 0         # raw fault register value
    serial_number: str   = ""        # firmware version string

This step is the most commonly forgotten. Without the field, hasattr(data, 'your_sensor_name') returns False and any condition on that sensor silently fails — the sensor never appears in HA.

Type guidelines: - float = 0.0 — voltages, currents, power, energy, temperature, percentages - int = 0 — status codes, counts, mode selectors, raw bitfields - str = "" — text fields such as firmware version or serial number

Group the new field with similar fields (battery fields together, PV fields together) rather than appending at the end.


Step 3 — Add a sensor definition

File: custom_components/growatt_modbus/sensor.py

First check whether a template helper already covers your sensor. The file contains _pv_string_sensors(n) and _phase_sensors(n) that generate uniform entries for PV strings and three-phase AC sensors automatically. If you are adding pv4_voltage, pv4_current, pv4_power — they are generated, not hand-written. Search for the grep index comment at the top of the file.

For everything else, add an entry to SENSOR_DEFINITIONS:

"battery_soc": {
    "name": "Battery State of Charge",
    "icon": "mdi:battery",
    "device_class": SensorDeviceClass.BATTERY,
    "state_class": SensorStateClass.MEASUREMENT,
    "unit": PERCENTAGE,
    "attr": "battery_soc",          # Must match GrowattData field name exactly
},

Optional fields:

"bms_max_cell_volt": {
    ...
    "entity_category": EntityCategory.DIAGNOSTIC,   # Hides from main UI
    "condition": lambda data: data.bms_max_cell_volt > 0,  # Suppress if register absent
},

Common device classes and their canonical units:

Measurement device_class unit
Voltage SensorDeviceClass.VOLTAGE UnitOfElectricPotential.VOLT
Current SensorDeviceClass.CURRENT UnitOfElectricCurrent.AMPERE
Power SensorDeviceClass.POWER UnitOfPower.WATT
Energy SensorDeviceClass.ENERGY UnitOfEnergy.KILO_WATT_HOUR
Temperature SensorDeviceClass.TEMPERATURE UnitOfTemperature.CELSIUS
Frequency SensorDeviceClass.FREQUENCY UnitOfFrequency.HERTZ
Battery % SensorDeviceClass.BATTERY PERCENTAGE

Step 4 — Assign to a device

File: custom_components/growatt_modbus/const.py

Add the sensor key to the correct set in SENSOR_DEVICE_MAP:

SENSOR_DEVICE_MAP = {
    DEVICE_TYPE_SOLAR:    { ..., "pv3_voltage", ... },
    DEVICE_TYPE_GRID:     { ..., "grid_export_power", ... },
    DEVICE_TYPE_LOAD:     { ..., "house_consumption", ... },
    DEVICE_TYPE_BATTERY:  { ..., "battery_soc", ... },
    DEVICE_TYPE_INVERTER: { ..., "inverter_temp", ... },
}

Assignment guidelines:

Device What belongs here
DEVICE_TYPE_SOLAR PV string voltage/current/power, AC output, solar energy totals
DEVICE_TYPE_GRID Grid import/export power, grid voltage/frequency
DEVICE_TYPE_LOAD House consumption, load power
DEVICE_TYPE_BATTERY Battery voltage/current/SOC/temperature/power, BMS data
DEVICE_TYPE_INVERTER Status, fault codes, temperatures, diagnostics

Step 5 — Add to a sensor group

File: custom_components/growatt_modbus/device_profiles.py

Profiles compose their sensor set from sensor group constants. Add the sensor key to the appropriate group:

BATTERY_SENSORS: Set[str] = {
    "battery_voltage", "battery_current", "battery_soc",
    "battery_temp", "battery_power",
    "your_new_sensor",   # ← add here
    ...
}

Available sensor groups:

Group Contents
BASIC_PV_SENSORS PV1/PV2 voltage, current, power; total PV power
PV3_SENSORS PV3 voltage, current, power, energy
BASIC_AC_SENSORS Single-phase AC voltage, current, power, frequency
THREE_PHASE_SENSORS R/S/T phase voltages, currents, powers; line voltages
GRID_SENSORS grid_power, grid_export_power, grid_import_power
POWER_FLOW_SENSORS power_to_grid, power_to_load, power_to_user
CONSUMPTION_SENSORS house_consumption, self_consumption
ENERGY_SENSORS energy_today, energy_total
ENERGY_BREAKDOWN_SENSORS Grid/load energy today and total
PV_DC_ENERGY_SENSORS pv_energy_total, per-string energy today
PV_MPPT_TOTAL_SENSORS pv1_energy_total, pv2_energy_total (lifetime, per-MPPT)
BATTERY_SENSORS SOC, voltage, current, power, charge/discharge energy
BMS_SENSORS BMS status, error codes, cell voltage extremes
TEMPERATURE_SENSORS inverter_temp, ipm_temp, boost_temp
STATUS_SENSORS status, fault_code, warning_code, derating_mode
SPF_OFFGRID_SENSORS Off-grid specific (load %, AC input, generator, fans)
SPE_OFFGRID_SENSORS SPE 8000–12000 ES subset of SPF sensors
WIT_EXTRA_SENSORS WIT multi-inverter extra output sensors

The composite groups (HYBRID_1P_SENSORS, HYBRID_3P_SENSORS, GRID_TIED_1P_SENSORS) are unions of the above — if you add to any base group, the composites automatically include the new sensor.

If no existing group fits, add the sensor to an existing group with a comment, or — only if genuinely a new category — define a new group and add it to the relevant composite.


Step 6 — Validate and syntax-check

# Validate the specific sensor across all files
python validate_sensors.py --sensor your_sensor_name

# Syntax-check the files you touched
python -m py_compile custom_components/growatt_modbus/profiles/*.py
python -m py_compile custom_components/growatt_modbus/growatt_modbus.py
python -m py_compile custom_components/growatt_modbus/sensor.py
python -m py_compile custom_components/growatt_modbus/const.py

# Confirm the sensor name appears in all expected places
grep -r "your_sensor_name" custom_components/growatt_modbus/

The validator checks: - Register defined in at least one profile - Key present in SENSOR_DEFINITIONS - Key present in SENSOR_DEVICE_MAP - Key present in at least one sensor group


Common mistakes

Symptom Likely cause
Sensor never appears in HA Forgot Step 2 — GrowattData field missing
Sensor appears but always unavailable attr in SENSOR_DEFINITIONS doesn't match register name or GrowattData field name
Sensor appears in wrong device Wrong assignment in SENSOR_DEVICE_MAP (Step 4)
Sensor defined but not in any profile Forgot Step 5 — not added to any sensor group
Values 10× too high or too low Wrong scale or combined_scale in register definition
Large positive value instead of negative Missing 'signed': True on the register
32-bit value wrong pair pointing to wrong address, or combined_scale on the wrong word

Quick reference

What you're changing File
Register address, scale, unit, sign profiles/<family>.py
Data field (required — most often forgotten) growatt_modbus.py
Display name, icon, device class, unit sensor.pySENSOR_DEFINITIONS
Which sub-device the sensor appears under const.pySENSOR_DEVICE_MAP
Which profiles include the sensor device_profiles.py — sensor group constants