Universal Solar Inverter over Modbus RS485 / TCP custom_component (Growatt, Sofar, SolaX, Solis)

Anyone with Solax X1-Hybrid Generation 4? I would like to know If the Modbus RTU works on these new models.

Hello I have recently bought a Solax X1 hybrid, Im looking to connect my own HV battery so i need a capture of the CAN-bus on start-up, is there anyone here that can help? Thanks

Wills, after the update to core-2021.7.3, looks modbus changed, my solax entities stoped to work. I believe i have to change something in the solax.yaml under packages.
Can you point in the right direction?

Hi,

I have made some changes to the group read for the Solax X3 so that it works with the new home assistant.

modbus:
    name: SolaX
    type: tcp # Comment out for RS485
    host: "IP nr" # Comment out for RS485
    port: 502 # Comment out for RS485¨
    timeout: 5

    sensors:
  # Holding Registers
      - name: SolaX Group h1
        address: 139
        count: 19
        data_type: custom
        structure: ">19H"
        scan_interval: 3

      - name: SolaX Group h2
        address: 180
        count: 8
        data_type: custom
        structure: ">8H"
        scan_interval: 3

  # Input Registers
      - name: SolaX Group i1
        address: 0
        input_type: input
        count: 30
        data_type: custom
        structure: ">H2h5Hh3Hh7H3hHh5H"
    
      - name: SolaX Group i2
        address: 70
        input_type: input
        count: 14
        data_type: custom
        structure: ">2h10HI"

sensor:
  - platform: template
    sensors:
# SolaX Group H1
      solax_charger_use_mode:
        friendly_name: "SolaX Charger Use Mode"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[0] }}"
      solax_battery_min_capacity:
        friendly_name: "SolaX Battery Minimum Capacity"
        unit_of_measurement: "%"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[1] }}"
      solax_battery_type:
        friendly_name: "SolaX Battery Type"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[2] }}"
      solax_battery_charge_float_voltage:
        friendly_name: "SolaX Battery Charge Float Voltage"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_h1').split(',')[3]| float / 10 }}"
      solax_battery_discharge_cut_off_voltage:
        friendly_name: "SolaX Battery Discharge Cut Off Voltage"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_h1').split(',')[4]| float / 10 }}"
      solax_battery_charge_max_current:
        friendly_name: "SolaX Battery Charge Maximum Current"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_h1').split(',')[5]| float / 10 }}"
      solax_battery_discharge_max_current:
        friendly_name: "SolaX Battery Discharge Maximum Current"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_h1').split(',')[6]| float / 10 }}"
      solax_charger_start_time_1:
        friendly_name: "SolaX Charger Start Time 1"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[7] }}:{{ states('sensor.solax_group_h1').split(',')[8] }}"
      solax_charger_end_time_1:
        friendly_name: "SolaX Charger End Time 1"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[9] }}:{{ states('sensor.solax_group_h1').split(',')[10] }}"
      solax_charger_start_time_2:
        friendly_name: "SolaX Charger Start Time 2"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[15] }}:{{ states('sensor.solax_group_h1').split(',')[16] }}"
      solax_charger_end_time_2:
        friendly_name: "SolaX Charger End Time 2"
        value_template: "{{ states('sensor.solax_group_h1').split(',')[17] }}:{{ states('sensor.solax_group_h1').split(',')[18] }}"

# SolaX Group H1
      solax_allow_grid_charge:
        friendly_name: "SolaX Allow Grid Charge"
        value_template: "{{ states('sensor.solax_group_h2').split(',')[0] }}"
      solax_export_control_factory_limit:
        friendly_name: "SolaX Export Control Factory Limit"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_h2').split(',')[1]| float / 10 }}"
      solax_export_control_user_limit:
        friendly_name: "SolaX Export Control User Limit"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_h2').split(',')[2]| float / 10 }}"
      solax_eps_mute:
        friendly_name: "SolaX EPS Mute"
        value_template: "{{ states('sensor.solax_group_h2').split(',')[3] }}"
      solax_eps_set_frequency:
        friendly_name: "SolaX EPS Set Frequency"
        value_template: "{{ states('sensor.solax_group_h2').split(',')[4] }}"
      solax_inverter_rate_power:
        friendly_name: "SolaX Inverter rate Power"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_h2').split(',')[6] }}"
      solax_language:
        friendly_name: "SolaX Language / Sprache"
        value_template: "{{ states('sensor.solax_group_h2').split(',')[7] }}"

# SolaX Group I1
      solax_inverter_voltage:
        friendly_name: "SolaX Inverter Voltage"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_i1').split(',')[0]| float / 10 }}"
      solax_inverter_current:
        friendly_name: "SolaX Inverter Current"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_i1').split(',')[1]| float / 10 }}"
      solax_inverter_load:
        friendly_name: "SolaX House Load"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_i1').split(',')[2] }}"
      solax_pv_voltage_1:
        friendly_name: "SolaX PV Voltage 1"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_i1').split(',')[3]| float / 10 }}"
      solax_pv_voltage_2:
        friendly_name: "SolaX PV Voltage 2"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_i1').split(',')[4]| float / 10 }}"
      solax_pv_current_1:
        friendly_name: "SolaX PV Current 1"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_i1').split(',')[5]| float / 10 }}"
      solax_pv_current_2:
        friendly_name: "SolaX PV Current 2"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_i1').split(',')[6]| float / 10 }}"
      solax_grid_frequency:
        friendly_name: "SolaX Grid Frequency"
        unit_of_measurement: Hz
        value_template: "{{ states('sensor.solax_group_i1').split(',')[7]| float / 100 }}"
      solax_inverter_temperature:
        friendly_name: "SolaX Inverter Temperature"
        unit_of_measurement: °C
        value_template: "{{ states('sensor.solax_group_i1').split(',')[8] }}"
      solax_run_mode:
        friendly_name: "SolaX Run Mode"
        value_template: "{{ states('sensor.solax_group_i1').split(',')[9] }}"
      solax_pv_power_1:
        friendly_name: "SolaX Power Power 1"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_i1').split(',')[10] }}"
      solax_pv_power_2:
        friendly_name: "SolaX Power Power 2"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_i1').split(',')[11] }}"
      solax_time_count_down:
        friendly_name: "SolaX Time Count Down"
        unit_of_measurement: s
        value_template: "{{ states('sensor.solax_group_i1').split(',')[19]| float / 1000 }}"
      solax_battery_voltage_charge:
        friendly_name: "SolaX Battery Voltage"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_i1').split(',')[20]| float / 10 }}"
      solax_battery_current_charge:
        friendly_name: "SolaX Battery Curent"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_i1').split(',')[21]| float / 10 }}"
      solax_battery_power_charge:
        friendly_name: "SolaX Battery Power"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_i1').split(',')[22] }}"
      solax_bms_connect_state:
        friendly_name: "SolaX BMS Connect State"
        value_template: "{{ states('sensor.solax_group_i1').split(',')[23] }}"
      solax_battery_temperature:
        friendly_name: "SolaX Battery Temperature"
        unit_of_measurement: °C
        value_template: "{{ states('sensor.solax_group_i1').split(',')[24] }}"
      solax_battery_capacity_charge:
        friendly_name: "SolaX Battery Capacity"
        unit_of_measurement: "%"
        value_template: "{{ states('sensor.solax_group_i1').split(',')[28] }}"
# SolaX Group I2
      solax_feedin_power:
        friendly_name: "SolaX Measured Power"
        unit_of_measurement: W
        value_template: "{{ states('sensor.solax_group_i2').split(',')[0] }}"
      solax_feedin_energy_total:
        friendly_name: "SolaX Feedin Energy Total"
        unit_of_measurement: kWh
        value_template: "{{ states('sensor.solax_group_i2').split(',')[2]| float / 10 }}"
      solax_consumed_energy_total:
        friendly_name: "SolaX Consumed Energy Total"
        unit_of_measurement: kWh
        value_template: "{{ states('sensor.solax_group_i2').split(',')[4]| float / 10 }}"
      solax_eps_volatge:
        friendly_name: "SolaX EPS Voltage"
        unit_of_measurement: V
        value_template: "{{ states('sensor.solax_group_i2').split(',')[6]| float / 10 }}"
      solax_eps_current:
        friendly_name: "SolaX EPS Current"
        unit_of_measurement: A
        value_template: "{{ states('sensor.solax_group_i2').split(',')[7]| float / 10 }}"
      solax_eps_power:
        friendly_name: "SolaX EPS Power"
        unit_of_measurement: VA
        value_template: "{{ states('sensor.solax_group_i2').split(',')[8] }}"
      solax_eps_frequency:
        friendly_name: "SolaX EPS Frequency"
        unit_of_measurement: Hz
        value_template: "{{ states('sensor.solax_group_i2').split(',')[9]| float / 10 }}"
      solax_energy_today:
        friendly_name: "SolaX Today's Yield"
        unit_of_measurement: kWh
        value_template: "{{ states('sensor.solax_group_i2').split(',')[10]| float / 10 }}"
      solax_total_energy_to_grid:
        friendly_name: "SolaX Total Export"
        unit_of_measurement: kWh
        value_template: "{{ states('sensor.solax_group_i2').split(',')[12]| float / 1000 }}"



# Combine PV1 and PV2 power
      solax_pv_total_power:
        friendly_name: "Solax PV Total Power"
        value_template: "{{ states('sensor.solax_pv_power_1') | int + states('sensor.solax_pv_power_2') | int }}"
        unit_of_measurement: 'W'

      solax_run_mode_template:
        friendly_name: "SolaX Run Mode"
        value_template: >-
          {% if is_state('sensor.solax_run_mode', '0') %}
            Waiting
          {% elif is_state('sensor.solax_run_mode', '1') %}
            Checking
          {% elif is_state('sensor.solax_run_mode', '2') %}
            Normal Mode
          {% elif is_state('sensor.solax_run_mode', '3') %}
            Off Mode
          {% elif is_state('sensor.solax_run_mode', '4') %}
            Pemanent Fault Mode
          {% elif is_state('sensor.solax_run_mode', '5') %}
            Update Mode
          {% elif is_state('sensor.solax_run_mode', '6') %}
            EPS Check Mode
          {% elif is_state('sensor.solax_run_mode', '7') %}
            EPS Mode
          {% elif is_state('sensor.solax_run_mode', '8') %}
            Self Test
          {% elif is_state('sensor.solax_run_mode', '9') %}
            Idle Mode
          {% else %}
            Unknown
          {% endif %}
      solax_charger_use_mode_template:
        friendly_name: "SolaX Charger Use Mode"
        value_template: >-
          {% if is_state('sensor.solax_charger_use_mode', '0') %}
            Self Use Mode
          {% elif is_state('sensor.solax_charger_use_mode', '1') %}
            Force Time Use
          {% elif is_state('sensor.solax_charger_use_mode', '2') %}
            Back Up Mode
          {% elif is_state('sensor.solax_charger_use_mode', '3') %}
            Feedin Priority
          {% else %}
            Unknown
          {% endif %}
      solax_bms_connect_state_template:
        friendly_name: "SolaX BMS Connect State"
        value_template: >-
          {% if is_state('sensor.solax_bms_connect_state', '0') %}
            Disconnected
          {% elif is_state('sensor.solax_bms_connect_state', '1') %}
            Connected
          {% else %}
            Unknown
          {% endif %}
      solax_battery_type_template:
        friendly_name: "SolaX Battery Type"
        value_template: >-
          {% if is_state('sensor.solax_battery_type', '0') %}
            Lead Acid
          {% elif is_state('sensor.solax_battery_type', '1') %}
            Lithium
          {% else %}
            Unknown
          {% endif %}
      solax_allow_grid_charge_template:
        friendly_name: "SolaX Allow Grid Charge"
        value_template: >-
          {% if is_state('sensor.solax_allow_grid_charge', '0') %}
            Forbidden
          {% elif is_state('sensor.solax_allow_grid_charge', '1') %}
            Charger Time 1
          {% elif is_state('sensor.solax_allow_grid_charge', '2') %}
            Charger Time 2
          {% elif is_state('sensor.solax_allow_grid_charge', '3') %}
            Both Charger Time's
          {% else %}
            Unknown
          {% endif %}
      solax_eps_mute_template:
        friendly_name: "SolaX EPS Mute"
        value_template: >-
          {% if is_state('sensor.solax_eps_mute', '0') %}
            Off
          {% elif is_state('sensor.solax_eps_mute', '1') %}
            On
          {% else %}
            Unknown
          {% endif %}
      solax_eps_set_frequency_template:
        friendly_name: "SolaX EPS Set Frequency"
        value_template: >-
          {% if is_state('sensor.solax_eps_set_frequency', '0') %}
            50Hz
          {% elif is_state('sensor.solax_eps_set_frequency', '1') %}
            60Hz
          {% else %}
            Unknown
          {% endif %}
      solax_language_template:
        friendly_name: "SolaX Language / Sprache"
        value_template: >-
          {% if is_state('sensor.solax_language', '0') %}
            English
          {% elif is_state('sensor.solax_language', '1') %}
            Deutsche
          {% else %}
            Unknown
          {% endif %}



input_boolean:
  solax_backup:
    name: SolaX Backup Mode
  solax_forcetime:
    name: SolaX Force Time
  solax_feedin:
    name: SolaX Feed In
    initial: off

input_number:
  solax_battery_charge:
    name: SolaX Battery Charge A
    min: 0
    max: 25 # Technical Datasheet states Max Charge is 30A, but recommend 25A
    step: 1

  solax_battery_discharge:
    name: SolaX Battery Disharge A
    min: 0
    max: 25 # Technical Datasheet states Max Discharge is 30A, but recommend 25A
    step: 1

  solax_battery_min_energy:
    name: "SolaX Battery Min Energy Level %"
    min: 10 # Technical Datasheet states Max DOD is 90%
    max: 70
    step: 5
    
  solax_battery_charge_max:
    name: "SolaX Battery Force Charge Capacity"
    min: 20  
    max: 95
    step: 5
    initial: 60



automation:
  - alias: House Battery Charge Rate
    trigger:
      platform: state
      entity_id: input_number.solax_battery_charge
    action:
    - service: modbus.write_register # Battery Charge
      data_template:
        hub: SolaX
        unit: '255'
        address: '36'
        value: "{{ (trigger.to_state.state | float * 10) | int }}"
    - delay: '00:00:01'
    - service: modbus.write_register # Repeat Battery Charge incase Inverter missed first
      data_template:
        hub: SolaX
        unit: '255'
        address: '36'
        value: "{{ (trigger.to_state.state | float * 10) | int }}"

  - alias: House Battery Disharge Rate
    trigger:
      platform: state
      entity_id: input_number.solax_battery_discharge
    action:
    - service: modbus.write_register # Battery Discharge
      data_template:
        hub: SolaX
        unit: '255'
        address: '37'
        value: "{{ (trigger.to_state.state | float * 10) | int }}"
    - delay: '00:00:01'
    - service: modbus.write_register # Repeat Battery Discharge incase Inverter missed first
      data_template:
        hub: SolaX
        unit: '255'
        address: '37'
        value: "{{ (trigger.to_state.state | float * 10) | int }}"

  - alias: "House Battery Min Energy %"
    trigger:
      platform: state
      entity_id: input_number.solax_battery_min_energy
    action:
    - service: modbus.write_register # Min Discharge %
      data_template:
        hub: SolaX
        unit: '255'
        address: '32'
        value: "{{ trigger.to_state.state | int }}"

  - alias: Solax Backup
    trigger:
      platform: state
      entity_id: input_boolean.solax_backup
      to: 'on'
    action:
    - service: modbus.write_register # Backup mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '2'

  - alias: Solax Backup Off
    trigger:
      platform: state
      entity_id: input_boolean.solax_backup
      to: 'off'
    action:
    - service: modbus.write_register # Auto Mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '0'

  - alias: Solax ForceTime
    trigger:
      platform: state
      entity_id: input_boolean.solax_forcetime
      to: 'on'
    action:
    - service: modbus.write_register # Force Time Mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '1'
    - delay: '00:00:01'
    - service: modbus.write_register # 2nd Force Time Mode incase Inverter missed first
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '1'

  - alias: Solax ForceTime Off
    trigger:
      platform: state
      entity_id: input_boolean.solax_forcetime
      to: 'off'
    action:
    - service: modbus.write_register # Auto Mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '0'
    - delay: '00:00:01'
    - service: modbus.write_register # 2nd Auto Mode incase Inverter missed first
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '0'
  - alias: Solax Feed In
    trigger:
      platform: state
      entity_id: input_boolean.solax_feedin
      to: 'on'
    action:
    - service: modbus.write_register # Feed In Mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '3'
        
  - alias: Solax Feed In Off
    trigger:
      platform: state
      entity_id: input_boolean.solax_feedin
      to: 'off'
    action:
    - service: modbus.write_register # Auto Mode
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '0'
    - delay: '00:00:01'
    - service: modbus.write_register # 2nd Auto Mode incase Inverter missed first
      data_template:
        hub: SolaX
        unit: '255'
        address: '31'
        value: '0'
1 Like

I have updated my GitHub in case you haven’t noticed with the changes for 2021.7.x

Very very early stage of a custom_component

Look at the known issues, it’s more proof of concept at the min.
I don’t plan on adding HACS support yet, so don’t ask.

2 Likes

@wills106 - Looking Good
Screenshot 2021-08-10 at 17.53.37

Just pushed out version 0.0.2

BMS Connect State
Start and End Times for Force-Time-Mode Formatted
House Load Import, Export & Solar Daily Energy working in Energy Dashboard
Optional Sensors on the 4th Tickbox

Edit: 13 Aug 2021

EPIC Version bump!

0.1.0 Just released.

  • Working config screen!

Version 0.2.0

More Optional Sensors

X3 Support

X1 EPS Sensors

X3 EPS Sensors

Version 0.2.1

New Optional Sensors - Only seem to update when Import / Exporting?

  • Consumed Energy Total
  • Feedin Energy Total

Total Energy To Grid - Tweaked to see if it returns sensible figure?

SolaX Today’s Export Energy - Rounded to two places and moved to Gen3 only

SolaX Today’s Export Energy - Rounded to two places and moved to Gen3 only

Added missing sensors to Gen3 X3

  • Battery Current Charge
  • Battery Voltage Charge
1 Like

Can I ask what firmware version yr SK-SU5000 is running? Mine is very old so I need to ask Solax for a firmware file for update and a procedure to install.

@robb1

This is the version I have. I managed to get it from another forum as SolaX are useless at support and dont understand why we want the latest version etc

1 Like

Thanks. Mine is 2 yrs older : SolarSingle&Charger_20140708.
I will try Solax first and then look further if they don’t oblige.

Happy to DM you the links and the F/W

Do so at your own risk as I bricked mine and had to take it apart, connect direct to the board and flash

No big deal just a bit or a brown undies moment :slight_smile:

2 Likes

Is anyone using the ‘Grid-tied min limit’ function in a Solax Hybrid X1 or X3 inverter?
I can’t get it to function properly. In theory the battery should start charging from the grid as soon as the limit is reached (in my case 14 %) at low power. My inverter goes into “Stand By” at 14 %. Then the charge drops further to 9 % and the inverter goed into ‘Idle Mode’ and does no longer respond. I want to use the function to keep the battery charged at 14 % in case of a power failure. I know there are other ways to do it but this seems like an automated integrated function of the inverter.

Why would it start charging? There are two minimum charge limits, grid tied the level that the battery will stop discharging at when grid tied. The other minimum level is the level the battery will stop discharging at when on EPS. This functionality allows you to ensure you always have energy in the bank whenever a power cut may happen.

Secondly, where is your battery stored and what temperature is it when it falls to 9% from 14%?

Morning @Jean_Slootmans. I have the “Charger” “Min Capacity (%)” set to 15% on the setup app advanced settings and if it dropped below that it will charge at the current charge rate set.

@mp3guy: the guy who installed the inverter and battery claims that the new firmware will charge the battery from the grid with 400 Watt as soon as it goes below the ‘grid-tied min limit’ (on condition that the function is ‘enabled’). He works only with SolaX and is in very close contact with the SolaX representative in The Netherlands. So, I believe him. But it turns out that my inverter does not start charging at the limit, but simply goes in “Stand By” mode, waiting for the sun to come up and then starts charging from the PV-power.

@boydo: when my inverter reaches the “min capacity” it simply stops delivering and goes into “Idle Mode”.
No charging from the grid. That is why I assumed that the “grid-tied limit” function would give a solution here.
Once in "Idle Mode’ it accepts no further Modbus commands. The only way to get it back to Normal is to wait for the sun and for the panels to produce more than 1000 Watt.

Hi,

I experimented some last winter with battery, limits etc. For me the batteries charges with 400W but only after the batteris are 3-5% bellow the limit. Not directly at 1%. I also had some problems dealing with the BMS. I wanted the inverter to keep awake at night so that I could use batteries in the electricity peak in the morning. I put discharge to 0 at let say 15% (min 10%) But then the BMS adjusted the battery capacity below 10% and it shut off to Idle (It is possible to wake it manually with the app). The explanation from Solax was that the BMS ajdusted uneven discharge form the cells. So maybe the percentage isnt that exact. I have made an automation that charges for just 5 min if I come to close the min so that it doesnt Idle. I know Wills solved in another way with allowed charging times.

If the battery suddenly drops from 14% to 9% you might be on an older firmware still.

You might be best contacting SolaX to see if there is an update, as I had a similar issue when I first had mine installed.

Thanks for the comment. I am on a newer firmware now and it looks better. Will probably evaluate better this winter as I dont need to shut off the batteries yet.