SolarMax Inverter Integration

The Domoticz system has functionality to interface with the SolarMax solar inverter via a LAN interface. I now use Home Assistant, however I have no choice but to keep Domoticz to interface with my SolarMax inverter. I want to retire Domoticz, so having the SolarMax integration in HA would enable me to use Home Assistant exclusively.

Domoticz supports the SolarMax solar inverter over the LAN natively, and Home Assistant doesn’t have any integration for SolarMax. The only other alternative would be for me to write my own integration code… but I am not that good :slight_smile:

See the reference to SolarMax on the Domoticz page Hardware Setup - Domoticz

Hi,
there are several scripts around to read out a Solarmax inverter, no integration in HA though so far.
I was using a Spacelynk Controller from Schneider with a lua script and am now switching over to HA.

It’s still early stage: Install node-red for HA and create a flow to periodically send a TCP-request to the inverter, parse the response and update a state using a hass-post-node. Example flow below.

The query strings contain a checksum, which is some work to generate. In the node you find example queries, you can just go with one of them. In the ‘decode’ node there’s a list of query, status and error codes. Just send a query string to the inverter and observe what is coming back. Then extract what you need from the response and send it to HA. node-red has the advante of being able to debug every step of the flow.

best, Valentin

[{"id":"42a5d63b.36e0c","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"c72dd8a6a98f5a10","type":"tcp request","z":"42a5d63b.36e0c","server":"192.168.8.222","port":"12345","out":"time","ret":"string","splitc":"1000","name":"Solarmax","x":300,"y":220,"wires":[["ea88b20808b4d104","8ee586d73de7eb03"]]},{"id":"b460e626df5316ec","type":"inject","z":"42a5d63b.36e0c","name":"","props":[{"p":"payload"}],"repeat":"600","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{FB;01;36|64:PAC;KLD;KLM;IL1;KDY;KMT;KLY;KT0;ADR|0cff}","payloadType":"str","x":120,"y":220,"wires":[["c72dd8a6a98f5a10"]]},{"id":"ea88b20808b4d104","type":"debug","z":"42a5d63b.36e0c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":490,"y":120,"wires":[]},{"id":"8ee586d73de7eb03","type":"function","z":"42a5d63b.36e0c","name":"decode","func":"/*\n\nFollowing info is not JavaScript yet, just for informational purposes.\nThe query codes are:\n\nquery_codes = {\n  [\"ADR\"] = '', -- Device Address (z.B. 1)\n  [\"BDN\"] = '', -- ???\n  [\"CAC\"] = '', -- ???\n  [\"DDY\"] = '', -- Date day\n  [\"DIN\"] = '', -- ???\n  [\"DMT\"] = '', -- Date month\n  [\"DD00\"] = '', -- date,total watt,peak watt,houres sunshine , all on day base\n  [\"DM05\"] = '', -- DM05 ansers : idem on month base\n  [\"DYR\"] = '', -- Date year\n  [\"E11\"] = '', -- Error 1, number\n  [\"E1D\"] = '', -- Error 1, day\n  [\"E1h\"] = '', -- Error 1, hour\n  [\"E1m\"] = '', -- Error 1, minute\n  [\"E1M\"] = '', -- Error 1, month\n  [\"E21\"] = '', -- Error 2, number\n  [\"E2D\"] = '', -- Error 2, day\n  [\"E2h\"] = '', -- Error 2, hour\n  [\"E2m\"] = '', -- Error 2, minute\n  [\"E2M\"] = '', -- Error 2, month\n  [\"E31\"] = '', -- Error 3, number\n  [\"E3D\"] = '', -- Error 3, day\n  [\"E3h\"] = '', -- Error 3, hour\n  [\"E3m\"] = '', -- Error 3, minute\n  [\"E3M\"] = '', -- Error 3, month\n  [\"FDAT\"] = ' ', -- ???\n  [\"IDC\"] = 'dc_current',   -- DC Current (mA)\n  [\"IL1\"] = 'current_phase1',  -- current phase 1 (mA)\n  [\"KDY\"] = '', -- Energy today (Wh)\n  [\"KHR\"] = '', -- Operating hours\n  [\"KLD\"] = '', -- Energy yesterday (Wh)\n  [\"KLM\"] = '', -- Energy last month (kWh)\n  [\"KLY\"] = '', -- Energy last year (kWh)\n  [\"KMT\"] = '', -- Energy this month (kWh)\n  [\"KT0\"] = 'total_yield',  -- total yield (kWh)\n  [\"KYR\"] = '', -- Energy this year (kWh)\n  [\"LAN\"] = '', -- Language (1)\n  [\"MAC\"] = '', -- ???\n  [\"PAC\"] = 'power_output',  -- AC power being generated * 2 (W)\n  [\"PIN\"] = '', -- ???\n  [\"PRL\"] = 'relative_ouput',  -- relative output (%)\n  [\"SAL\"] = '', -- ???\n  [\"SDAT\"] = '', -- ???\n  [\"SWV\"] = 'Software Version', -- Software Version\n  [\"SYS\"] = 'sys',  -- 4E28 = 17128\n  [\"THR\"] = '', -- Time Hours\n  [\"TKK\"] = 'inverter_temp', -- inverter operating temp\n  [\"TMI\"] = '', -- Time Minutes\n  [\"TNF\"] = 'frequency',  -- generated frequency (Hz)\n  [\"TYP\"] = '', -- ???\n  [\"UDC\"] = 'dc_voltage',  -- DC voltage (mV)\n  [\"UL1\"] = 'voltage_phase1',  -- AC Voltage Phase 1 (mV)\n}\n\n status_codes = {\n       20000: 'No Communication',\n       20001: 'Running',\n       20002: 'Not enough solar radiation',\n       20003: 'Starting up',\n       20004: 'Running with MPP',\n       20005: 'Fan running',\n       20006: 'Running on max power',\n       20007: 'Temperature limit reached',\n       20008: 'Connected to grid',\n     }\n\n alarm_codes = {\n           0: 'No error',\n           1: 'External error 1',\n           2: 'Isolation error DC side',\n           4: 'Residual current to ground',\n           8: 'Fuse break to star point',\n          16: 'External alarm 2',\n          32: 'Long time temperature limit reached',\n          64: 'Error AC connection',\n         128: 'External alarm 4',\n         256: 'Fan defect',\n         512: 'Fuse break',\n        1024: 'Failure temperature sensor',\n        2048: 'Alarm 12',\n        4096: 'Alarm 13',\n        8192: 'Alarm 14',\n       16384: 'Alarm 15',\n       32768: 'Alarm 16',\n       65536: 'Alarm 17',\n   }\n\nExamples:\nQueries:\nquery = \"{FB;01;32|64:PAC;IL1;KDY;KDL;KYR;KLY;KMT;KT0|2943}\"\nquery = \"{FB;01;4E|64:E1D;E11;E1h;E1m;E1M;E2D;E21;E2h;E2m;E2M;E3D;E31;E3h;E3m;E3M|126C}\"\nquery = \"{FB;01;3E|64:IDC;UL1;TKK;IL1;SYS;TNF;UDC;PAC;PRL;KT0;SYS|0F66}\\n\"\nExample answer of inverter:\n{01;FB;62|64:UL1=8DA;TKK=24;IL1=19B;SYS=4E28,0;TNF=1387;PAC=1690;PRL=10;KT0=1096F;SYS=4E28,0|16C0}\n\nThe response values of the inverter can be extracted from the response string using regular expressions.\n\n*/\n\n// Example: Extract PAC, AC Power\nvar code = \"PAC\";\nconst pattern1 = new RegExp(code + \"=(.*?);\");\nconst pattern2 = new RegExp(code + \"=(.*?)|\");\nvar found = msg.payload.match(pattern1);\n\nreturn { payload: { state: parseInt(found[1],16) } };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":220,"wires":[["16b44c0756d8d413","31e17bd387bc14f5"]]},{"id":"16b44c0756d8d413","type":"debug","z":"42a5d63b.36e0c","name":"Power","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":120,"wires":[]},{"id":"31e17bd387bc14f5","type":"hass-post","z":"42a5d63b.36e0c","endpoint":"6e4958c82c66dfe6","entityid":"sensor.solar_actual_power","name":"sensor.solar_actual_power","x":760,"y":220,"wires":[]},{"id":"6e4958c82c66dfe6","type":"hass-config","host":"http://192.168.8.12","port":"8123"}]
2 Likes

Thanks for this. So for the script you shared, node-red is not necessary but optionally, am I gettig this right?

I know this is an old thread but I have managed to integrate our older solarmax controller quite easily.

The steps I followed were:
Download the solarmax MaxTalk software from here https://www.solarmax.com/wp-content/uploads/MaxTalk-1.zip

Connect to your Inverters using this software first (you have to configure the IP and serial address on the inverter itself first)

Here I could check they were actually outputting data

The next step is optional and only needed if the config below doesn’t work with your model of SolarMax inverters

Once MaxTalk is getting data from your inverters, download Wire Shark, while running MaxTalk capture the packets using Wire Shark and filter on port 12345

In the data of the packets to the inverter you will find the request string used for the payload in the example below.


sensor:
  - platform: tcp
    scan_interval:
      seconds: 5
    name: solarmax3
    host: 10.129.xxx.xxx
    port: 12345
    payload: '{FB;F8;26|64:KDY;KMT;KT0;KYR;PAC|08D9}'

In the example above F8 in the string is the serial address I set on the inverter (represented in hex)
Because I’m lazy I use this website for my conversions: Hexadecimal to Decimal Converter

I believe 08D9 at the end also has something to do with the address as this changes for each of my inverters. This is shown in the output of the WireShark capture

The above code receives a nice hex string and I break it down into the useful bits using the templates below.


- sensor:
      - name: solar_3_monthly
        device_class: energy
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3').split(';')[3] | regex_findall_index('\=(.*)') | int(base=16) }}
          
  - sensor:
      - name: solar_3_daily
        device_class: energy
        state_class: total_increasing
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3').split(';')[2] | regex_findall_index('\=(.*)') | int(base=16) | int / 10 }}
          
  - sensor:
      - name: solar_3_current_w
        device_class: power
        unit_of_measurement: "w"
        state: >
          {{ states('sensor.solarmax3').split(';')[6] | regex_findall_index('(?<=\=)(.*?)(?=\|)') | int(base=16) | int / 2 | int }}

I hope the above helps someone figure out theirs as there is nothing more annoying than not having it in Home Assistant :slight_smile:

4 Likes

Thank you so much for that detailed explenation :slight_smile:

Unfortunately, this whole Wireshark and conversion is new to me and that’s what I’m stuck on. I see that some is sent to the inverter and also comes back again.
I have seen two potential payloads and tried to get the data into Home assistant.

The sensors are there, but unfortunately no data is coming in.

sensor:
  - platform: tcp
    scan_interval:
      seconds: 5
    name: solarmax3000s
    host: 192.168.178.223
    port: 12345
    payload: "{FB;01;26|64:KDY;KMT;KT0;KYR;PAC|08BC}"

  - platform: tcp
    scan_interval:
      seconds: 5
    name: solarmax3000st
    host: 192.168.178.223
    port: 12345
    payload: "{FB;01;1E|64:PAM;SAL;SYS|06A4}"

template:
  - sensor:
      - name: solarmax3000s_3_monthly
        device_class: energy
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[3] | regex_findall_index('\=(.*)') | int(base=16) }}

  - sensor:
      - name: solarmax3000s_3_daily
        device_class: energy
        state_class: total_increasing
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[2] | regex_findall_index('\=(.*)') | int(base=16) | int / 10 }}

  - sensor:
      - name: solarmax3000s_3_current_w
        device_class: power
        unit_of_measurement: "w"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[6] | regex_findall_index('(?<=\=)(.*?)(?=\|)') | int(base=16) | int / 2 | int }}

Can you help me how to get the correct information from Wireshark?
I would be happy to send you the recordings as well.

Ok, I found it out myself. The payload of solarmax3000s was already correct, but as long as the software MaxTalk is running, the sensor gets no values. Why? No idea :smiley:
Now I finally have my values in Home assistant. One question can you possibly still read out the total fed current or tell me which sensor I need for this?

Sorry for the late reply , so are you getting data correctly now?

No worries. Yes, I got it working a short time later. The error was that I still had MaxTalk open and somehow it “grabs” the data as long as it is active and Home Assistant does not get it. In the meantime everything arrives cleanly.
I have one last question. Do you happen to have an idea how to read the total number of kWh fed? Quasi solarmax “total generated energy”.

I have also noticed if MaxTalk is running then HA can not access the data. I haven’t got that particular metric out of mine yet , I will take a look tomorrow as it should be just another call that we can capture and work out.

1 Like

Hmm… I don’t get it where you guys got the payload from.
I can see a lot of my two inverter’s (SolarMax 3000s and SolarmMax 2000s) TCP data packages on Wireshark on port 12345, and I’ve looked through a lot of packages now, but there is no similiar payload to yours.
You just double clicked the packages and looked deeper into them, right?

Well guys, ti works now. I was not aware I have to look out for the according payload within the packet bytes:
Unbenannt
But holy crap @Dbuckley1712, I wonder how the hell you were able to find out on which bytes are relevant and then even how to covert them correctly? :face_with_monocle:

EDIT: It lacks indeed the possibility to get kWh to implement it to the energy dashboard.

Apologies for spamming too much here, but I just discovered this:

@Buddinski88 Should work for you I guess?

EDIT: I tried it and it works perfectly in my case and this way I can use it within the HA energy dashboard.

1 Like

Thank you @Dbuckley1712 ! I am so glad you took the effort to explain how to get this done :+1:t2:

1 Like

Just for anyone looking for the same for the more recent Solarmax / Solarmax Ultimate inverters: Those can be easily integrated via ModBus. Solarmax has been posting the relevant information at their homepage (https://www.solarmax.com/download), and from there it is easy getting this into HA.

1 Like

Hi David,
thanks a lot for your investigation regarding SolarMax. I will also reduce the equipment (RPi running Solaranzeige and Volkszähler) and integrate SolarMax directly into HA (as long as the inverters keep running …). Unfortunally I get bad info from config check…
Do you, or anyone else here, have any idea
Dieter

sensor:
  - platform: tcp
    scan_interval:
      seconds: 5
    name: solarmaxTest1
    host: 192.168.178.100
    port: 12300
    payload: '{FB;01;26|64:KDY;KMT;KT0;KYR;PAC|08BC}'

  - sensor:
      - name: solar_Test1_monthly
        device_class: energy
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmaxTest1').split(';')[3] | regex_findall_index('\=(.*)') | int(base=16) }}
          
  - sensor:
      - name: solar_Test1_daily
        device_class: energy
        state_class: total_increasing
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmaxTest1').split(';')[2] | regex_findall_index('\=(.*)') | int(base=16) | int / 10 }}
          
  - sensor:
      - name: solar_Test1_current_w
        device_class: power
        unit_of_measurement: "w"
        state: >
          {{ states('sensor.solarmaxTest1').split(';')[6] | regex_findall_index('(?<=\=)(.*?)(?=\|)') | int(base=16) | int / 2 | int }}


WireShark1

Do you have any example-config-file?

I tried multiple times, but always failed.

Sure, here it is. I struggled also hugely until I learnt that you need to add the “input_type: input” so HA knows which register “family” to do the lookup in. Just add your IP address below, and all should work.

modbus:

  - name: solarmax
    type: tcp
    host: YOUR LOCAL IP ADDRESS
    port: 502
    sensors:
      - name: solarmax_firmware
        slave: 1
        address: 102
        input_type: input
        scan_interval: 3600
      - name: solarmax_dc_leistung_wechselrichter
        slave: 1
        address: 110
        input_type: input
        scan_interval: 1
      - name: solarmax_batterie_ladeleistung
        slave: 1
        address: 114
        input_type: input
        scan_interval: 1
      - name: solarmax_eigenverbrauchs_leistung
        slave: 1
        address: 116
        input_type: input
        scan_interval: 1
      - name: solarmax_einspeiseleistung
        slave: 1
        address: 118
        input_type: input
        scan_interval: 1
      - name: solarmax_ac_leistung_komplettanlage
        slave: 1
        address: 120
        input_type: input
        scan_interval: 1
      - name: solarmax_batterie_ladezustand
        slave: 1
        address: 122
        input_type: input
        scan_interval: 1
1 Like

Thanks alot :slight_smile:

Working perfectly :slight_smile:

Glad to hear!

Hello, I’m following your post carefully, but I can’t get a good result.
Can someone help me because I don’t see where I made the mistake.
thank you very much

Hello, I follow your post carefully, but I can’t get a good result. Can someone help me ? thank you very much

# SOLARMAX
sensor:
  - platform: tcp
    scan_interval:
      seconds: 5
    name: solarmax3000s
    host: 192.168.1.123
    port: 12345
    payload: "{FB;01;3E|64:IDC;UL1;TKK;IL1;SYS;TNF;UDC;PAC;PRL;KT0;SYS|0F66}"


template:
  - sensor:
      - name: solarmax3000s_3_monthly
        device_class: energy
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[3] | regex_findall_index('\=(.*)') | int(base=16) }}

  - sensor:
      - name: solarmax3000s_3_daily
        device_class: energy
        state_class: total_increasing
        unit_of_measurement: "kWh"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[2] | regex_findall_index('\=(.*)') | int(base=16) | int / 10 }}

  - sensor:
      - name: solarmax3000s_3_current_w
        device_class: power
        unit_of_measurement: "w"
        state: >
          {{ states('sensor.solarmax3000s').split(';')[6] | regex_findall_index('(?<=\=)(.*?)(?=\|)') | int(base=16) | int / 2 | int }}