SolarMax (S6000) - Getting data

Hi there,

since the SolarMax Binding for OpenHab is not released, yet - more information about that project can be found here - I’ve made a simple script for reading out the data provided by the inverters.
Usually, this solution should also work with other SolarMax Inverters than the S6000, which I am working with.

After capturing a Wireshark capture of the communication with the provided software MaxTalk and the inverter I recognized that the parameters are transferred via a TCP connection with plain ASCII data.
A quick research led me to the following page, which confirmed my assumption.
SolarMax MaxTalk protocol reverse engineered

So I decided to script the usage of netcat on my Linux machine for polling the inverter data and calculating the checksum for the communication automatically.
For this approach, netcat has to be installed on your machine as well.

Bash-Script:

#!/bin/bash

# Solar Max Request script

# Script parameters
# $Source: Linux server
# $Destination: Inverter module
# $Cmd: Semicolan seperated command(s) (PAC, PDC)
# $IpAddress: Ip address of your inverter
# $PortNumber: Portnumber assigned to your inverter

Source=$1      # e.g "FF"
Destination=$2 # e.g. "01" depending on your inverter address
Cmd=$3         # e.g. "PAC;KDY" for the total Power (AC) and the total energy this year
IpAddress=$4
PortNumber=$5


# Calculate lenght of the command; Lenght of Source, Destination, Lenght and CheckSum is fixed
printf -v Length "%02X" "$((${#Cmd} + 19))"


# Calculate TCP command without checksum
TcpCmd="$Source;$Destination;$Length|64:$Cmd|"


# Calculate checksum - Sum up all the bytes of string "$Source;$Destination;$Length|64:$Cmd|" and keep the lowest 16 bits of the sum.

for i in $(seq 1 ${#TcpCmd})
do
    # Get charackter bytes
    printf -v AsciiByte "%02X" "'${TcpCmd:i-1:1}"

    # Calclulate checksum
    CheckSum=$(( $CheckSum + 16#$AsciiByte ))
done


# Get lower bytes of checksum
printf -v CheckSum "%04X" "${CheckSum}"


# Append checksum to command
TcpCmd="{${TcpCmd}$CheckSum}"


# Get solar max properties according to TCP command using netcat; The first response is taken; Timeout is set to 5 seconds
echo -n $TcpCmd | netcat -W1 -w5 $IpAddress $PortNumber
echo ""

This script can be easily used with the parameters:
SolarMax_GetData.sh “SourceAddress” “DestinationAddress” “SemicolonSeperatedParameters” “InverterIpAddress” “InverterPortNumber”

For using the script automatically within OpenHab, I am using the Exec Binding and saved the script into the script’s directory.
Make sure for adding the command using the script to the whitelist, as described within the Exec Binding configuration more thoroughly.

Now a simple rule can be used to cyclically call the script and pull the inverter data. I also used the rule to convert the returning hexadecimal value into decimal values for a better interpretation.

Things-File:

//Using exec binding to execute script
//Keep aware of listing the executed conmmand line at $OpenHAB_config/misc/exec.whitelist
//Keep aware of setting propper folder/file permissions

// Solar Max Thing
Thing exec:command:SolarMaxUpdate "SolarMax" [command="/openhab/conf/scripts/SolarMax_GetData.sh %2$s", interval=60, timeout=5, autorun=false]

Items-File:

// Solar Max Script items
Switch      SolarMaxUpdate_update          "SolarMax update"                       {channel="exec:command:SolarMaxUpdate:run"}
String      SolarMaxUpdate_output          "SolarMax output string"                {channel="exec:command:SolarMaxUpdate:output"}
String      SolarMaxUpdate_input           "SolarMax input string"                 {channel="exec:command:SolarMaxUpdate:input"}
Number      SolarMaxUpdate_return          "SolarMax return value"                 {channel="exec:command:SolarMaxUpdate:exit"}
DateTime    SolarMaxUpdate_executed        "SolarMax executed"                     {channel="exec:command:SolarMaxUpdate:lastexecution"}


// Solar Max items
Switch      SolarMaxUpdate_manualUpdate    "SolarMax update"

Number      SolarMax_PAC                   "SolarMax AC power [%.0f W]"
Number      SolarMax_PDC                   "SolarMax DC power [%.0f W]"
Number      SolarMax_KHR                   "SolarMax operating hours [%d h]"
Number      SolarMax_KDY                   "SolarMax energy today [%d Wh]"
Number      SolarMax_KYR                   "SolarMax energy this year [%d kWh]"

Number      SolarMax_UDC                   "SolarMax DC voltage [%.2f V]"
Number      SolarMax_UL1                   "SolarMax AC voltage L1 [%.2f V]"
Number      SolarMax_IDC                   "SolarMax DC current [%.2f A]"
Number      SolarMax_IL1                   "SolarMax AC current L1 [%.2f A]"
Number      SolarMax_PIN                   "SolarMax installed power [%.0f W]"
Number      SolarMax_PRL                   "SolarMax used power [%d %%]"

Rules-File:

val String Source = "FF"
val String Destination = "01"
val String Prperties = "PAC;PDC;KHR;KDY;KYR;UDC;UL1;IDC;IL1;PIN;PRL"
val String IpAddresse = "xxx.xxx.xxx.xxx"
val String PortNumber = "12345"

rule "SolarMax update parameters"
when
    Time cron "0 0/5 * * * ?"  or// every 5 Minute
    Item SolarMaxUpdate_manualUpdate changed to ON
then
    logInfo("SolarMax", "Request Solar Max stats")

    // Set input string for command
    var String Command = String.format("'%s' '%s' '%s' '%s' '%s'", Source, Destination, Prperties, IpAddresse, PortNumber);
    SolarMaxUpdate_input.sendCommand(Command)

    //Executing Solar Max updates script
    logInfo("SolarMax", "Requestion command: {}", Command)
    SolarMaxUpdate_update.sendCommand(ON)

    var String Output = SolarMaxUpdate_output.state.toString()
    logInfo("SolarMax", "Returning command: {}", Output)

    var int StartIndex = Output.indexOf(':') + 1
    var int EndIndex = Output.length() - 6

    var String Result = Output.substring(StartIndex, EndIndex);

    var String[] SplittedProperties = Result.split(";")

    // Loop through respond properties
    SplittedProperties.forEach [Prop |
        // Split values
        // logDebug("SolarMax", "Returning property: {}", Prop)
        var String[] temp = Prop.split("=")
        var String PropName = temp.get(0)
        var String PropValue_hex = temp.get(1) // Hexadecimal

        // Convert hex to dec
        var int PropValue_dec = Integer.parseInt(PropValue_hex, 16)
        //logDebug("SolarMax", "Returning property: {}, {}", PropName, PropValue_dec)

        // Assign items
        switch (PropName) {
            case "PAC":  SolarMax_PAC.sendCommand(PropValue_dec)
            case "PDC":  SolarMax_PDC.sendCommand(PropValue_dec)
            case "KHR":  SolarMax_KHR.sendCommand(PropValue_dec)
            case "KDY":  SolarMax_KDY.sendCommand(PropValue_dec)
            case "KYR":  SolarMax_KYR.sendCommand(PropValue_dec)
            case "UDC":  SolarMax_UDC.sendCommand(PropValue_dec.doubleValue()/10)
            case "UL1":  SolarMax_UL1.sendCommand(PropValue_dec.doubleValue()/10)
            case "IDC":  SolarMax_IDC.sendCommand(PropValue_dec.doubleValue()/100)
            case "IL1":  SolarMax_IL1.sendCommand(PropValue_dec.doubleValue()/100)
            case "PIN":  SolarMax_PIN.sendCommand(PropValue_dec)
            case "PRL":  SolarMax_PRL.sendCommand(PropValue_dec)
        }
    ]

    //Set items to initial state
    createTimer(now.plusSeconds(1))[|SolarMaxUpdate_update.postUpdate(OFF)]
    createTimer(now.plusSeconds(1))[|SolarMaxUpdate_manualUpdate.postUpdate(OFF)]
end

I hope, this slight workaround will fit your needs until the SolarMax Binding is finished.

Best regards

The parameters I found during the communication between MaxTalk and the inverters are as follows.
Unfortunately, I only recognized the few I am using in the rule above.

COS
CPA
CYC
DD00
DD01
DD02
DD03
DD04
DD05
DD06
DD07
DD08
DD09
DD10
DD11
DD12
DD13
DD14
DD15
DD16
DD17
DD18
DD19
DD20
DD21
DD22
DD23
DD24
DD25
DD26
DD27
DD28
DD29
DD30
DIN
DM00
DM01
DM02
DM03
DM04
DM05
DM06
DM07
DM08
DM09
DM10
DM11
DY00
DY01
DY02
DY03
DY04
DY05
DY06
DY07
DY08
DY09
IAA
IAM
IDC
IEA
IED
IEE
IEM
IL1
ILM
ISL
KDY
KHR
KLD
KLM
KLY
KMT
KT0
KYR
PAC
PAM
PDC
PLF
PRL
RSD
SAL
SYS
TKK
TND
TNF
TNH
TNL
TYP
UDC
UGD
UL1
ULH
ULL
UM1
UMX

Best regards