A Python wrapper for the Huawei Inverter modbus TCP API
This library implements an easy to use interface to locally connect to Huawei SUN2000 inverters over Modbus-TCP or Modbus-RTU following the 'Solar Inverter Modbus Interface Definitions' provided by Huawei.
It was primarily developed to add support for Huawei Solar inverters to Home Assistant, resulting in the following integration: wlcrs/huawei_solar.
Features:
SUN2000-<serial_no>
)
broadcasted by the inverterNote t
This library is published on PyPI:
pip3 install huawei-solar
The library consists out of a low level interface implemented in huwei_solar.py which implements all the Modbus-operations, and a high level interface in bridge.py which facilitates easy usage (primarily meant for the HA integration).
An example on how to read the most interesting registers from the inverter:
bridge = await HuaweiSolarBridge.create(host="192.168.200.1", port=6607)
print(await bridge.update())
This results in the following output being printed:
{'input_power': Result(value=82, unit='W'), 'line_voltage_A_B': Result(value=233.4, unit='V'), 'line_voltage_B_C': Result(value=0.0, unit='V'), 'line_voltage_C_A': Result(value=0.0, unit='V'), 'phase_A_voltage': Result(value=247.2, unit='V'), 'phase_B_voltage': Result(value=0.3, unit='V'), 'phase_C_voltage': Result(value=0.0, unit='V'), 'phase_A_current': Result(value=0.408, unit='A'), 'phase_B_current': Result(value=0.0, unit='A'), 'phase_C_current': Result(value=0.0, unit='A'), 'day_active_power_peak': Result(value=2407, unit='W'), 'active_power': Result(value=70, unit='W'), 'reactive_power': Result(value=-1, unit='VA'), 'power_factor': Result(value=1.0, unit=None), 'grid_frequency': Result(value=50.02, unit='Hz'), 'efficiency': Result(value=100.0, unit='%'), 'internal_temperature': Result(value=24.4, unit='°C'), 'insulation_resistance': Result(value=30.0, unit='MOhm'), 'device_status': Result(value='On-grid', unit=None), 'fault_code': Result(value=0, unit=None), 'startup_time': Result(value=datetime.datetime(2022, 11, 18, 9, 2, 40, tzinfo=datetime.timezone.utc), unit=None), 'shutdown_time': Result(value=None, unit=None), 'accumulated_yield_energy': Result(value=3515.62, unit='kWh'), 'daily_yield_energy': Result(value=0.12, unit='kWh'), 'state_1': Result(value=['Grid-Connected', 'Grid-Connected normally'], unit=None), 'state_2': Result(value=['Locked', 'PV connected', 'DSP data collection'], unit=None), 'state_3': Result(value=['On-grid', 'Off-grid switch disabled'], unit=None), 'alarm_1': Result(value=[], unit=None), 'alarm_2': Result(value=[], unit=None), 'alarm_3': Result(value=[], unit=None), 'pv_01_voltage': Result(value=287.8, unit='V'), 'pv_01_current': Result(value=0.0, unit='A'), 'pv_02_voltage': Result(value=0.0, unit='V'), 'pv_02_current': Result(value=0.0, unit='A'), 'nb_online_optimizers': Result(value=10, unit=None), 'grid_A_voltage': Result(value=234.1, unit='V'), 'grid_B_voltage': Result(value=234.1, unit='V'), 'grid_C_voltage': Result(value=233.1, unit='V'), 'active_grid_A_current': Result(value=-0.48, unit='I'), 'active_grid_B_current': Result(value=-0.46, unit='I'), 'active_grid_C_current': Result(value=-0.56, unit='I'), 'power_meter_active_power': Result(value=-151, unit='W'), 'power_meter_reactive_power': Result(value=187, unit='Var'), 'active_grid_power_factor': Result(value=-0.428, unit=None), 'active_grid_frequency': Result(value=50.0, unit='Hz'), 'grid_exported_energy': Result(value=1705.65, unit='kWh'), 'grid_accumulated_energy': Result(value=1048.0, unit='kWh'), 'grid_accumulated_reactive_power': Result(value=0.0, unit='kVarh'), 'meter_type': Result(value=<MeterType.THREE_PHASE: 1>, unit=None), 'active_grid_A_B_voltage': Result(value=405.3, unit='V'), 'active_grid_B_C_voltage': Result(value=404.6, unit='V'), 'active_grid_C_A_voltage': Result(value=404.6, unit='V'), 'active_grid_A_power': Result(value=-72, unit='W'), 'active_grid_B_power': Result(value=-71, unit='W'), 'active_grid_C_power': Result(value=-7, unit='W'), 'storage_state_of_capacity': Result(value=22.0, unit='%'), 'storage_running_status': Result(value=<StorageStatus.RUNNING: 2>, unit=None), 'storage_bus_voltage': Result(value=454.2, unit='V'), 'storage_bus_current': Result(value=0.0, unit='A'), 'storage_charge_discharge_power': Result(value=12, unit='W'), 'storage_total_charge': Result(value=1094.26, unit='kWh'), 'storage_total_discharge': Result(value=1049.3, unit='kWh'), 'storage_current_day_charge_capacity': Result(value=0.39, unit='kWh'), 'storage_current_day_discharge_capacity': Result(value=0.15, unit='kWh')}
Example code:
from huawei_solar import AsyncHuaweiSolar, register_names as rn
slave_id = 0
client = await AsyncHuaweiSolar.create("192.168.200.1", 6607, slave_id)
# Reading a single register
result = await bridge.client.get(rn.NB_PV_STRINGS, slave_id)
print("Number of PV strings: ", result.value)
# Batched reading of multiple registers
# Only possible when they are located closely to each other in the Modbus register space
results = await self.client.get_multiple([rn.LINE_VOLTAGE_A_B, rn.LINE_VOLTAGE_B_C, rn.LINE_VOLTAGE_C_A], self.slave_id)
print("A-B voltage: ", results[0].value)
print("B-C voltage: ", results[1].value)
print("C-A voltage: ", results[2].value)
A good starting point to learn how to use the low level interface is to look at how the high level interface in bridge.py uses it.
The initial implementation of v1 was done by @Emilv2.
Subsequent developement on v2 was done by @wlcrs.