Solax X1 Hybrid G4 (local & cloud API)

Thanks for the suggestion but your solution requires a modbus adapter which I don’t have and additional configuration. This way looks easier.

I am using what came with my default installation. Nothing else. Simply the X1 Hybrid G4 and the PocketWifi v3 adapter which it also uses to communicate with the cloud.

Not sure what you mean by it needing a modbus adapter and additional configuration. It works out of the box.

Simply put the IP address of the X1 in and off it goes (obviously your X1 must be on your network)

It uses TCP or MODBUS - this is the setting screen for TCP.

image

O the page is mentioning it at the Installation section:
GitHub - wills106/homeassistant-solax-modbus: SolaX Power Modbus custom_component for Home Assistant (Supports some Ginlong Solis, Growatt, Sofar Solar, TIGO TSI & Qcells Q.Volt Hyb)

But thats good news. Will try it somewhere these days :slight_smile:

If you expand the readme it shows more info. Anyway, I know it works and gets rid of the horrible cloud reliance.

You also get a heck of a lot more control and entities by default (this is on my installation)

image

I have just setup an X1-Hybrid-G4 and it hooked up brilliantly but for some odd reason my import energy is 1000 kWh over what it should be?
image

I’ve updated the configuration files and added a few automations for Octopus Saving Sessions - described here in more details: Automated Octopus Saving Sessions with Solax X1 Hybrid G4

Anyone else lost all connectivity after the last solax update?

Hi. What update are you referring to? Inverter firmware, wifi dongle or HA integration?

Hi John, Yes ofcause I can share my config file.

Local part of my sensor.yaml

### Solax Local REST sensor ###
- platform: rest
  scan_interval: 10
  resource: !secret solax_local_ip
  payload: !secret solax_local_realtime_payload
  method: POST
  name: "solax_rest_local"
  json_attributes:
        - SN
        - ver
        - type
        - Data
        - Information
  value_template: 'OK'  # dummy value, not used; avoids the "State max length is 255 characters" error

### Solax Local REST settings ###
- platform: rest
  scan_interval: 3600
  resource: !secret solax_local_ip
  payload: !secret solax_local_settings_payload
  method: POST
  name: "solax_rest_local_settings"
  # Unfortunately settings are not returned as a JSON document but an array of numbers, so having to pick just the relevant to avoid the max 255 chars limit
  # 0 - Self Use Min SOC %
  # 1 - Self Use Charge from grid (0 for disabled, 1 for enabled)
  # 2 - Self Use Charge from grid to % 
  value_template: "{{ '[' ~ value.split(',')[28] ~ ',' ~ value.split(',')[29] ~ ',' ~ value.split(',')[30] ~ ']' }}"

This works fime for me now. It is a but customised from the original script, where I use sensor.yaml.

Please let me know if this works for you!

Thanks for the reply! I was looking for the mapping part so the seperate sensors for all attributes, can you share that part also?

# --- LOCAL --------------------------

### Local sensor readings ###
  - name: solax_local
    state: > 
          {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......."  %}{{ now().strftime("%H:%M:%S") }}
          {% else %}{{ (states('sensor.solax_local')) }}{% endif %}
    attributes: 
      sn: >-
          {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......." %}{{ (state_attr('sensor.solax_rest_local', 'SN')) }}
          {% else %}{{ (state_attr('sensor.solax_local', 'SN')) }}{% endif %}
      ver: >-
        {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......." %}{{ (state_attr('sensor.solax_rest_local', 'ver')) }}
        {% else %}{{ (state_attr('sensor.solax_local', 'ver')) }}{% endif %}
      type: >-
        {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......." %}{{ (state_attr('sensor.solax_rest_local', 'type')) }}
        {% else %}{{ (state_attr('sensor.solax_local', 'type')) }}{% endif %}
      Data: >-
        {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......." %}{{ (state_attr('sensor.solax_rest_local', 'Data')) }}
        {% else %}{{ (state_attr('sensor.solax_local', 'Data')) }}{% endif %}
      Information: >-
        {% if state_attr('sensor.solax_rest_local', 'SN') == "SW......." %}{{ (state_attr('sensor.solax_rest_local', 'Information')) }}
        {% else %}{{ (state_attr('sensor.solax_local', 'Information')) }}{% endif %}

### Make the settings look like sensor data by embedding the info in the Data attribute 
  - name: solax_local_settings
    state: > 
          {{ now().strftime("%H:%M:%S") }}
    attributes: 
      Data: >-
        {{ (states('sensor.solax_rest_local_settings')) }}

#### Combined Solar PV output ####
  - name: "Solax Local PV Output"
    unique_id: solax_local_pv_output
    state: "{{ (state_attr('sensor.solax_local', 'Data')[11]) | int(default=0) }}"
    unit_of_measurement: "W"
    state_class: measurement
    device_class: "power"

#### Actual Voltage AC ####
  - name: "Solax Local Voltage AC Output"
    unique_id: solax_local_voltage_ac
    state: "{{ (state_attr('sensor.solax_local', 'Data')[5]) | int(default=0) }}"
    unit_of_measurement: "V"
    state_class: measurement
    device_class: "power"

#### Actual Voltage DC ####
  - name: "Solax Local Voltage DC Output"
    unique_id: solax_local_voltage_dc
    state: "{{ (state_attr('sensor.solax_local', 'Data')[2]) | int(default=0) }}"
    unit_of_measurement: "V"
    state_class: measurement
    device_class: "power"

#### Actual Current AC ####
  - name: "Solax Local Current AC Output"
    unique_id: solax_local_current_ac
    state: "{{ ((state_attr('sensor.solax_local', 'Data')[4]) | float - 0) }}"
    unit_of_measurement: "A"
    state_class: measurement
    device_class: "power"

#### Actual Current DC ####
  - name: "Solax Local Current DC Output"
    unique_id: solax_local_current_dc
    state: "{{ ((state_attr('sensor.solax_local', 'Data')[0]) | float - 0) }}"
    unit_of_measurement: "A"
    state_class: measurement
    device_class: "power"

#### Total Yield ####
  - name: "Solax Local PV Total Yield"
    unique_id: solax_local_pv_total_yield
    state: "{{ ((state_attr('sensor.solax_local', 'Data')[9]) | float - 0) }}"
    unit_of_measurement: "kWh"
    device_class: energy
#    state_class: total_increasing

#### Daily Yield ####
  - name: "Solax Local PV Yield Today"
    unique_id: solax_local_pv_yield_today
    state: "{{ ((state_attr('sensor.solax_local', 'Data')[8]) | float -0) }}"
    unit_of_measurement: "kWh"
    device_class: energy
#    state_class: measurement

### Inverter output (negative for charging battery) ####
  - name: "Solax Local AC Power"
    unique_id: solax_local_ac_power
    state: >
      {% if state_attr('sensor.solax_local', 'Data')[6] > 32767 %}{{ (state_attr('sensor.solax_local', 'Data')[6] - 65536) | int(default=0) }}
      {% else %}{{ state_attr('sensor.solax_local', 'Data')[6] | int(default=0) }}{% endif %}
    unit_of_measurement: "W"
    state_class: measurement
    device_class: "power"

### Grid power (positive for feed-in, negative for consumption) ###
  - name: "Solax Local Grid Power"
    unique_id: solax_local_grid_power
    state:  >
      {% if state_attr('sensor.solax_local', 'Data')[32] > 32767 %}{{ (state_attr('sensor.solax_local', 'Data')[32] - 65536) }}
      {% else %}{{ state_attr('sensor.solax_local', 'Data')[32] }}{% endif %}
    unit_of_measurement: "W"
    state_class: measurement
    device_class: "power"
1 Like

@Freakandel
I just reviewed the code and it seems that you don’t need the solax_local_settings part, it doesnt have a reference anywhere, so you could remove it.
I also compared the data from solax_local and Solaxcloud.com and the following mapping differs between us:

Your code:

solax_local_pv_output > 'Data')[11]
solax_local_voltage_ac > 'Data')[5]
solax_local_voltage_dc > 'Data')[2]
solax_local_current_dc > 'Data')[0]

I think that these should change to:

Data 11 > solax_local_pv1_power
Data 5 > solax_local_network_voltage
Data 2 > solax_local_pv1_voltage
Data 0 > solax_local_pv1_current

Beside total yield and daily yield should contain state class total_increasing, so you can use this data in the energy dashboard and for long term statics.

One last thing: You’re missing the operation mode of the inverter so you know the status:

    - name: "Solax Local Inverter Operation Mode"
      unique_id: solax_local_inverter_operation_mode
      icon: mdi:solar-power-variant
      state: >
        {% if state_attr('sensor.solax_local', 'Data')[68] ==  0 %}Wait
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  1 %}Check
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  2 %}Normal
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  3 %}Fault
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  4 %}Permanent Fault
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  5 %}Update
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  6 %}EPS Check
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  7 %}EPS
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  8 %}Self-test
        {% elif state_attr('sensor.solax_local', 'Data')[68] ==  9 %}Idle
        {% elif state_attr('sensor.solax_local', 'Data')[68] == 10 %}Standby
        {% elif state_attr('sensor.solax_local', 'Data')[68] == 11 %}Pv Wake Up Bat
        {% elif state_attr('sensor.solax_local', 'Data')[68] == 12 %}Gen Check
        {% elif state_attr('sensor.solax_local', 'Data')[68] == 13 %}Gen Run
        {% else %}Unknown{% endif %}    

Do whatever you want with this information :slight_smile:

2 Likes

Has anyone had this problem? My X1 Hybrid G4 is being very strange with the ‘setReg’ API call to the LAN (wi-fi) IP. The URL formats used by @kamilb and others earlier in this thread, just don’t want to work.

If I make a simple operation like unlocking, it’s fine:

$ curl -d 'optType=setReg&pwd=SVVxxxxxxx&data={"num":1,"Data":[{"reg":0,"val":"2014"}]}' -X POST http://192.168.8.170
Y:code,message:"success"}

And if I set the operating mode, register 27, to 0 (self-use), it’s happy:

$ curl -d 'optType=setReg&pwd=SVVxxxxxxx&data={"num":1,"Data":[{"reg":"27","val":"0"}]}' -X POST http://192.168.8.170
Y:code,message:"success"}

(Notice that I have to quote the register number, else I get '0:code,message:“failed” as the response).

If I try to set the mode to something else - e.g. manual, it fails every time:

$ curl -d 'optType=setReg&pwd=SVVxxxxxxx&data={"num":1,"Data":[{"reg":"27","val":"3"}]}' -X POST http://192.168.8.170
0:code,message:"failed"}

Register 35, the manual operation mode, gives similar results; value 0 is okay, anything else creates an error.

Making a REST call from HA gets similar results, ranging from HTTP/401 (unauthorised) to some more random stuff. NodeRed gets ‘ECONNRESET’ for any attempt to hit the setReg API.

The read data API headers show: “sn”:“SVVxxxxxxx”,“ver”:“3.012.01”,“type”:15. Pocket wifi web page reports version 3.001.02.

Does anyone have any ideas? I’m stumped!

@jefft4 Do you happen to be running curl from Windows / dos? I remember odd behaviour when I was testing from a PC; I switched to a raspberry pi and it started working as expected.

Ubuntu Linux.

Hi,
Really like this. I just had solar installed and this is the first time I have used home assistant.
Not fully setup powercalc however the automation triggered last night and failed due to this error - Any ideas what the issue is?

Battery - Disable forced charge uses an unknown service
The automation "Battery - Disable forced charge" (automation.battery_disable_forced_charge) has an action that calls an unknown service: rest_command.solax_local_set_forced_charge_start.

Set Solar Battery Target Level uses an unknown service
The automation "Set Solar Battery Target Level" (automation.set_solar_battery_target_level) has an action that calls an unknown service: rest_command.solax_local_set_charge_battery_from_grid.

type or paste code here

Solved it! The register numbers for ‘setReg’ are not a zero-based array, they’re a 1-based array. So it’s 28 and 36 to set, rather than the 27 and 35 that I read!
Gotta love Solax, feels like they’re never knowingly logical or consistent.

That looks like HA doesn’t know those REST commands, which suggests they’re not in your configuration or the names are mis-typed. Have you added kamilb’s rest_command definitions to your configuration somewhere and did you reload or restart HA after adding them?

If you think they’re okay, enable the developer tools and try calling the service from the services page there.

I’ve implemented the Solax local settings and these are great - thank you! I’m on Intelligent Octopus so when my car is charging, to stop the battery discharging I set the Minimum State of Charge to 100% - this works fine! However, when the car stops charging and I go to reset the minimum State of Charge parameter back to the usual 10% it fails. Looking into this in more detail, it fails because the inverter has gone into idle mode. According to Solax, idle mode will be exited when either the battery starts charging (this won’t happen as it’s at 100%) or the PV starts generating power - but at 6:00am it’s dark!! Is there a way I can stop it going into idle mode or alternatively is there another way to stop the battery discharging whilst the car is charging!?

Bonjour à tous

Merci Kamilb pour tous vos recherches, elle mon été très bénéfique.
J’ai démarré Home assistante il y a 6 mois et je m’intéresse sur la suite de l’intégration avec la gestion intelligente de la batterie.
Cependant je ne comprends pas pourquoi mon code ne fonctionne pas.
Je souhaite juste passer l’état :

  • “solax_local_set_inverter_mode”
    - “Self Use” vers “Manual” respectivement “0” vers “3”
    -“solax_local_set_manual_mode_behaviour”
    - “Do Nothing” vers “Forced charge” respectivement “0” vers “2”

Comme je ne suis pas alèse avec le codage j’utilise NODE-RED pour tous les déclanchement et gestion qui suivrons.

- id: '3008'
  alias: Battery - Saving Sessions - Return to Self-Use
  description: Stop forced discharge and return to normal operation
  trigger:
  - platform: time
    at: '17:30:00'
  condition:
  - condition: template
    value_template: '{{ (states(''sensor.solax_local_inverter_mode'') == ''Manual''
      ) }}    '
  action:
  - repeat:
      sequence:
      - delay:
          hours: 0
          minutes: 0
          seconds: 15
          milliseconds: 0
      - service: rest_command.solax_local_set_inverter_mode
        data:
          value: '{{ 0 }}

            '
      - delay:
          hours: 0
          minutes: 0
          seconds: 15
          milliseconds: 0
      - service: rest_command.solax_local_set_manual_mode_behaviour
        data:
          enabled: '{{ 0 }}

            '
      - delay:
          hours: 0
          minutes: 0
          seconds: 15
          milliseconds: 0
      - service: homeassistant.update_entity
        entity_id: sensor.solax_rest_local_settings
      until:
      - condition: template
        value_template: '{{ (states(''solax_local_manual_mode_behaviour'') == ''Do
          Nothing'' and states(''sensor.solax_local_inverter_mode'') == ''Self Use'')
          or repeat.index == 3 }}'
  mode: single

Pour information si je modifie a la main les paramètre sur l’onduleur ou sur solax cloud j’ai bien les modification des ID suivent:
- “sensor.solax_local_inverter_mode”
- “sensor.solax_local_manual_mode_behaviour”
- “sensor.solax_local_manual_mode_behaviour”

Je ne comprends pas trop mon erreur ci quelqu’un veux bien prendre de temps de me lire merci a vous :slight_smile: