Graphing Solar Data

Grafana displaying Solar inverter data

Grafana reading Sungrow inverter data

It turns out that the “Sungrow” inverters use a version of modbus standards that can be interrogated via TCP/IP.

Refer to meltaxa at Github for some fine development work which formed the basis of this project.
https://github.com/meltaxa/solariot

In use:

  • Meltaxa’s “solariot” python script will scrape the inverter.
    While there are many options to use influxdb and other methods, my preference is (always) output to an MQTT host.
  • Some quick python (see below for example) running as a Raspberry pi systemd service catches the MQTT publications in the topic “inverter/stats” and punches them into a local MySQL database.
  • Grafana isn’t really designed for MySQL but with the plugin it works well enough and with a bit of work can create the most stunning displays.

The following code will subscribe to the Solariot mqtt publications and push selected data into a MySQL instance.

#!/usr/bin/python

# Subscribes to  MQTT server and waits for Topic: inverter/stats
# On receipt of the JSON data summary in the form {"daily_pv_energy":15330,"total_pv_power":3188,"load_power":1287,"export_power":188,   ....etc}
# Extracts  the variables and sends to mySQL server on  localhost / mySQL host
# Puts it inot the {solar} database and {inverter1} table
import MySQLdb
import json
import paho.mqtt.client as mqtt
import datetime


def on_connect(mqttc, obj, flags, rc):
    print("rc: " + str(rc))

def on_message(mqttc, obj, msg):
    msg.payload = msg.payload.decode("utf-8")  ## Rmmoves b for bytes
    #    print(msg.topic + " " + str(msg.qos) + " " + str(msg.payload))

    if (str(msg.topic) == 'inverter/stats'):
            now = datetime.datetime.now()
            print("mqtt2mysql: inverter/stats message received at ", now.strftime("%H:%M:%S"))
            #print(msg.payload)
            p = msg.payload
            y = json.loads(p)

            m1 = y["daily_pv_energy"]
            m2 = y["total_pv_power"]
            m3 = y["load_power"]
            m4 = y["export_power"]
            m5 = y["battery_level"]
            m6 = y["battery_temp"]
            m7 = y["pv1_current"]
            m8 = y["pv2_current"]
            m9 = y["daily_use_energy"]
            m10 = y["battery_power"]
            m11 = y["daily_export_energy"]
            m12 = y["13036"]/10    # Possibly grid use??
            if (m3 > m2):
            m10=-m10  # Guess when the battery is being charged or discharged

            #print m1
            db = MySQLdb.connect(host="localhost",    # your host, usually localhost
                                 user="xxxxx",         # your username
                                 passwd="xxxxxx",  # your password
                                 db="solar")        # name of the data base
            # you must create a Cursor object. It will let you execute all the queries you need
            cur = db.cursor()
            cur.execute('INSERT INTO inverter1 (daily_pv_energy,total_pv_power,load_power,export_power,battery_level,battery_temp,pv1_current,pv2_current,daily_use_energy,battery_power,daily_export_energy,grid_use) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' %(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12))
            db.commit()
            #    print (row[1])  #for row in cur.fetchall():
            print("mqtt2mysql:",str(m1), str(m2) ,str(m3), str(m4), str(m5), str(m6), str(m7), str(m8), str(m9), str(m10), str(m11), str(m12) )

            print("mqtt2mysql:", cur.rowcount, "record inserted.")
            db.close()

def on_subscribe(mqttc, obj, mid, granted_qos):
    print("mqtt2mysql: Subscribed: " + str(mid) + " " + str(granted_qos))

def on_log(mqttc, obj, level, string):
    #print(string)
    print(str(m1), str(m2) ,str(m3), str(m4), str(m5), str(m6), str(m7), str(m8), str(m9), str(m10), str(m11), str(m12) )

mqttc = mqtt.Client()
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
# mqttc.on_log = on_log  # Uncomment to enable debug messages
mqttc.connect("nnn.nnn.nnn.nnn", 1883, 60)
mqttc.subscribe("inverter/stats", 1)    #mqttc.subscribe("#", 1)
mqttc.loop_forever()