Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update sensor Battery #9

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.cfg
config.cfg
234 changes: 120 additions & 114 deletions InverterData.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def twosComplement_hex(hexval):
val -= 1 << bits
return val

os.chdir(os.path.dirname(sys.argv[0]))
# os.chdir(os.path.dirname(sys.argv[0]))
os.chdir(os.getcwd())
# CONFIG

configParser = configparser.RawConfigParser()
Expand All @@ -39,122 +40,127 @@ def twosComplement_hex(hexval):


# PREPARE & SEND DATA TO THE INVERTER
output="{" # initialise json output
pini=59
pfin=112
chunks=0
while chunks<2:
if chunks==-1: # testing initialisation
pini=235
pfin=235
print("Initialise Connection")
start = binascii.unhexlify('A5') #start
length=binascii.unhexlify('1700') # datalength
controlcode= binascii.unhexlify('1045') #controlCode
serial=binascii.unhexlify('0000') # serial
datafield = binascii.unhexlify('020000000000000000000000000000') #com.igen.localmode.dy.instruction.send.SendDataField
pos_ini=str(hex(pini)[2:4].zfill(4))
pos_fin=str(hex(pfin-pini+1)[2:4].zfill(4))
businessfield= binascii.unhexlify('0103' + pos_ini + pos_fin) # sin CRC16MODBUS
crc=binascii.unhexlify(str(hex(libscrc.modbus(businessfield))[4:6])+str(hex(libscrc.modbus(businessfield))[2:4])) # CRC16modbus
checksum=binascii.unhexlify('00') #checksum F2
endCode = binascii.unhexlify('15')

inverter_sn2 = bytearray.fromhex(hex(inverter_sn)[8:10] + hex(inverter_sn)[6:8] + hex(inverter_sn)[4:6] + hex(inverter_sn)[2:4])
frame = bytearray(start + length + controlcode + serial + inverter_sn2 + datafield + businessfield + crc + checksum + endCode)

checksum = 0
frame_bytes = bytearray(frame)
for i in range(1, len(frame_bytes) - 2, 1):
checksum += frame_bytes[i] & 255
frame_bytes[len(frame_bytes) - 2] = int((checksum & 255))

# OPEN SOCKET

for res in socket.getaddrinfo(inverter_ip, inverter_port, socket.AF_INET,
socket.SOCK_STREAM):
family, socktype, proto, canonname, sockadress = res
try:
clientSocket= socket.socket(family,socktype,proto);
clientSocket.settimeout(10);
clientSocket.connect(sockadress);
except socket.error as msg:
print("Could not open socket");
break

# SEND DATA
#print(chunks)
clientSocket.sendall(frame_bytes);

ok=False;
while (not ok):
try:
data = clientSocket.recv(1024);
ok=True
try:
data
except:
print("No data - Die")
sys.exit(1) #die, no data
except socket.timeout as msg:
print("Connection timeout");
sys.exit(1) #die

# PARSE RESPONSE (start position 56, end position 60)
totalpower=0
i=pfin-pini
a=0
while a<=i:
p1=56+(a*4)
p2=60+(a*4)
response=twosComplement_hex(str(''.join(hex(ord(chr(x)))[2:].zfill(2) for x in bytearray(data))+' '+re.sub('[^\x20-\x7f]', '', ''))[p1:p2])
hexpos=str("0x") + str(hex(a+pini)[2:].zfill(4)).upper()
with open("./DYRealTime.txt") as txtfile:
parameters=json.loads(txtfile.read())
for parameter in parameters:
for item in parameter["items"]:
title=item["titleEN"]
ratio=item["ratio"]
unit=item["unit"]
for register in item["registers"]:
if register==hexpos and chunks!=-1:
#print(hexpos+"-"+title+":"+str(response*ratio)+unit)
if title.find("Temperature")!=-1:
response=round(response*ratio-100,2)
else:
response=round(response*ratio,2)
output=output+"\""+ title + "(" + unit + ")" + "\":" + str(response)+","
if hexpos=='0x00BA': totalpower+=response*ratio;
if hexpos=='0x00BB': totalpower+=response*ratio;
a+=1
pini=150
pfin=195
chunks+=1
output=output[:-1]+"}"
if totalpower<installed_power+1000:
if mqtt==1:
# Initialise MQTT if configured
client=paho.Client("inverter")
if mqtt_username!="":
client.tls_set() # <--- even without arguments
client.username_pw_set(username=mqtt_username, password=mqtt_passwd)
client.connect(mqtt_server, mqtt_port)
client.publish(mqtt_topic,totalpower)
client.publish(mqtt_topic+"/attributes",output)
print("Ok")
else:
print(output)
else:
#open text file
text_file = open("picos_potencia.txt", "a")
output = "{" # initialise json output
pini = 59
pfin = 112
chunks = 0
while chunks < 2:
if chunks == -1: # testing initialisation
pini = 235
pfin = 235
print("Initialise Connection")

start = binascii.unhexlify('A5') # start
length = binascii.unhexlify('1700') # datalength
controlcode = binascii.unhexlify('1045') # controlCode
serial = binascii.unhexlify('0000') # serial
datafield = binascii.unhexlify('020000000000000000000000000000') # com.igen.localmode.dy.instruction.send.SendDataField
pos_ini = str(hex(pini)[2:4].zfill(4))
pos_fin = str(hex(pfin - pini + 1)[2:4].zfill(4))
businessfield = binascii.unhexlify('0103' + pos_ini + pos_fin) # sin CRC16MODBUS
crc = binascii.unhexlify(str(hex(libscrc.modbus(businessfield))[4:6]) + str(hex(libscrc.modbus(businessfield))[2:4])) # CRC16modbus
checksum = binascii.unhexlify('00') # checksum F2
endCode = binascii.unhexlify('15')
inverter_sn2 = bytearray.fromhex(hex(inverter_sn)[8:10] + hex(inverter_sn)[6:8] + hex(inverter_sn)[4:6] + hex(inverter_sn)[2:4])
frame = bytearray(start + length + controlcode + serial + inverter_sn2 + datafield + businessfield + crc + checksum + endCode)

checksum = 0
frame_bytes = bytearray(frame)
for i in range(1, len(frame_bytes) - 2, 1):
checksum += frame_bytes[i] & 255
frame_bytes[len(frame_bytes) - 2] = int((checksum & 255))

#write string to file
text_file.write(datetime.datetime.now().strftime('%d/%m/%y %I:%M %S %p')+'\n')
text_file.write(output+'\n'+'\n')
# OPEN SOCKET
for res in socket.getaddrinfo(inverter_ip, inverter_port, socket.AF_INET, socket.SOCK_STREAM):
family, socktype, proto, canonname, sockadress = res
try:
clientSocket = socket.socket(family, socktype, proto)
clientSocket.settimeout(10)
clientSocket.connect(sockadress)
except socket.error as msg:
print("Could not open socket")
break

#close file
text_file.close()
# SEND DATA
# print(chunks)
clientSocket.sendall(frame_bytes)

ok = False
while (not ok):
try:
data = clientSocket.recv(1024)
ok = True
try:
data
except:
print("No data - Die")
sys.exit(1) # die, no data
except socket.timeout as msg:
print("Connection timeout")
sys.exit(1) # die

# PARSE RESPONSE (start position 56, end position 60)
totalpower = 0
i = pfin - pini
a = 0
while a <= i:
p1 = 56 + (a * 4)
p2 = 60 + (a * 4)
response = twosComplement_hex(str(''.join(hex(ord(chr(x)))[2:].zfill(2) for x in bytearray(data)) + ' ' + re.sub('[^\x20-\x7f]', '', ''))[p1:p2])
hexpos = str("0x") + str(hex(a + pini)[2:].zfill(4)).upper()
with open("./DYRealTime.txt") as txtfile:
parameters = json.loads(txtfile.read())
for parameter in parameters:
for item in parameter["items"]:
title = item["titleEN"]
ratio = item["ratio"]
unit = item["unit"]
for register in item["registers"]:
if register == hexpos and chunks != -1:
# print(hexpos+"-"+title+":"+str(response*ratio)+unit)
if title.find("Temperature") != -1:
response = round(response * ratio - 100, 2)
else:
response = round(response * ratio, 2)

# Change battery Soc unit to percentage so python doesnt use the modulus operator
if unit == '%':
unit = 'perc'

# output = output + "\"" + title + "(" + unit + ")" + "\":" + str(response) + ","
output=output+"\""+ title + "_" + unit + "\":" + str(response)+","

if hexpos == '0x00BA':
totalpower += response * ratio
if hexpos == '0x00BB':
totalpower += response * ratio
a += 1
pini = 150
pfin = 195
chunks += 1

output = output[:-1] + "}"
if totalpower < installed_power + 1000:
if mqtt == 1:
# Initialise MQTT if configured
client = paho.Client("inverter")
if mqtt_username != "":
client.tls_set() # <--- even without arguments
client.username_pw_set(username=mqtt_username, password=mqtt_passwd)
client.connect(mqtt_server, mqtt_port)
client.publish(mqtt_topic, totalpower)
client.publish(mqtt_topic + "/attributes", output)
print(f"MQTT Topic:{mqtt_topic}'/attributes' Output:{output}")
print("Ok")
else:
print(output)
else:
# open text file
text_file = open("picos_potencia.txt", "a")

# write string to file
text_file.write(datetime.datetime.now().strftime('%d/%m/%y %I:%M %S %p') + '\n')
text_file.write(output + '\n' + '\n')

# close file
text_file.close()
18 changes: 14 additions & 4 deletions deye_logger/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The Solarman Wifi Logger must be reachable via an ip address on the network wher
Using the Home Assistant Mosquitto broker add-on as the mqtt destination works really well. I have been using add-on on my Raspberry Pi v4 Home Assistant setup without issues for about a month now.

This add-on is possible thanks to the work of @jlopez77 https://github.com/jlopez77/DeyeInverter
And Modded according, added Battery SOC %

## Solarman Wifi Logger ##

Expand Down Expand Up @@ -47,39 +48,48 @@ Ensure you configure the add-on before starting it. Setting the mqtt flag to fal

Ensure you create the required mqtt sensors in the homeassistant configuration.yaml to interpret the stats received on the /sunsynk mqtt topic.

# Solar Inverter sensors setup and all mqtt platforms
sensor:
- platform: mqtt
name: "solarpower"
name: "Solarpower"
state_topic: "/sunsynk"
unit_of_measurement: "W"
json_attributes_topic: "/sunsynk/attributes"
device_class: power
state_class: measurement

- platform: mqtt
name: "solar_daily_energy_bought_kwh"
name: "Solar Daily Energy Bought KWH"
state_topic: "/sunsynk/attributes"
unit_of_measurement: "kWh"
value_template: "{{ value_json.daily_energy_bought_kwh }}"
device_class: energy
state_class: total_increasing

- platform: mqtt
name: "solar_daily_energy_sold_kwh"
name: "Solar Daily Energy Sold KWH"
state_topic: "/sunsynk/attributes"
unit_of_measurement: "kWh"
value_template: "{{ value_json.daily_energy_sold_kwh }}"
device_class: energy
state_class: total_increasing

- platform: mqtt
name: "solar_daily_production_KWH"
name: "Solar Daily Production KWH"
state_topic: "/sunsynk/attributes"
unit_of_measurement: "kWh"
value_template: "{{ value_json.daily_production_KWH }}"
device_class: energy
state_class: total_increasing

- platform: mqtt
name: "Solar Battery SOC"
state_topic: "/sunsynk/attributes"
unit_of_measurement: "%"
value_template: "{{ value_json.battery_soc_soc }}"
device_class: energy
state_class: measurement

Once these sensors are added to the Home assistant configuration you can use them in the Energy Dashboard to monotor energy usage.

[//]: # "![Home Assistant Energy Config](images/energy_config.png)"
Expand Down
1 change: 0 additions & 1 deletion deye_logger/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ RUN \
libc6-compat \
git \
py3-pip && \

# Install requirements for add-on
pip3 install --no-cache-dir wheel && \
pip3 install --no-cache-dir paho-mqtt && \
Expand Down
Loading