Compare commits

..

No commits in common. "65b84d15030523dc6a098a136ba40bf8b2926d47" and "f6f7a4ae294db005b66e92dd7c86586223dcdc6b" have entirely different histories.

7 changed files with 68 additions and 50 deletions

View file

@ -38,3 +38,4 @@ print("Post CRate: %d%%" % charge)
mode = froniusreg.StorCtl_Mode.getValue(fronius1) mode = froniusreg.StorCtl_Mode.getValue(fronius1)
print("Post Mode: %d" % mode) print("Post Mode: %d" % mode)

View file

@ -6,7 +6,6 @@ import os
def lint(session): def lint(session):
session.install("ruff") session.install("ruff")
session.run("ruff", "check", "--exclude", "examples") session.run("ruff", "check", "--exclude", "examples")
session.run("ruff", "check", "--extend-select", "N", "src/pyfroniusreg/froniusreg.py")
@nox.session @nox.session

View file

@ -32,10 +32,10 @@ Issues = "https://git.pwarren.id.au/pwarren/PyFroniusReg/issues"
[tool.ruff] [tool.ruff]
# Set the maximum line length to 79. # Set the maximum line length to 79.
line-length = 99 line-length = 79
[tool.ruff.lint] [tool.ruff.lint]
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that # Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
# overlap with the use of a formatter, like Black, but we can override this behavior by # overlap with the use of a formatter, like Black, but we can override this behavior by
# explicitly adding the rule. # explicitly adding the rule.
extend-select = ["E501", "T20", "E", "PL"] extend-select = ["E501"]

View file

@ -24,6 +24,7 @@ class DataType:
return self._decode(decoder) return self._decode(decoder)
def encode_to_buffer(self, value): def encode_to_buffer(self, value):
print(type(value))
encoder = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.BIG) encoder = BinaryPayloadBuilder(byteorder=Endian.BIG, wordorder=Endian.BIG)
self._add(encoder, value) self._add(encoder, value)
return encoder.build() return encoder.build()
@ -61,15 +62,21 @@ string16 = DataType(8, decode_string16, BinaryPayloadBuilder.add_string)
string32 = DataType(16, decode_string32, 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(
uint32 = DataType(2, BinaryPayloadDecoder.decode_32bit_uint, BinaryPayloadBuilder.add_32bit_uint) 2, BinaryPayloadDecoder.decode_32bit_int, BinaryPayloadBuilder.add_32bit_int
)
uint32 = DataType(
2, BinaryPayloadDecoder.decode_32bit_uint, BinaryPayloadBuilder.add_32bit_uint
)
float32 = DataType( float32 = DataType(
2, BinaryPayloadDecoder.decode_32bit_float, BinaryPayloadBuilder.add_32bit_float 2, BinaryPayloadDecoder.decode_32bit_float, BinaryPayloadBuilder.add_32bit_float
) )
uint64 = DataType(4, BinaryPayloadDecoder.decode_64bit_uint, BinaryPayloadBuilder.add_64bit_uint) uint64 = DataType(
4, BinaryPayloadDecoder.decode_64bit_uint, BinaryPayloadBuilder.add_64bit_uint
)
class RegisterReadError(Exception): class registerReadError(Exception):
pass pass
@ -84,8 +91,7 @@ class RegisterReadError(Exception):
# Constructor parameters: # Constructor parameters:
# address: address as specified in the spreadsheet # address: address as specified in the spreadsheet
# datatype: One of the above datatypes, as specified for the address in the spreadsheet # datatype: One of the above datatypes, as specified for the address in the spreadsheet
# unit: the modbus unit, either '1' for the fronius inverter or 200 for the attached fronius # unit: the modbus unit, either '1' for the fronius inverter or 200 for the attached fronius smart meter.
# smart meter.
# description: free text to describe the register's purpose # description: free text to describe the register's purpose
class FroniusReg: class FroniusReg:
def __init__(self, address, datatype, unit, description): def __init__(self, address, datatype, unit, description):
@ -94,46 +100,46 @@ class FroniusReg:
self.unit = unit self.unit = unit
self.description = description self.description = description
def get(self, modbus_client): def getValue(self, modbusClient):
return self.__get_register_value(modbus_client) return self.__getRegisterValue(modbusClient)
def set(self, modbus_client, value): def setValue(self, modbusClient, value):
return self._set_register_value(modbus_client, value) return self.__setRegisterValue(modbusClient, value)
def __get_register_value(self, modbus_client): def __getRegisterValue(self, modbusClient):
modbus_value = modbus_client.read_holding_registers( modbusValue = modbusClient.read_holding_registers(
self.address - 1, self.datatype.width, slave=self.unit self.address - 1, self.datatype.width, slave=self.unit
) )
if modbus_value.isError(): if modbusValue.isError():
raise RegisterReadError( raise registerReadError(
"Unable to read from Fronius Register: %d, %s\n%s" "Unable to read from Fronius Register: %d, %s\n%s"
% (self.address, self.description, modbus_value) % (self.address, self.description, modbusValue)
) )
if modbus_value is None: if modbusValue is None:
raise RegisterReadError("It's NONE!") raise registerReadError("It's NONE!")
return self.datatype.decode_from_register(modbus_value.registers) return self.datatype.decode_from_register(modbusValue.registers)
def _set_register_value(self, modbus_client, value): def __setRegisterValue(self, modbusClient, value):
modbus_value = modbus_client.write_registers( modbusValue = modbusClient.write_registers(
self.address - 1, self.address - 1,
self.datatype.encode_to_buffer(value), self.datatype.encode_to_buffer(value),
slave=self.unit, slave=self.unit,
skip_encode=True, skip_encode=True,
) )
return modbus_value return modbusValue
class ScaledFroniusReg: class ScaledFroniusReg:
def __init__(self, value_register, scale_register): def __init__(self, valueReg, scaleReg):
self.value_register = value_register self.valueReg = valueReg
self.scale_register = scale_register self.scaleReg = scaleReg
def get(self, modbus_client): def getValue(self, modbusClient):
return self.value_register.get(modbus_client) * 10 ** self.scale_register.get( return self.valueReg.getValue(modbusClient) * 10 ** self.scaleReg.getValue(
modbus_client modbusClient
) )
def set(self, modbus_client, value): def setValue(self, modbusClient, value):
return self.value_register.set( return self.valueReg.setValue(
modbus_client, value / 10 ** self.scale_register.get(modbus_client) modbusClient, value / 10 ** self.scaleReg.getValue(modbusClient)
) )

View file

@ -10,7 +10,9 @@ storageStateOfCharge = froniusreg.FroniusReg(
storageStateOfChargeSF = froniusreg.FroniusReg( storageStateOfChargeSF = froniusreg.FroniusReg(
40376, froniusreg.int16, 1, "Storage State of Charge Scaling Factor" 40376, froniusreg.int16, 1, "Storage State of Charge Scaling Factor"
) )
scaledStateOfCharge = froniusreg.ScaledFroniusReg(storageStateOfCharge, storageStateOfChargeSF) scaledStateOfCharge = froniusreg.ScaledFroniusReg(
storageStateOfCharge, storageStateOfChargeSF
)
ID = froniusreg.FroniusReg( ID = froniusreg.FroniusReg(
40003, 40003,
@ -18,7 +20,9 @@ ID = froniusreg.FroniusReg(
1, 1,
"Well-known value. Uniquely identifies this as a sunspec model 'common' (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") L = froniusreg.FroniusReg(
40004, froniusreg.uint16, 1, "Sunspec model commen register count"
)
Mn = froniusreg.FroniusReg(40005, froniusreg.string16, 1, "Manufacturer") Mn = froniusreg.FroniusReg(40005, froniusreg.string16, 1, "Manufacturer")
Md = froniusreg.FroniusReg(40021, froniusreg.string16, 1, "Device Model") Md = froniusreg.FroniusReg(40021, froniusreg.string16, 1, "Device Model")
Vr = froniusreg.FroniusReg(40045, froniusreg.string8, 1, "SW version") Vr = froniusreg.FroniusReg(40045, froniusreg.string8, 1, "SW version")
@ -46,8 +50,12 @@ DCW_SF = froniusreg.FroniusReg(40268, froniusreg.int16, 1, "DC Power Scaling fac
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(
StorCtl_Mode = froniusreg.FroniusReg(40359, froniusreg.uint16, 1, "Hold/Charge/Discharge limit") 40379, froniusreg.int16, 1, "ScalingFactor for storage Watts"
)
StorCtl_Mode = froniusreg.FroniusReg(
40359, froniusreg.uint16, 1, "Hold/Charge/Discharge limit"
)
MinRsvPct = froniusreg.FroniusReg(40361, froniusreg.uint16, 1, "Reserve Percentage") MinRsvPct = froniusreg.FroniusReg(40361, froniusreg.uint16, 1, "Reserve Percentage")
InOutWRte_RvrtTms = froniusreg.FroniusReg( InOutWRte_RvrtTms = froniusreg.FroniusReg(
40369, froniusreg.uint16, 1, "Revert timer for charge settings" 40369, froniusreg.uint16, 1, "Revert timer for charge settings"
@ -55,8 +63,12 @@ InOutWRte_RvrtTms = froniusreg.FroniusReg(
ChaGriSet = froniusreg.FroniusReg( ChaGriSet = froniusreg.FroniusReg(
40371, froniusreg.uint16, 1, "enum16, 0 = PV only, 1 = Grid enabled" 40371, froniusreg.uint16, 1, "enum16, 0 = PV only, 1 = Grid enabled"
) )
WChaDisChaGra_SF = froniusreg.FroniusReg(40373, froniusreg.int16, 1, "Charge/Discharge Power SF") WChaDisChaGra_SF = froniusreg.FroniusReg(
MinRsvPct_SF = froniusreg.FroniusReg(40375, froniusreg.int16, 1, "Reserve Percentage Scaling") 40373, froniusreg.int16, 1, "Charge/Discharge Power SF"
)
MinRsvPct_SF = froniusreg.FroniusReg(
40375, froniusreg.int16, 1, "Reserve Percentage Scaling"
)
scaledOutWRte = froniusreg.ScaledFroniusReg(OutWRte, WRteSF) scaledOutWRte = froniusreg.ScaledFroniusReg(OutWRte, WRteSF)
scaledInWRte = froniusreg.ScaledFroniusReg(InWRte, WRteSF) scaledInWRte = froniusreg.ScaledFroniusReg(InWRte, WRteSF)

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
from pyfroniusreg import froniusreg import pyfroniusreg.froniusreg as froniusreg
class TestDataTypes(unittest.TestCase): class TestDataTypes(unittest.TestCase):

View file

@ -11,39 +11,39 @@ fronius1.connect()
class TestRead(unittest.TestCase): class TestRead(unittest.TestCase):
def test_read_scaled(self): def test_read_scaled(self):
soc = gen24_registers.scaledStateOfCharge.get(fronius1) soc = gen24_registers.scaledStateOfCharge.getValue(fronius1)
assert isinstance(soc, float) assert isinstance(soc, float)
def test_read_direct(self): def test_read_direct(self):
dr = gen24_registers.OutWRte.get(fronius1) dr = gen24_registers.OutWRte.getValue(fronius1)
assert isinstance(dr, int) assert isinstance(dr, int)
def test_read_string16(self): def test_read_string16(self):
Mn = gen24_registers.Mn.get(fronius1) Mn = gen24_registers.Mn.getValue(fronius1)
assert Mn[0:7] == "Fronius" assert Mn[0:7] == "Fronius"
def test_read_string8(self): def test_read_string8(self):
# this isn't a good test, this value changes regularly # this isn't a good test, this value changes regularly
Vr = gen24_registers.Vr.get(fronius1) Vr = gen24_registers.Vr.getValue(fronius1)
assert Vr == "1.33.7-1" assert Vr == "1.33.7-1"
def test_read_model(self): def test_read_model(self):
Md = gen24_registers.Md.get(fronius1) Md = gen24_registers.Md.getValue(fronius1)
assert Md == "Primo GEN24 5.0\x00" assert Md == "Primo GEN24 5.0\x00"
# def test_read_sn(self): # def test_read_sn(self):
# This doesn't seem to return anything useful # This doesn't seem to return anything useful
# SN = gen24_registers.SN.get(fronius1) # SN = gen24_registers.SN.getValue(fronius1)
# assert SN == "12345567" # assert SN == "12345567"
def test_write_direct(self): def test_write_direct(self):
current = gen24_registers.OutWRte.get(fronius1) current = gen24_registers.OutWRte.getValue(fronius1)
retval = gen24_registers.OutWRte.set(fronius1, current) retval = gen24_registers.OutWRte.setValue(fronius1, current)
assert retval is not None assert retval is not None
def test_write_scaled(self): def test_write_scaled(self):
current = gen24_registers.scaledInWRte.get(fronius1) current = gen24_registers.scaledInWRte.getValue(fronius1)
retval = gen24_registers.scaledInWRte.set(fronius1, current) retval = gen24_registers.scaledInWRte.setValue(fronius1, current)
assert retval is not None assert retval is not None