Add some string reading bits, SerialNumber test failing

This commit is contained in:
Paul Warren 2024-10-12 17:37:46 +11:00
parent 989323f96d
commit 3929da604d
5 changed files with 63 additions and 14 deletions

View file

@ -1,8 +1,11 @@
# pyFroniusReg
python library for interacting with Fronius inverters via modbus/TCP. Includes some example utilities
The aim of this python library is to provide some nice to use abstractions to interact with Fronius solar inverters and storage systems over ModBus. It is tested on a Gen24 Primo 5kW system with an attached BYD battery storage system using 'float' mode of the sunspec ModBus over TCP protocol.
FroniusReg.py: the library
# Examples
force_charge.py: Use the library to force my system to charge at 2.5kW

View file

@ -1,32 +1,44 @@
#!/usr/bin/env python3
from pyfroniusreg import gen24_registers as froniusreg
from pyfroniusreg.froniusreg import registerReadError
from pymodbus.client.tcp import ModbusTcpClient
fronius1 = ModbusTcpClient("172.19.107.211", port=502, timeout=10)
fronius1.connect()
print(froniusreg.ID.getValue(fronius1))
print(froniusreg.L.getValue(fronius1))
# Some strings
print(froniusreg.Mn.getValue(fronius1))
print(froniusreg.Md.getValue(fronius1))
print(froniusreg.Vr.getValue(fronius1))
print(froniusreg.SN.getValue(fronius1))
print(froniusreg.DA.getValue(fronius1))
soc = froniusreg.scaledStateOfCharge.getValue(fronius1)
print(" SOC: %s%%" % soc)
discharge = froniusreg.scaledOutWRte.getValue(fronius1)
print("Pre DRate: %d%%" % discharge)
print(" DRate: %d%%" % discharge)
charge = froniusreg.scaledInWRte.getValue(fronius1)
print("Pre CRate: %d%%" % charge)
print(" CRate: %d%%" % charge)
mode = froniusreg.StorCtl_Mode.getValue(fronius1)
print("Pre Mode: %d" % mode)
print(" Mode: %d" % mode)
reserve = froniusreg.scaledReserve.getValue(fronius1)
print("Pre Res: %d" % reserve)
print(" Res: %d" % reserve)
rate = froniusreg.scaledMaxChaRte.getValue(fronius1)
print("Pre rate: %d" % rate)
print(" rate: %d" % rate)
rate = froniusreg.scaledMaxWChaGra.getValue(fronius1)
print("Pre WGra rate: %d" % rate)
print(" WGra rate: %d" % rate)
revert = froniusreg.InOutWRte_RvrtTms.getValue(fronius1)
print("Timer: %d" % revert)

View file

@ -29,16 +29,25 @@ class DataType():
# helper functions for DataType constructors
def decode_string(decoder, value):
return str(decoder.decode_string(16).decode('utf-8'))
def decode_string8(decoder):
return str(decoder.decode_string(8).decode('utf-8'))
def decode_string16(decoder):
try:
return str(decoder.decode_string(16).decode('utf-8'))
except UnicodeDecodeError:
return str(decoder.decode_string(16))
def decode_string32(decoder):
return str(decoder.decode_string(32).decode('utf-8'))
def encode_16bit_int(encoder, value):
return encoder.add_16bit_int(int(value))
# The various data types that the fronius inverters use
string8 = DataType(4, decode_string, BinaryPayloadBuilder.add_string)
string16 = DataType(8, decode_string, BinaryPayloadBuilder.add_string)
string32 = DataType(16, decode_string, BinaryPayloadBuilder.add_string)
string8 = DataType(4, decode_string8, BinaryPayloadBuilder.add_string)
string16 = DataType(8, decode_string16, BinaryPayloadBuilder.add_string)
string32 = DataType(16, decode_string32, BinaryPayloadBuilder.add_string)
int16 = DataType(1, BinaryPayloadDecoder.decode_16bit_int, encode_16bit_int)
uint16 = DataType(1, BinaryPayloadDecoder.decode_16bit_uint, encode_16bit_int)
int32 = DataType(2, BinaryPayloadDecoder.decode_32bit_int, BinaryPayloadBuilder.add_32bit_int)
@ -80,7 +89,7 @@ class FroniusReg:
self.datatype.width,
slave=self.unit)
if(modbusValue.isError()):
raise registerReadError("Unable to read from Fronius Register: %d, %s" % (self.id, self.description))
raise registerReadError("Unable to read from Fronius Register: %d, %s\n%s" % (self.address, self.description, modbusValue))
if(modbusValue is None):
raise registerReadError("It's NONE!")
return self.datatype.decode_from_register(modbusValue.registers)

View file

@ -8,6 +8,14 @@ storageStateOfCharge = froniusreg.FroniusReg(40362, froniusreg.uint16, 1, "Stora
storageStateOfChargeSF = froniusreg.FroniusReg(40376, froniusreg.int16, 1, "Storage State of Charge Scaling Factor")
scaledStateOfCharge = froniusreg.ScaledFroniusReg(storageStateOfCharge, storageStateOfChargeSF)
ID = froniusreg.FroniusReg(40003, froniusreg.uint16, 1, "Well-known value. Uniquely identifies this as a sunspec model 'common' (1)")
L = froniusreg.FroniusReg(40004, froniusreg.uint16, 1, "Sunspec model commen register count")
Mn = froniusreg.FroniusReg(40005, froniusreg.string16, 1, "Manufacturer")
Md = froniusreg.FroniusReg(40021, froniusreg.string16, 1, "Device Model")
Vr = froniusreg.FroniusReg(40045, froniusreg.string8, 1, "SW version")
SN = froniusreg.FroniusReg(40068, froniusreg.string16, 1, "Serial Number")
DA = froniusreg.FroniusReg(40069, froniusreg.uint16, 1, "Modbus Device Address")
OutWRte = froniusreg.FroniusReg(40366, froniusreg.int16, 1, "DischargeRate")
InWRte = froniusreg.FroniusReg(40367, froniusreg.int16, 1, "ChargeRate")
WRteSF = froniusreg.FroniusReg(40379, froniusreg.int16, 1, "ScalingFactor for storage Watts")

View file

@ -18,6 +18,23 @@ class TestRead(unittest.TestCase):
dr = gen24_registers.OutWRte.getValue(fronius1)
assert isinstance(dr, int)
def test_read_string16(self):
Mn = gen24_registers.Mn.getValue(fronius1)
assert Mn[0:7] == "Fronius"
def test_read_string8(self):
# this isn't a good test, this value changes regularly
Vr = gen24_registers.Vr.getValue(fronius1)
assert Vr == "1.33.7-1"
def test_read_model(self):
Md = gen24_registers.Md.getValue(fronius1)
assert Md == "Primo GEN24 5.0\x00"
def test_read_sn(self):
SN = gen24_registers.SN.getValue(fronius1)
assert SN == "12345567"
def test_write_direct(self):
current = gen24_registers.OutWRte.getValue(fronius1)
retval = gen24_registers.OutWRte.setValue(fronius1, current)