Add some string reading bits, SerialNumber test failing
This commit is contained in:
parent
989323f96d
commit
3929da604d
5 changed files with 63 additions and 14 deletions
|
@ -1,8 +1,11 @@
|
||||||
# pyFroniusReg
|
# 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
|
force_charge.py: Use the library to force my system to charge at 2.5kW
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,44 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from pyfroniusreg import gen24_registers as froniusreg
|
from pyfroniusreg import gen24_registers as froniusreg
|
||||||
|
from pyfroniusreg.froniusreg import registerReadError
|
||||||
|
|
||||||
from pymodbus.client.tcp import ModbusTcpClient
|
from pymodbus.client.tcp import ModbusTcpClient
|
||||||
|
|
||||||
fronius1 = ModbusTcpClient("172.19.107.211", port=502, timeout=10)
|
fronius1 = ModbusTcpClient("172.19.107.211", port=502, timeout=10)
|
||||||
fronius1.connect()
|
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)
|
soc = froniusreg.scaledStateOfCharge.getValue(fronius1)
|
||||||
print(" SOC: %s%%" % soc)
|
print(" SOC: %s%%" % soc)
|
||||||
|
|
||||||
discharge = froniusreg.scaledOutWRte.getValue(fronius1)
|
discharge = froniusreg.scaledOutWRte.getValue(fronius1)
|
||||||
print("Pre DRate: %d%%" % discharge)
|
print(" DRate: %d%%" % discharge)
|
||||||
|
|
||||||
charge = froniusreg.scaledInWRte.getValue(fronius1)
|
charge = froniusreg.scaledInWRte.getValue(fronius1)
|
||||||
print("Pre CRate: %d%%" % charge)
|
print(" CRate: %d%%" % charge)
|
||||||
|
|
||||||
mode = froniusreg.StorCtl_Mode.getValue(fronius1)
|
mode = froniusreg.StorCtl_Mode.getValue(fronius1)
|
||||||
print("Pre Mode: %d" % mode)
|
print(" Mode: %d" % mode)
|
||||||
|
|
||||||
reserve = froniusreg.scaledReserve.getValue(fronius1)
|
reserve = froniusreg.scaledReserve.getValue(fronius1)
|
||||||
print("Pre Res: %d" % reserve)
|
print(" Res: %d" % reserve)
|
||||||
|
|
||||||
rate = froniusreg.scaledMaxChaRte.getValue(fronius1)
|
rate = froniusreg.scaledMaxChaRte.getValue(fronius1)
|
||||||
print("Pre rate: %d" % rate)
|
print(" rate: %d" % rate)
|
||||||
|
|
||||||
rate = froniusreg.scaledMaxWChaGra.getValue(fronius1)
|
rate = froniusreg.scaledMaxWChaGra.getValue(fronius1)
|
||||||
print("Pre WGra rate: %d" % rate)
|
print(" WGra rate: %d" % rate)
|
||||||
|
|
||||||
revert = froniusreg.InOutWRte_RvrtTms.getValue(fronius1)
|
revert = froniusreg.InOutWRte_RvrtTms.getValue(fronius1)
|
||||||
print("Timer: %d" % revert)
|
print("Timer: %d" % revert)
|
||||||
|
|
|
@ -29,16 +29,25 @@ class DataType():
|
||||||
|
|
||||||
|
|
||||||
# helper functions for DataType constructors
|
# helper functions for DataType constructors
|
||||||
def decode_string(decoder, value):
|
def decode_string8(decoder):
|
||||||
return str(decoder.decode_string(16).decode('utf-8'))
|
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):
|
def encode_16bit_int(encoder, value):
|
||||||
return encoder.add_16bit_int(int(value))
|
return encoder.add_16bit_int(int(value))
|
||||||
|
|
||||||
# The various data types that the fronius inverters use
|
# The various data types that the fronius inverters use
|
||||||
string8 = DataType(4, decode_string, BinaryPayloadBuilder.add_string)
|
string8 = DataType(4, decode_string8, BinaryPayloadBuilder.add_string)
|
||||||
string16 = DataType(8, decode_string, BinaryPayloadBuilder.add_string)
|
string16 = DataType(8, decode_string16, BinaryPayloadBuilder.add_string)
|
||||||
string32 = DataType(16, decode_string, BinaryPayloadBuilder.add_string)
|
string32 = DataType(16, decode_string32, BinaryPayloadBuilder.add_string)
|
||||||
int16 = DataType(1, BinaryPayloadDecoder.decode_16bit_int, encode_16bit_int)
|
int16 = DataType(1, BinaryPayloadDecoder.decode_16bit_int, encode_16bit_int)
|
||||||
uint16 = DataType(1, BinaryPayloadDecoder.decode_16bit_uint, encode_16bit_int)
|
uint16 = DataType(1, BinaryPayloadDecoder.decode_16bit_uint, encode_16bit_int)
|
||||||
int32 = DataType(2, BinaryPayloadDecoder.decode_32bit_int, BinaryPayloadBuilder.add_32bit_int)
|
int32 = DataType(2, BinaryPayloadDecoder.decode_32bit_int, BinaryPayloadBuilder.add_32bit_int)
|
||||||
|
@ -80,7 +89,7 @@ class FroniusReg:
|
||||||
self.datatype.width,
|
self.datatype.width,
|
||||||
slave=self.unit)
|
slave=self.unit)
|
||||||
if(modbusValue.isError()):
|
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):
|
if(modbusValue is None):
|
||||||
raise registerReadError("It's NONE!")
|
raise registerReadError("It's NONE!")
|
||||||
return self.datatype.decode_from_register(modbusValue.registers)
|
return self.datatype.decode_from_register(modbusValue.registers)
|
||||||
|
|
|
@ -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")
|
storageStateOfChargeSF = froniusreg.FroniusReg(40376, froniusreg.int16, 1, "Storage State of Charge Scaling Factor")
|
||||||
scaledStateOfCharge = froniusreg.ScaledFroniusReg(storageStateOfCharge, storageStateOfChargeSF)
|
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")
|
OutWRte = froniusreg.FroniusReg(40366, froniusreg.int16, 1, "DischargeRate")
|
||||||
InWRte = froniusreg.FroniusReg(40367, froniusreg.int16, 1, "ChargeRate")
|
InWRte = froniusreg.FroniusReg(40367, froniusreg.int16, 1, "ChargeRate")
|
||||||
WRteSF = froniusreg.FroniusReg(40379, froniusreg.int16, 1, "ScalingFactor for storage Watts")
|
WRteSF = froniusreg.FroniusReg(40379, froniusreg.int16, 1, "ScalingFactor for storage Watts")
|
||||||
|
|
|
@ -18,6 +18,23 @@ class TestRead(unittest.TestCase):
|
||||||
dr = gen24_registers.OutWRte.getValue(fronius1)
|
dr = gen24_registers.OutWRte.getValue(fronius1)
|
||||||
assert isinstance(dr, int)
|
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):
|
def test_write_direct(self):
|
||||||
current = gen24_registers.OutWRte.getValue(fronius1)
|
current = gen24_registers.OutWRte.getValue(fronius1)
|
||||||
retval = gen24_registers.OutWRte.setValue(fronius1, current)
|
retval = gen24_registers.OutWRte.setValue(fronius1, current)
|
||||||
|
|
Loading…
Reference in a new issue