Last active
October 29, 2023 19:30
-
-
Save markgdev/ce2dbf9002385cbe5a35b81985f9c84a to your computer and use it in GitHub Desktop.
Query a Solis Hybrid 5G inverter and push to MQTT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import minimalmodbus | |
import time | |
import paho.mqtt.publish as publish | |
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1, debug = False) | |
instrument.serial.baudrate = 9600 | |
# This probably doesn't need setting but I was getting timeout failures | |
# rather than wrong data address when I had the registers wrong | |
instrument.serial.timeout = 2 | |
# An example read for long | |
# instrument.read_long(33004, functioncode=4, signed=False) | |
# MQTT | |
mqtt = os.environ['USE_MQTT'] | |
mqtt_client = os.environ['MQTT_CLIENT_ID'] | |
mqtt_server = os.environ['MQTT_SERVER'] | |
mqtt_username = os.environ['MQTT_USERNAME'] | |
mqtt_password = os.environ['MQTT_PASSWORD'] | |
def get_status(): | |
# Inverter Temp | |
inverter_temp = instrument.read_register(33093, functioncode=4, number_of_decimals=1, signed=False) | |
# Inverter Time | |
inverter_time_hour = instrument.read_register(33025, functioncode=4, signed=False) | |
inverter_time_min = instrument.read_register(33026, functioncode=4, signed=False) | |
inverter_time_sec = instrument.read_register(33027, functioncode=4, signed=False) | |
# Battery Status, 0=Charge, 1=Discharge | |
battery_status = instrument.read_register(33135, functioncode=4, signed=False) | |
# Battery SOC | |
battery_soc = instrument.read_register(33139, functioncode=4, signed=False) | |
# Grid Power (w), Positive=Export, Negative=Import | |
grid_power = instrument.read_long(33130, functioncode=4, signed=True) | |
# House load power (w) | |
house_power = instrument.read_register(33147, functioncode=4, signed=False) | |
# Battery Power (w) | |
battery_power = instrument.read_long(33149, functioncode=4, signed=True) | |
# Current generation (Active power) (w), need to confirm when generating | |
current_generation = instrument.read_long(33057, functioncode=4, signed=False) | |
total_active_power = instrument.read_long(33263, functioncode=4, signed=True) | |
# instrument.read_long(33079, functioncode=4, signed=True) | |
# possibly this too 33263? "Meter total active power" | |
# Total generation today (kWh) | |
generation_today = instrument.read_register(33035, functioncode=4, number_of_decimals=1, signed=False) | |
# Total generation yesterday (kWh) | |
generation_yesterday = instrument.read_register(33036, functioncode=4, number_of_decimals=1, signed=False) | |
# Battery storage mode, 33=self use, 35=timed charge | |
storage_mode = instrument.read_register(43110, functioncode=3, signed=False) | |
print(f"Inverter time: {str(inverter_time_hour).zfill(2)}:{str(inverter_time_min).zfill(2)}:{str(inverter_time_sec).zfill(2)}") | |
print(f"Inverter Temperature: {inverter_temp}") | |
print(f"Battery Status: {battery_status}") | |
print(f"Battery SOC: {battery_soc}") | |
print(f"Grid Power: {grid_power}") | |
print(f"House Power: {house_power}") | |
print(f"Battery Power: {battery_power}") | |
print(f"Current Generation: {current_generation}") | |
print(f"Total Active Power: {total_active_power}") | |
print(f"Generation Today: {generation_today}") | |
print(f"Generation Yesterday: {generation_yesterday}") | |
print(f"Battery Storage Mode: {storage_mode}") | |
# Push to MQTT | |
msgs = [] | |
mqtt_topic = ''.join([mqtt_client, "/" ]) # Create the topic base using the client_id and serial number | |
if (mqtt_username != "" and mqtt_password != ""): | |
auth_settings = {'username':mqtt_username, 'password':mqtt_password} | |
else: | |
auth_settings = None | |
msgs.append((mqtt_topic + "Battery_Charge_Percent", battery_soc, 0, False)) | |
msgs.append((mqtt_topic + "Battery_Power", battery_power, 0, False)) | |
# Fix to match what we get from m.ginlong.com while we're switching between the two | |
Battery_Status = 1.0 # Default to charging | |
if battery_status == 1: | |
Battery_Status = 2.0 | |
msgs.append((mqtt_topic + "Battery_Status", Battery_Status, 0, False)) | |
msgs.append((mqtt_topic + "Power_Grid_Total_Power", grid_power, 0, False)) | |
# Fix to match what we get from m.ginlong.com while we're switching between the two | |
Power_Grid_Status = 2.0 # Default to importing | |
if grid_power > 0: | |
Power_Grid_Status = 1.0 | |
msgs.append((mqtt_topic + "Power_Grid_Status", Power_Grid_Status, 0, False)) | |
msgs.append((mqtt_topic + "Consumption_Power", house_power, 0, False)) | |
msgs.append((mqtt_topic + "AC_Power", current_generation, 0, False)) | |
msgs.append((mqtt_topic + "generation_today", generation_today, 0, False)) | |
msgs.append((mqtt_topic + "generation_yesterday", generation_yesterday, 0, False)) | |
msgs.append((mqtt_topic + "inverter_temp", inverter_temp, 0, False)) | |
msgs.append((mqtt_topic + "storage_mode", storage_mode, 0, False)) | |
publish.multiple(msgs, hostname=mqtt_server, auth=auth_settings) | |
def timed_charge(): | |
# We can use read_registers to grab all the values in one call: | |
# instrument.read_registers(43143, number_of_registers=8, functioncode=3) | |
# Not going to check charge/discharge for now, we haven't implemented it yet. | |
# Timed charge start | |
# Hour | |
charge_start_hour = instrument.read_register(43143, functioncode=3, signed=False) | |
# Minute | |
charge_start_min = instrument.read_register(43144, functioncode=3, signed=False) | |
# Timed charge end | |
# Hour | |
charge_end_hour = instrument.read_register(43145, functioncode=3, signed=False) | |
# Minute | |
charge_end_min = instrument.read_register(43146, functioncode=3, signed=False) | |
# Timed discharge start | |
# Hour | |
discharge_start_hour = instrument.read_register(43147, functioncode=3, signed=False) | |
# Minute | |
discharge_start_min = instrument.read_register(43148, functioncode=3, signed=False) | |
# Timed discharge end | |
# Hour | |
discharge_end_hour = instrument.read_register(43149, functioncode=3, signed=False) | |
# Minute | |
discharge_end_min = instrument.read_register(43150, functioncode=3, signed=False) | |
print(f"Charge Start: {charge_start_hour}:{charge_start_min}") | |
print(f"Charge End: {charge_end_hour}:{charge_end_min}") | |
print(f"Discharge Start: {discharge_start_hour}:{discharge_start_min}") | |
print(f"Discharge End: {discharge_end_hour}:{discharge_end_min}") | |
# Change charge start time minute, will work the same for the othe values. | |
# instrument.write_register(43144, functioncode=6, signed=False, value=54) | |
# We can write all the times in one call with write_registers: | |
# instrument.write_registers(43143, [18, 00, 8, 0, 8, 0, 18, 0]) | |
# Storage control switch | |
# 33 = Self use mode | |
# 35 = Timed charge mode | |
# Read/Write register | |
# instrument.read_register(43110, functioncode=3, signed=False) | |
# instrument.write_register(43110, functioncode=6, signed=False, value=33) | |
# instrument.write_register(43110, functioncode=6, signed=False, value=35) | |
# Read only register | |
# instrument.read_register(33132, functioncode=4, signed=False) | |
while True: | |
try: | |
get_status() | |
except: | |
print("Failed to query inverter, backing off for 30 seconds") | |
time.sleep(30) | |
print("-------------------------------") | |
time.sleep(5) |
Where can you find all the register numbers for Solis inverter (and their function(s))?
@AeroJohnBE The one I've been using is here: https://www.scss.tcd.ie/coghlan/Elios4you/RS485_MODBUS-Hybrid-BACoghlan-201811228-1854.pdf
I've made some tweaks to the one I'm using for reporting some more stuff, so I've posted it here: https://gist.github.com/madbobmcjim/5b8d42edce81893cd3ee47f9dfc8cbfd
Can you write multiple charge and discharge periods to the registers? E.g. charging from 8 am to 9 am and from 17 pm to 19 pm (and the rest is discharge)
It doesn't look like it from the doc. I guess you'd have to set it each time, so set it to charge between 8am and 9am, then after 9am change it to charge from 5pm to 7pm.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I just wanted to say thinks for this :-) I grabbed a RS485 adapter and connector off eBay, wired it up to an old Pi and it all worked first time.