Sungrow SH10.RT Modbus integration

additional info, just have a look to the sensor parameter… the “Battery discharging power” has a value of 0, the “Battery power raw” shows the right value

I have this issue too — on/off indicators are all off and some of the sensors are displaying 0. I also have the problem with running_state being 0 and Sungrow inverter state being “Unknown - should not see me!”

Did anyone have any luck figuring out how to get these working? I’ve tried a couple of things in this thread but nothing has fixed it yet. SH6.0RS inverter and SBR096 battery

Oh my god … have found the inconsistency :frowning:
I have configured the wrong Lan port, it seems that the one on WinNetS can also do a little bit ModBus, but does not deliver everything. After the changeover, I now receive correct values.

The data for the energy display is also correct if you understand that the daily values are displayed there. I was of the (stupid) opinion that the flow is displayed there :frowning:
Just learning how homeassitant is working :wink:

most of the “some sensors are not correct”-issues are related to not using the internal LAN port.

The Ethernet by WiNetS only provides a subset of the functions (same with the WLAN).

So use the internal Ethernet port of the Inverter (NOT the WinetS).

btw. there are currently some issues with the new HA version 2023.9 and pymodbus (which this integration relys on). In some cases this leads to a stopped communication after some minutes.

If you are affected, downgrade to 2023.8.

If you are still on <= 2023.8: Stay, until the github message says “cleared” :slight_smile:

I just updated the description in the github to clarify more :slight_smile:

1 Like

As I also couldn’t find any entity for the total energy used by the complete load / all devices / my home (whatever you wanna call it).
Do you calculate it on your own now (how exactly)?

For the current value (W instead of kWh) we have the power consumption as entity “Load power”.

Before I installed the solar panels, the used energy of the house was simply the value on the smart meter of my grid provider, and it was always a good indicator to see if I use more or less energy over time and if there’s room for improvement or savings.
So I want to implement this again, even if the smart meter can’t provide it anymore because of the savings of the solar installation.

I think I need new template sensors like this, maybe someone with more experience in solar stuff can tell me if I understand the values correctly:

sensor.total_exported_energy_from_battery = sensor.total_exported_energy - sensor.total_exported_energy_from_pv

sensor.total_direct_battery_consumption = sensor.total_battery_discharge - sensor.total_exported_energy_from_battery

sensor.total_load_energy = sensor.total_imported_energy + sensor.total_direct_energy_consumption + sensor.total_direct_battery_consumption

@mkai if this makes sense, do you think this would be useful for others too and you might add it to your repo?
Otherwise I add it to my local config …

Thanks, everyone

Does this integration also work with SGx.0RT inverters?

Partly.
Some things do not work. The SG inverter uses partly other registers and you don’t need the battery section.
Here is the code for my SG. The code is based on the code from @mkai.

modbus:
  - name: WR2_Sungrow_SG6.0RT
    type: tcp
    host: !secret sungrow_wr2_modbus_host_ip
    port: !secret sungrow_wr2_modbus_port
    retries: 10

    sensors:
      - name: WR2_Sungrow device type code
        unique_id: wr2_dev_code
        slave: !secret sungrow_wr2_modbus_slave
        address: 4999 # reg 5000
        input_type: input
        
        data_type: uint16
        
        scan_interval: 600

      - name: WR2_Inverter temperature
        unique_id: wr2_inverter_temperature
        slave: !secret sungrow_wr2_modbus_slave
        address: 5007 # reg 5008
        input_type: input
        
        data_type: int16
        precision: 1
        unit_of_measurement: °C
        device_class: Temperature
        scale: 0.1
        scan_interval: 10

      - name: WR2_MPPT1 voltage
        unique_id: wr2_mppt1_voltage
        slave: !secret sungrow_wr2_modbus_slave
        address: 5010 # reg 5011
        input_type: input
        
        data_type: uint16
        
        precision: 1
        unit_of_measurement: V
        device_class: Voltage
        scale: 0.1
        scan_interval: 10

      - name: WR2_MPPT1 current
        unique_id: wr2_mppt1_current
        slave: !secret sungrow_wr2_modbus_slave
        address: 5011 # reg 5012
        input_type: input
        
        data_type: uint16
        
        precision: 2
        unit_of_measurement: A
        device_class: Current
        scale: 0.1
        scan_interval: 10

      - name: WR2_MPPT2 voltage
        unique_id: wr2_mppt2_voltage
        slave: !secret sungrow_wr2_modbus_slave
        address: 5012 # reg 5013
        input_type: input
        
        data_type: uint16
        
        precision: 1
        unit_of_measurement: V
        device_class: Voltage
        scale: 0.1
        scan_interval: 10

      - name: WR2_MPPT2 current
        unique_id: wr2_mppt2_current
        slave: !secret sungrow_wr2_modbus_slave
        address: 5013 # reg 5014
        input_type: input
        
        data_type: uint16
        
        precision: 2
        unit_of_measurement: A
        device_class: Current
        scale: 0.1
        scan_interval: 10

      - name: WR2_Total DC power
        unique_id: wr2_total_dc_power
        slave: !secret sungrow_wr2_modbus_slave
        address: 5016 # reg 5017
        input_type: input
        
        data_type: uint32
        swap: word
        precision: 0
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        scale: 1
        scan_interval: 10

      - name: WR2_Grid frequency
        unique_id: wr2_grid_frequency
        slave: !secret sungrow_wr2_modbus_slave
        address: 5035 # reg 5036
        input_type: input
        
        data_type: uint16
        
        precision: 2
        unit_of_measurement: "Hz"
        device_class: frequency
        state_class: measurement
        scale: 0.1
        scan_interval: 10

      - name: WR2_Reactive power
        unique_id: wr2_reactive_power
        slave: !secret sungrow_wr2_modbus_slave
        address: 5032 # reg 5033
        input_type: input
        
        data_type: int32
        swap: word
        precision: 0
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        scale: 1
        scan_interval: 10

      - name: WR2_Power factor
        unique_id: wr2_power_factor
        slave: !secret sungrow_wr2_modbus_slave
        address: 5034 # reg 5035
        input_type: input
        data_type: int16
        precision: 3
        unit_of_measurement: "%"
        device_class: power_factor
        state_class: measurement
        scale: 0.001
        scan_interval: 10

      - name: WR2_Meter active power raw
        unique_id: wr2_meter_active_power_raw
        slave: !secret sungrow_wr2_modbus_slave
        address: 5600 # reg 5601
        input_type: input
        
        data_type: int32
        swap: word
        precision: 0
        unit_of_measurement: W
        device_class: power
        state_class: measurement
        scale: 1
        scan_interval: 10

      - name: WR2_BDC rated power
        unique_id: wr2_bdc_rated_power
        slave: !secret sungrow_wr2_modbus_slave
        address: 5627 # reg 5628
        input_type: input
        
        data_type: uint16
        
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        scale: 100
        scan_interval: 600

      - name: WR2_System state
        unique_id: wr2_system_state
        slave: !secret sungrow_wr2_modbus_slave
        address: 5037 # reg 5038
        input_type: input
        
        data_type: uint16
        
        precision: 0
        scale: 1
        scan_interval: 10

      # register running state is not available for certain SH*RS inverters
      # template sensors are used to determine the states based on other sensors
      - name: WR2_Running state
        unique_id: wr2_running_state
        slave: !secret sungrow_wr2_modbus_slave
        address: 5038 # reg 5039
        input_type: input
        
        data_type: uint16
        
        precision: 0
        scale: 1
        scan_interval: 10

      - name: WR2_Daily PV generation
        unique_id: wr2_daily_pv_generation
        slave: !secret sungrow_wr2_modbus_slave
        address: 5002 # reg 5003
        input_type: input
        
        data_type: uint16
        
        precision: 1
        unit_of_measurement: kWh
        device_class: energy
        state_class: total_increasing
        scale: 0.1
        scan_interval: 600

      - name: WR2_Total PV generation
        unique_id: wr2_total_pv_generation
        slave: !secret sungrow_wr2_modbus_slave
        address: 5003 # reg 5004
        input_type: input
        
        data_type: uint32
        swap: word
        precision: 1
        unit_of_measurement: kWh
        device_class: energy
        state_class: total

        scan_interval: 600


# 'virtual' template sensors for better readability
template:
  - binary_sensor:
      - name: WR2_PV generating
        unique_id: wr2_pv_generating
        availability: >-
          {{states('sensor.wr2_running_state')|is_number or 
            states('sensor.wr2_total_dc_power')|is_number
          }}
        delay_on:
          seconds: 60
        state: >-
          {% if states('sensor.wr2_running_state')|is_number %}
            {# use available sensor running_state #}
            {{ states('sensor.wr2_running_state')|int(default=0)|bitwise_and(0x1) > 0 }}
          {% else %} 
            {# workaround for SH*RS inverters without working running_state #}
            {% if states('sensor.wr2_total_dc_power')|int > 0 %}
              1
            {% else %} 
              0 
            {% endif %}
          {% endif %}


  - sensor:
      - name: WR2_MPPT1 power
        unique_id: wr2_mppt1_power
        unit_of_measurement: W
        device_class: power
        availability: "{{states('sensor.wr2_mppt1_voltage')|is_number and states('sensor.wr2_mppt1_current')|is_number }}"
        state: "{{ (states('sensor.wr2_mppt1_voltage') | float * states('sensor.wr2_mppt1_current') | float) |int }}"

      - name: WR2_MPPT2 power
        unique_id: wr2_mppt2_power
        unit_of_measurement: W
        device_class: power
        availability: "{{states('sensor.wr2_mppt2_voltage')|is_number and states('sensor.wr2_mppt2_current')|is_number }}"
        state: "{{ (states('sensor.wr2_mppt2_voltage') | float * states('sensor.wr2_mppt2_current') | float) |int }}"

      - name: WR2_Sungrow inverter state
        unique_id: wr2_inverter_state
        state: >-
          {% if ((states('sensor.wr2_system_state') | int(default=0)) == 0x8000) %}
            Stop
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1400) %}
            Standby
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1200) %}
            Initial Standby
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1600) %}
            Startup
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x0) %}
            Running
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x5500) %}
            Fault
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1500) %}
            Emergency Stop
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1200) %}
            Initial standby
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x9100) %}
            Alarm run
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x8100) %}
            Derating run
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x8200) %}
            Dispatch run
          {% elif ((states('sensor.wr2_system_state') | int(default=0)) == 0x1300) %}
            Key stop
          {% else %}
            Unknown - should not see me!
          {% endif %}

      - name: WR2_Sungrow device type
        unique_id: wr2_device_type
        state: >-
          {% if ((states('sensor.wr2_sungrow_device_type_code') | int(default=0))  == 0x243D) %}
            SG3.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x243E) %}
            SG4.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2430) %}
            SG5.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2431) %}
            SG6.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x243C) %}
            SG7.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2432) %}
            SG8.0RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2433) %}
            SG10RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2434) %}
            SG12RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2435) %}
            SG15RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2436) %}
            SG17RT
          {% elif ((states('sensor.wr2_sungrow_device_type_code') | int(default=0)) == 0x2437) %}
            SG20RT
          {% else %}
            Unknown device code!
          {% endif %}

input_select:
  wr2_set_inverter_run_mode:
    name: WR2_Inverter mode
    options:
      - "Enabled"
      - "Shutdown"

# Automations: Write modbus registers on input changes via GUI
# note: If you change a value by the sliders, it will take up to 60 seconds until the state variables are updated
# Unfortunately, I could not find a way to "force update" modbus registers, yet...
automation:
  - id: "automation_wr2_sungrow_inverter_state"
    alias: "sungrow wr2 inverter state"
    description: "Enables/ stops the inverter"
    trigger:
      - platform: state
        entity_id:
          - input_select.wr2_set_inverter_run_mode
    condition: []
    variables:
      sg_start: 0xCF
      sg_stop: 0xCE
    action:
      - service: modbus.write_register
        data_template:
          hub: WR2_Sungrow_SG6.0RT
          slave: !secret sungrow_wr2_modbus_slave
          address: 12999 # reg 13000
          value: >
            {% if is_state('input_select.wr2_set_inverter_run_mode', 'Enabled') %}
            {{sg_start}}
            {% else %}
            {{sg_stop}}
            {% endif %}
    mode: single

  - id: "automation_wr2_sungrow_inverter_state_input_selector_update"
    alias: "sungrow wr2 inverter enable/ stop input selector update"
    description: "Updates enable/ stops input selector"
    trigger:
      - platform: state
        entity_id:
          - sensor.wr2_system_state
    condition: []
    action:
      - service: input_select.select_option
        target:
          entity_id: input_select.wr2_set_inverter_run_mode
        data:
          option: >
            {% if is_state('sensor.wr2_sungrow_inverter_state', 'Stop') %}
            "Shutdown"
            {% else %}
            "Enabled"
            {% endif %}
    mode: single

1 Like

I am planning to provide some auto-generated yaml-files with different config settings in the future (using github actions for automated copy/pasting). I guess that will take 1-2 month, depending on my schedule…

Just try to add the integration. Most Registers for SG* work also for SH* inverters.

@basti242 : do you have a list of differences at hand? I don’t have the time at the moment to manually diff the modbus register description from sungrow

1 Like

@Thyraz

will check it, soon. Sorry, not much time at the moment :slight_smile:
but it totally makes sense to integrate a template sensor for this!

I use the Home Assistant Energy Dashboard to get the house load in energy/kWh

The Energy Dashboard uses these sensors as input:

Total imported energy
Total exported energy

Total PV generation

Total battery discharge
Total battery charge

I assume:
total_load_energy = Total PV generation - Total exported energy + Total imported energy - Total battery charge + Total battery discharge

@mkai
Tomorow I will create a list for you.

Hi,
am I the only one having issues with modbus and 2023.10.0?
After updating the sensors are “unavailable” again… :upside_down_face:

Invalid config for [modbus]: BDC rated power: count: 1 cannot be combined with data_type: uint16 @ data[‘modbus’][0][‘sensors’][19]. Got {‘name’: ‘BDC rated power’, ‘unique_id’: ‘sg_bdc_rated_power’, ‘slave’: 1, ‘address’: 5627, ‘input_type’: ‘input’, ‘count’: 1, ‘data_type’: ‘uint16’, ‘swap’: ‘none’, ‘unit_of_measurement’: ‘W’, ‘device_class’: ‘power’, ‘state_class’: ‘measurement’, ‘scale’: 100, ‘scan_interval’: 600} BMS max. charging current: count: 1 cannot be combined with data_type: uint16 @ data[‘modbus’] …

I found this issue maybe related to my problem:

Any ideas? Shall I downgrade?

Please update your modbus script to the latest version

1 Like

Have a look at the corresponding github page…

I fixed it one week ago:

Yes, I know it is annoying that the HA modbus protocol changed very much over the last weeks, but all changes were good and justified and resulted in a clearer code of this sungrow integration.

1 Like

:laughing: Sorry for that. Thank you very much for your great work and support! :pray:

Here you will find a small list. It is not so much.
The codes for Inverter state are also different. I will add this later the day.

1 Like

thank you,

I will integrate that into version 2 (experimental branch), in a couple of weeks

1 Like

Hi all,

Thanks for all your work on this @mkai!

I’m preparing to do this integration for a friend, can you help me understand what the difference is between this integration and @mvandersteen’s one here: GitHub - mvandersteen/SungrowInverter: Allows to pull data from a Sungrow residential inverter via Modbus TCP

The documentation here is a lot better… I’m new to Home Assistant and I’m not sure how to install his.

Hey all :wave:

I’ll be having a SH10RS installed shortly (within the month), and I want to make sure the installers use the right comms cables for HA monitoring. In this model, COM1 is for the WiNet-S module, while COM2 has 2 RJ45 ports (see attached images)

Which port should the installers run to my switch for home assistant to read from?

Side note - they will also be using a Sungrow energy monitor, and there will be Sungrow batteries too - so not 100% if those other devices will utilise those COM2 ports.

that depends :slight_smile:

mvandersteens integration requires some more setup to do and is based on python / HA addon

I wrote the integration in a way that it is self-contained in one .yaml file (and some configurations in the secrets.yaml) and does not require any additional stuff…

What inverter / battery will be installed?

1 Like

make sure to have an Ethernet / LAN cable attached to the inverter (NOT! the WiNet-S stick). Then you will have the most stable modbus connection possible.

image