Solax X1 Hybrid G4 (local & cloud API)

@kamilb thank you so much for your guide. I’ve just got a Solax system and the dongle’s open SSID just really freaked me out.

I came across this reverse engineering repo to get the local data translation. Have you had a look at it? https://github.com/nazar-pc/solax-local-api-docs/blob/fe4d46346fb3936963e8c31ada53b870f0d73b61/Data2.txt

Witam. U mnie też działają sensory. Mam tylko jedno pytanie, czy można to pokazać w jakiś ładnych wykresach.

Been trying to get this to work but keep getting 401 unauthorized error message, I’ve changed the REG_NO and can access the inverter using a web browsers on the same network, only thing is, it doesn’t ask for a password to access the site and I think this might be complicating the process? any thoughts as I’m slowly going out my mind!

I’ve tried admin, ADMIN, Admin, the inverter 4 digit pin, blank (removing the pwd prompt) and all sorts… this really is the last part of a puzzle as we use Agile and I would really like some intelligence as to when the batteries are charging etc

(full error code content: “\r\n\r\nError\r\n<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">\r\n\r\n

401 Unauthorized.

\r\n”
status: 401)

Just a Note for Connections with Wifi Sticks V2 (tested with Version 2.033.20 and 2.034.22).

It seems not necessary using a reverse Proxy setup with nginx to map the IP 5.8.8.8 or use downgraded Firmware to access the API on a local IP, its enough to add a “X-Forwarded-For: 5.8.8.8” header to the request.

The device shows up at my local Network as “MXCHIP”. Using:

curl --header “X-Forwarded-For: 5.8.8.8” -X POST “http://192.168.178.22/?optType=ReadRealTimeData&pwd=&”

returns the JSON data.

Important, it seems to be necessary to have the “&” at the end of the URL, otherwise my stick just crashes.

If this works, adding the following lines in your YAML configuration of the REST API allows direct access on the local IP:

rest:

  • authentication: digest
    scan_interval: 20
    resource: http://192.168.178.22
    payload: “optType=ReadRealTimeData&pwd=&”
    method: POST
    headers:
    Content-Type: “application/x-www-form-urlencoded”
    X-Forwarded-For: “5.8.8.8”
    sensor:
1 Like

Hi Kamil,

Thanks for your very clear instructions, I’ve been reading countless posts on this topic for my Inverter + Cloud API only use case.

I’ve implemented your (cloud) instructions and have all my sensors available as entities, however not getting any data from Solax…yet. Will contintue to investigate.

One particular issue I am getting is 5 x errors related to Unit of Measurement, any suggestions?

Logger: homeassistant.components.sensor
Source: components/sensor/init.py:732
integration: Sensor (documentation, issues)
First occurred: 6:13:24 AM (1 occurrences)
Last logged: 6:13:24 AM

Entity sensor.solax_cloud_grid_power (<class ‘homeassistant.components.template.sensor.SensorTemplate’>) is using native unit of measurement ‘W’ which is not a valid unit for the device class (‘energy’) it is using; expected one of [‘kWh’, ‘MJ’, ‘GJ’, ‘Wh’, ‘MWh’]; Please update your configuration if your entity is manually configured, otherwise create a bug report at Issues · home-assistant/core · GitHub

Hi @riccid. This looks like a config error - worth testing out getting of the data using something like curl or postman first. Also, worth experimenting with the entity config (e.g. changing units, or commenting certain ones out).

(Please disregard - I was missing:
“sensor powercalc_label: !include powercalc.yaml”
from my config.yaml)

sigh

Hi Kamil,

You were correct, my issue was a simple (aka moronic) one, I was using the incorrect serial number in my API url.

I have another issue after adding all the cloud based components in your post. Currently I can add Energy from Grid and Energy to Grid in the Energy dashboard, however when I attempt to add Solar production there are no ‘current’ value sensors I can use.

Any thoughts as to why? Is this an issue within my Powercalc.yaml or have I perhaps missed a step?

Bizzarely I have also been able to build a dashboard that provides a whole bunch of API based feeds from the Inverter.

Nice work. Just confirming that I have local access working directly without a proxy.

X1 - G2 with Pocket WiFi 3.0. The data is different though being its a a X1-G2 so I’m working on a map for that.

You don’t need to add an “&” if your using the pocket v3.

- platform: rest
  name: "solax_rest_local"
  scan_interval: 10
  resource: !secret solax_local_ip
  payload: !secret solax_local_realtime_payload
  method: POST
  headers:
    Content-Type: "application/x-www-form-urlencoded"
    X-Forwarded-For: "5.8.8.8"
  authentication: digest
  json_attributes:
        - sn
        - ver
        - type
        - Data
        - Information
  value_template: OK -  {{ now().strftime("%H:%M:%S") }}

Good morning
I wanted to share with you an informative integration that I find practical, it is a battery charge and recharge estimation based on Node-RED. This is of course to be adapted according to your capacity, for my part I have 6000w.

[{"id":"a4c4fbb082a29c5e","type":"function","z":"823ffcb27199a3e4","name":"function 2","func":"let soc = msg.payload\nlet payload_batt = msg.payload_batt\n\nlet kwh_resten_batteri = 6000-((6000 * soc) / 100)\nlet heur_centieme = ((kwh_resten_batteri / payload_batt).toFixed(2)).toString().split(\".\")\nlet heur = parseInt(heur_centieme[0])\n//let heur = heur_centieme[0]\n\nlet centieme = parseInt(heur_centieme[1])\nlet minute = Math.round((60 * centieme) / 100)\n\n//let heur_Minute = (heur + \"h\" + minute).toString()\nlet heur_Minute = heur + \".\" + minute\n\nreturn {heur: heur, minute: minute,heur_Minute: heur_Minute}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":360,"wires":[["19d909b16ed213fe"]]},{"id":"641f1388717bd659","type":"api-current-state","z":"823ffcb27199a3e4","name":"batterie charge","server":"ee46c249.bf5b7","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.solax_local_battery_use_in","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload_batt","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":220,"y":360,"wires":[["a4c4fbb082a29c5e"],[]]},{"id":"9ef483368eba2406","type":"poll-state","z":"823ffcb27199a3e4","name":"soc","server":"ee46c249.bf5b7","version":3,"exposeAsEntityConfig":"","updateInterval":"1","updateIntervalType":"num","updateIntervalUnits":"minutes","outputInitially":false,"outputOnChanged":false,"entityId":"sensor.solax_local_battery_soc","stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is_not","outputs":1,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":70,"y":380,"wires":[["641f1388717bd659","4ce0b3598691af76"]]},{"id":"19d909b16ed213fe","type":"ha-sensor","z":"823ffcb27199a3e4","name":"batt heur charge","entityConfig":"4a7f66909dbb5d8f","version":0,"state":"heur_Minute","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":540,"y":360,"wires":[["c911df0b83ab7abd"]]},{"id":"da14648fa965217f","type":"function","z":"823ffcb27199a3e4","name":"function 3","func":"let soc = msg.payload - 10\nlet payload_batt = msg.payload_batt\n\nlet kwh_resten_batteri = ((6000 * soc) / 100)\nlet heur_centieme = ((kwh_resten_batteri / payload_batt).toFixed(2)).toString().split(\".\")\nlet heur = parseInt(heur_centieme[0])\n//let heur = heur_centieme[0]\n\nlet centieme = parseInt(heur_centieme[1])\nlet minute = Math.round((60 * centieme) / 100)\n\nlet heur_Minute = (heur + \".\" + minute).toString() \n//let heur_Minute = heur + \".\" + minute\n\nreturn {heur: heur, minute: minute,heur_Minute: heur_Minute,}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":420,"wires":[["6e442fb0705d776a"]]},{"id":"4ce0b3598691af76","type":"api-current-state","z":"823ffcb27199a3e4","name":"batterie decharge","server":"ee46c249.bf5b7","version":3,"outputs":2,"halt_if":"0","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.solax_local_battery_use_out","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload_batt","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":230,"y":420,"wires":[["da14648fa965217f"],[]]},{"id":"b7b85832ab9e96b8","type":"debug","z":"823ffcb27199a3e4","name":"heur decharge","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"heur_Minute","targetType":"msg","statusVal":"","statusType":"auto","x":760,"y":420,"wires":[]},{"id":"6e442fb0705d776a","type":"ha-sensor","z":"823ffcb27199a3e4","name":"batt heur decharge","entityConfig":"5d26a678d03e588e","version":0,"state":"heur_Minute","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":570,"y":420,"wires":[["b7b85832ab9e96b8"]]},{"id":"c911df0b83ab7abd","type":"debug","z":"823ffcb27199a3e4","name":"heur charge","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"heur_Minute","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":360,"wires":[]},{"id":"ee46c249.bf5b7","type":"server","name":"Home Assistant","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false},{"id":"4a7f66909dbb5d8f","type":"ha-entity-config","server":"ee46c249.bf5b7","deviceConfig":"e27b7c4e474e94ed","name":"batt heur charge","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"batt heur charge"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"5d26a678d03e588e","type":"ha-entity-config","server":"ee46c249.bf5b7","deviceConfig":"66d7642f45afe0a7","name":"batt heur decharge","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"batt heur decharge"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"e27b7c4e474e94ed","type":"ha-device-config","name":"batt heur charge","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""},{"id":"66d7642f45afe0a7","type":"ha-device-config","name":"batt heur decharge","hwVersion":"","manufacturer":"Node-RED","model":"","swVersion":""}]

I would like to ask a question, do you have any problems in system/logs with RESTful?
Because I installed the core version 2024.7.2 and every morning I have a crash at 5 a.m., restarting it works, so I put back 2024.6.4 but I have RESTful problems.

Enregistreur: homeassistant.components.rest.data
Source: components/rest/data.py:127
intégration: RESTful (documentation, problèmes)
S’est produit pour la première fois: 17 juillet 2024 à 17:16:06 (112 occurrences)
Dernier enregistrement: 14:07:36

Error fetching data: http://192.168.10.171/ failed with

IP is that of the solax WIFI key.

Hi, I am trying to connect to my X3-Hybrid-G4 connected to my network with PocketLAN. It seems to be getting an IP address of 192.168.1.186.

If I send it the curl command from a terminal I get this (SN Hidden):

% curl -d "optType=ReadRealTimeData&pwd=XXXXX" -X POST http://192.168.1.186 
curl: (7) Failed to connect to 192.168.1.186 port 80 after 15 ms: Couldn't connect to server

Any ideas on whats happening?

Hi
For me everything works correctly on a CMD, there is “%” too much.

curl -d "optType=ReadRealTimeData&pwd=XXXXX" -X POST http://192.168.1.186 
{"sn":"XXXXX","ver":"3.009.03","type":15,"Data":[2323,24,529,5002,2641,2681,4,4,122,119,2,17586,0,233,11810,65266,65206,31,96,2551,0,2957,0,55,100,0,35,3603,0,0,0,0,0,0,14551,2,228,6,529,40,256,1314,900,152,350,257,233,33,33,387,1,1,0,0,18000,0,65008,65535,65387,65535,0,0,0,0,0,0,0,0,0,0,1886,0,61937,65535,3000,0,62536,65535,1444,0,0,0,0,0,0,246,28,41,0,0,0,0,0,0,0,0,0,0,1,3851,788,6152,603,256,9509,0,0,0,0,0,1,1197,65508,65201,3312,3309,58343,69,21302,14389,18757,12600,16689,12611,14130,21302,14389,18757,12600,16689,12611,14130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,513,257,770,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Information":[5.000,15,"XXXXX",8,1.31,0.00,1.30,1.04,0.00,1]}

The first step would be to connect to the Wi-Fi key with IP: 192.168.1.186 with Chroms for example.

I’m using the cloud API to get data into Home Assistant. I have a SOLAX POWER POCKET LAN 2.0 adaptor. Is it possible to gather data locally? I’ve tried to follow the thread but haven’t succeeded.

Hi

It has been a huge help with this post and all the good input.
I have now got most of it working, but I need a way to set the inverter not to export power when there are negative prices.

Today, I use the app or web and set Export Control to 0. Maybe there are other options that do the same, but otherwise,

I would like help with what to call similar to solax_local_set_battery_grid_charge_level_payload: ‘optType=setReg&pwd=REG_NO&data={“num”:1,“Data”:[{“reg”:31,“val”:“{{ level }}”}]}’, which can change the value of Export Control.

I hope someone can help me get this to work. Thank you in advance.

Just to get it out there, I’m working on a integration for this:

Hi, I would like to write a simple php script for charging the battery at a low price of electricity, but I am stuck on changing the parameters of the inverter (Solax X3 Hybrid G4). Reading works fine for me
curl_setopt($ch, CURLOPT_POSTFIELDS,‘optType=ReadSetData&pwd=SRXXXXXXXX’);
return string(865) "[1035,2760,4750,5150,2553,1610,2645,4750,5150,20,10,10,10,270,500,10,10,300,300,139,0,0,138,109,7963,787,6154,0,10,1,55,10,100,30,50,0,13,14,0,15127,0,14,15,0, …
,but any writing ends with the answer “false”:
curl_setopt($ch, CURLOPT_POSTFIELDS,‘optType=setReg&pwd=SRXXXXXXXX&data={“num”:1,“Data”:[{“reg”:37,“val”:12}]}’);
This command should change “Charge_period_start_time” from the current value of 13 to 12. But without success.

You have no idea where I’m doing wrong? Do you know of any manual with a description of the commands and registers of the said inverter? Thanks. Tony

Have you tried the SolaX modbus integration? Easier to go that route than to try poking the inverter the hard way.

Hi,
I haven’t tried it. I think it would be much more complicated to study a comprehensive application and programing the reading of current electricity prices and weather forecast. If I could change the parameters of the inverter, I already have it done in php.

I think that the val fields all need to be strings - try adding double quotes around the “12” in the val field:

{“reg”:37,“val”:"12"}]}

To force charge my system I set the work mode to manual and the mode to force charge - if you need any more details give me a shout.

Hi, yes it works. Such a stupid mistake. Thanks a lot.