diff --git a/README.md b/README.md index 6eb375a..b46c951 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,8 @@ The following registers are provided by the Sun2000's Modbus interface and can b | ModelID | Number | 1 | | | | NumberOfPVStrings | Number | 1 | | | | NumberOfMPPTrackers | Number | 1 | | | -| RatedPower | Number | 1000 | kW | | -| MaximumActivePower | Number | 1000 | kW | | +| RatedPower | Number | 1 | W | | +| MaximumActivePower | Number | 1 | W | | | MaximumApparentPower | Number | 1000 | kVA | | | MaximumReactivePowerFedToTheGrid | Number | 1000 | kvar | | | MaximumReactivePowerAbsorbedFromTheGrid | Number | 1000 | kvar | | @@ -99,7 +99,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | PV3Current | Number | 100 | A | | | PV4Voltage | Number | 10 | V | | | PV4Current | Number | 100 | A | | -| InputPower | Number | 1000 | kW | | +| InputPower | Number | 1 | W | | | LineVoltageBetweenPhasesAAndB | Number | 10 | V | | | LineVoltageBetweenPhasesBAndC | Number | 10 | V | | | LineVoltageBetweenPhasesCAndA | Number | 10 | V | | @@ -109,8 +109,8 @@ The following registers are provided by the Sun2000's Modbus interface and can b | PhaseACurrent | Number | 1000 | A | | | PhaseBCurrent | Number | 1000 | A | | | PhaseCCurrent | Number | 1000 | A | | -| PeakActivePowerOfCurrentDay | Number | 1000 | kW | | -| ActivePower | Number | 1000 | kW | | +| PeakActivePowerOfCurrentDay | Number | 1 | W | | +| ActivePower | Number | 1 | W | | | ReactivePower | Number | 1000 | kvar | | | PowerFactor | Number | 1000 | | | | GridFrequency | Number | 100 | Hz | | @@ -183,17 +183,17 @@ The following registers are provided by the Sun2000's Modbus interface and can b | GridChargeCutoffSOC | Number | 10 | % | | | ForcibleChargeDischarge | Number | 1 | | Write only, not available for read | | FixedChargingAndDischargingPeriods | Bytestring | | | | -| PowerOfChargeFromGrid | Number | 100 | kW | | -| MaximumPowerOfChargeFromGrid | Number | 100 | kW | | +| PowerOfChargeFromGrid | Number | 0.1 | W | | +| MaximumPowerOfChargeFromGrid | Number | 0.1 | W | | | ForcibleChargeDischargeSettingMode | Number | 1 | | | -| ForcibleChargePower | Number | 100 | kW | | -| ForcibleDischargePower | Number | 100 | kW | | +| ForcibleChargePower | Number | 0.1 | W | | +| ForcibleDischargePower | Number | 0.1 | W | | | TimeOfUseChargingAndDischargingPeriods | Bytestring | | | | | ExcessPVEnergyUseInTOU | Number | 1 | | | | ActivePowerControlMode | Number | 1 | | | | MaximumFeedGridPowerInKW | Number | 1000 | kW | | | MaximumFeedGridPowerInPercentage | Number | 10 | % | | -| MaximumChargeFromGridPower | Number | 100 | kW | | +| MaximumChargeFromGridPower | Number | 0.1 | W | | | SwitchToOffGrid | Number | 1 | | | | VoltageInIndependentOperation | Number | 1 | | | | Unit1ProductModel | Number | 1 | | | @@ -238,7 +238,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit1BatteryPack1Voltage | Number | 10 | V | | | Unit1BatteryPack1Current | Number | 10 | A | | | Unit1BatteryPack1SOC | Number | 10 | % | | -| Unit1BatteryPack1ChargeDischargePower | Number | 100 | kW | | +| Unit1BatteryPack1ChargeDischargePower | Number | 0.1 | W | | | Unit1BatteryPack1TotalCharge | Number | 100 | kWh | | | Unit1BatteryPack1TotalDischarge | Number | 100 | kWh | | | Unit1BatteryPack1MinimumTemperature | Number | 10 | °C | | @@ -250,7 +250,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit1BatteryPack2Voltage | Number | 10 | V | | | Unit1BatteryPack2Current | Number | 10 | A | | | Unit1BatteryPack2SOC | Number | 10 | % | | -| Unit1BatteryPack2ChargeDischargePower | Number | 100 | kW | | +| Unit1BatteryPack2ChargeDischargePower | Number | 0.1 | W | | | Unit1BatteryPack2TotalCharge | Number | 100 | kWh | | | Unit1BatteryPack2TotalDischarge | Number | 100 | kWh | | | Unit1BatteryPack2MinimumTemperature | Number | 10 | °C | | @@ -262,7 +262,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit1BatteryPack3Voltage | Number | 10 | V | | | Unit1BatteryPack3Current | Number | 10 | A | | | Unit1BatteryPack3SOC | Number | 10 | % | | -| Unit1BatteryPack3ChargeDischargeStatus | Number | 100 | kW | | +| Unit1BatteryPack3ChargeDischargeStatus | Number | 0.1 | W | | | Unit1BatteryPack3TotalCharge | Number | 100 | kWh | | | Unit1BatteryPack3TotalDischarge | Number | 100 | kWh | | | Unit1BatteryPack3MinimumTemperature | Number | 10 | °C | | @@ -274,7 +274,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit2BatteryPack1Voltage | Number | 10 | V | | | Unit2BatteryPack1Current | Number | 10 | A | | | Unit2BatteryPack1SOC | Number | 10 | % | | -| Unit2BatteryPack1ChargeDischargePower | Number | 100 | kW | | +| Unit2BatteryPack1ChargeDischargePower | Number | 0.1 | W | | | Unit2BatteryPack1TotalCharge | Number | 100 | kWh | | | Unit2BatteryPack1TotalDischarge | Number | 100 | kWh | | | Unit2BatteryPack1MinimumTemperature | Number | 10 | °C | | @@ -286,7 +286,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit2BatteryPack2Voltage | Number | 10 | V | | | Unit2BatteryPack2Current | Number | 10 | A | | | Unit2BatteryPack2SOC | Number | 10 | % | | -| Unit2BatteryPack2ChargeDischargePower | Number | 100 | kW | | +| Unit2BatteryPack2ChargeDischargePower | Number | 0.1 | W | | | Unit2BatteryPack2TotalCharge | Number | 100 | kWh | | | Unit2BatteryPack2TotalDischarge | Number | 100 | kWh | | | Unit2BatteryPack2MinimumTemperature | Number | 10 | °C | | @@ -298,7 +298,7 @@ The following registers are provided by the Sun2000's Modbus interface and can b | Unit2BatteryPack3Voltage | Number | 10 | V | | | Unit2BatteryPack3Current | Number | 10 | A | | | Unit2BatteryPack3SOC | Number | 10 | % | | -| Unit2BatteryPack3ChargeDischargePower | Number | 100 | kW | | +| Unit2BatteryPack3ChargeDischargePower | Number | 0.1 | W | | | Unit2BatteryPack3TotalCharge | Number | 100 | kWh | | | Unit2BatteryPack3TotalDischarge | Number | 100 | kWh | | | Unit2BatteryPack3MinimumTemperature | Number | 10 | °C | | diff --git a/sun2000_modbus/inverter.py b/sun2000_modbus/inverter.py index 7a307f2..a1bff2d 100644 --- a/sun2000_modbus/inverter.py +++ b/sun2000_modbus/inverter.py @@ -10,34 +10,44 @@ class Sun2000: - def __init__(self, host, port=502, timeout=5, wait=2, unit=0): + def __init__(self, host, port=502, timeout=5, wait=2, unit=0): # some models need unit=1 self.wait = wait - self.connected = False self.unit = unit self.inverter = ModbusTcpClient(host, port, timeout=timeout) def connect(self): - if not self.connected: - self.connected = self.inverter.connect() + if not self.isConnected(): + self.inverter.connect() time.sleep(self.wait) - if self.connected: + if self.isConnected(): logging.info('Successfully connected to inverter') + return True else: logging.error('Connection to inverter failed') + return False + + def disconnect(self): + """Close the underlying tcp socket""" + # Some Sun2000 models with the SDongle WLAN-FE require the TCP connection to be closed + # as soon as possible. Leaving the TCP connection open for an extended time may cause + # dongle reboots and/or FusionSolar portal updates to be delayed or even paused. + self.inverter.close() + + def isConnected(self): + """Check if underlying tcp socket is open""" + return self.inverter.is_socket_open() def read_raw_value(self, register): - if not self.connected: + if not self.isConnected(): raise ValueError('Inverter is not connected') try: register_value = self.inverter.read_holding_registers(register.value.address, register.value.quantity, unit=self.unit) if type(register_value) == ModbusIOException: logging.error("Inverter unit did not respond") - self.connected = False raise register_value except ConnectionException: logging.error("A connection error occurred") - self.connected = False raise return datatypes.decode(register_value.encode()[1:], register.value.data_type) @@ -68,8 +78,7 @@ def read_range(self, start_address, quantity=0, end_address=0): if end_address != 0 and end_address <= start_address: raise ValueError("end_address must be greater than start_address") - if not self.connected: - self.connected = False + if not self.isConnected(): raise ValueError('Inverter is not connected') if end_address != 0: @@ -78,11 +87,9 @@ def read_range(self, start_address, quantity=0, end_address=0): register_range_value = self.inverter.read_holding_registers(start_address, quantity, unit=self.unit) if type(register_range_value) == ModbusIOException: logging.error("Inverter unit did not respond") - self.connected = False raise register_range_value except ConnectionException: logging.error("A connection error occurred") - self.connected = False raise return datatypes.decode(register_range_value.encode()[1:], datatypes.DataType.MULTIDATA) diff --git a/sun2000_modbus/registers.py b/sun2000_modbus/registers.py index 204ebf2..34d29f3 100644 --- a/sun2000_modbus/registers.py +++ b/sun2000_modbus/registers.py @@ -14,7 +14,7 @@ class Register: address: int quantity: int data_type: datatypes.DataType - gain: int + gain: float unit: str access_type: AccessType mapping: dict @@ -36,8 +36,8 @@ class InverterEquipmentRegister(Enum): ModelID = Register(30070, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RO, None) NumberOfPVStrings = Register(30071, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RO, None) NumberOfMPPTrackers = Register(30072, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RO, None) - RatedPower = Register(30073, 2, datatypes.DataType.UINT32_BE, 1000, "kW", AccessType.RO, None) - MaximumActivePower = Register(30075, 2, datatypes.DataType.UINT32_BE, 1000, "kW", AccessType.RO, None) + RatedPower = Register(30073, 2, datatypes.DataType.UINT32_BE, 1, "W", AccessType.RO, None) + MaximumActivePower = Register(30075, 2, datatypes.DataType.UINT32_BE, 1, "W", AccessType.RO, None) MaximumApparentPower = Register(30077, 2, datatypes.DataType.UINT32_BE, 1000, "kVA", AccessType.RO, None) MaximumReactivePowerFedToTheGrid = Register(30079, 2, datatypes.DataType.INT32_BE, 1000, "kvar", AccessType.RO, None) MaximumReactivePowerAbsorbedFromTheGrid = Register(30081, 2, datatypes.DataType.INT32_BE, 1000, "kvar", AccessType.RO, None) @@ -55,7 +55,7 @@ class InverterEquipmentRegister(Enum): PV3Current = Register(32021, 1, datatypes.DataType.INT16_BE, 100, "A", AccessType.RO, None) PV4Voltage = Register(32022, 1, datatypes.DataType.INT16_BE, 10, "V", AccessType.RO, None) PV4Current = Register(32023, 1, datatypes.DataType.INT16_BE, 100, "A", AccessType.RO, None) - InputPower = Register(32064, 2, datatypes.DataType.INT32_BE, 1000, "kW", AccessType.RO, None) + InputPower = Register(32064, 2, datatypes.DataType.INT32_BE, 1, "W", AccessType.RO, None) LineVoltageBetweenPhasesAAndB = Register(32066, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) LineVoltageBetweenPhasesBAndC = Register(32067, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) LineVoltageBetweenPhasesCAndA = Register(32068, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) @@ -65,8 +65,8 @@ class InverterEquipmentRegister(Enum): PhaseACurrent = Register(32072, 2, datatypes.DataType.INT32_BE, 1000, "A", AccessType.RO, None) PhaseBCurrent = Register(32074, 2, datatypes.DataType.INT32_BE, 1000, "A", AccessType.RO, None) PhaseCCurrent = Register(32076, 2, datatypes.DataType.INT32_BE, 1000, "A", AccessType.RO, None) - PeakActivePowerOfCurrentDay = Register(32078, 2, datatypes.DataType.INT32_BE, 1000, "kW", AccessType.RO, None) - ActivePower = Register(32080, 2, datatypes.DataType.INT32_BE, 1000, "kW", AccessType.RO, None) + PeakActivePowerOfCurrentDay = Register(32078, 2, datatypes.DataType.INT32_BE, 1, "W", AccessType.RO, None) + ActivePower = Register(32080, 2, datatypes.DataType.INT32_BE, 1, "W", AccessType.RO, None) ReactivePower = Register(32082, 2, datatypes.DataType.INT32_BE, 1000, "kvar", AccessType.RO, None) PowerFactor = Register(32084, 1, datatypes.DataType.INT16_BE, 1000, None, AccessType.RO, None) GridFrequency = Register(32085, 1, datatypes.DataType.UINT16_BE, 100, "Hz", AccessType.RO, None) @@ -138,17 +138,17 @@ class BatteryEquipmentRegister(Enum): GridChargeCutoffSOC = Register(47088, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RW, None) # ForcibleChargeDischarge = Register(47100, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.WO, mappings.ForcibleChargeDischarge) # disabled because not readable (AccessType.WO) FixedChargingAndDischargingPeriods = Register(47200, 41, datatypes.DataType.MULTIDATA, None, None, AccessType.RW, None) - PowerOfChargeFromGrid = Register(47242, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RW, None) - MaximumPowerOfChargeFromGrid = Register(47244, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RW, None) + PowerOfChargeFromGrid = Register(47242, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RW, None) + MaximumPowerOfChargeFromGrid = Register(47244, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RW, None) ForcibleChargeDischargeSettingMode = Register(47246, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RW, mappings.ForcibleChargeDischargeSettingMode) - ForcibleChargePower = Register(47247, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RW, None) - ForcibleDischargePower = Register(47249, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RW, None) + ForcibleChargePower = Register(47247, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RW, None) + ForcibleDischargePower = Register(47249, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RW, None) TimeOfUseChargingAndDischargingPeriods = Register(47255, 43, datatypes.DataType.MULTIDATA, None, None, AccessType.RW, None) ExcessPVEnergyUseInTOU = Register(47299, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RW, mappings.ExcessPVEnergyUseInTOU) ActivePowerControlMode = Register(47415, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RW, mappings.ActivePowerControlMode) MaximumFeedGridPowerInKW = Register(47416, 2, datatypes.DataType.INT32_BE, 1000, "kW", AccessType.RW, None) MaximumFeedGridPowerInPercentage = Register(47418, 1, datatypes.DataType.INT16_BE, 10, "%", AccessType.RW, None) - MaximumChargeFromGridPower = Register(47590, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RW, None) + MaximumChargeFromGridPower = Register(47590, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RW, None) SwitchToOffGrid = Register(47604, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RW, mappings.SwitchToOffGrid) VoltageInIndependentOperation = Register(47605, 1, datatypes.DataType.UINT16_BE, 1, None, AccessType.RW, mappings.VoltageIndependentOperation) @@ -199,7 +199,7 @@ class BatteryEquipmentRegister(Enum): Unit1BatteryPack1Voltage = Register(38235, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit1BatteryPack1Current = Register(38236, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit1BatteryPack1SOC = Register(38229, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit1BatteryPack1ChargeDischargePower = Register(38233, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit1BatteryPack1ChargeDischargePower = Register(38233, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit1BatteryPack1TotalCharge = Register(38238, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack1TotalDischarge = Register(38240, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack1MinimumTemperature = Register(38453, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) @@ -213,7 +213,7 @@ class BatteryEquipmentRegister(Enum): Unit1BatteryPack2Voltage = Register(38277, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit1BatteryPack2Current = Register(38278, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit1BatteryPack2SOC = Register(38271, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit1BatteryPack2ChargeDischargePower = Register(38275, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit1BatteryPack2ChargeDischargePower = Register(38275, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit1BatteryPack2TotalCharge = Register(38280, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack2TotalDischarge = Register(38282, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack2MinimumTemperature = Register(38455, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) @@ -227,7 +227,7 @@ class BatteryEquipmentRegister(Enum): Unit1BatteryPack3Voltage = Register(38319, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit1BatteryPack3Current = Register(38320, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit1BatteryPack3SOC = Register(38313, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit1BatteryPack3ChargeDischargeStatus = Register(38317, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit1BatteryPack3ChargeDischargeStatus = Register(38317, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit1BatteryPack3TotalCharge = Register(38322, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack3TotalDischarge = Register(38324, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit1BatteryPack3MinimumTemperature = Register(38457, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) @@ -241,7 +241,7 @@ class BatteryEquipmentRegister(Enum): Unit2BatteryPack1Voltage = Register(38361, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit2BatteryPack1Current = Register(38362, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit2BatteryPack1SOC = Register(38355, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit2BatteryPack1ChargeDischargePower = Register(38359, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit2BatteryPack1ChargeDischargePower = Register(38359, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit2BatteryPack1TotalCharge = Register(38364, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack1TotalDischarge = Register(38366, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack1MinimumTemperature = Register(38459, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) @@ -255,7 +255,7 @@ class BatteryEquipmentRegister(Enum): Unit2BatteryPack2Voltage = Register(38403, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit2BatteryPack2Current = Register(38404, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit2BatteryPack2SOC = Register(38397, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit2BatteryPack2ChargeDischargePower = Register(38401, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit2BatteryPack2ChargeDischargePower = Register(38401, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit2BatteryPack2TotalCharge = Register(38406, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack2TotalDischarge = Register(38408, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack2MinimumTemperature = Register(38461, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) @@ -269,7 +269,7 @@ class BatteryEquipmentRegister(Enum): Unit2BatteryPack3Voltage = Register(38445, 1, datatypes.DataType.UINT16_BE, 10, "V", AccessType.RO, None) Unit2BatteryPack3Current = Register(38446, 1, datatypes.DataType.INT16_BE, 10, "A", AccessType.RO, None) Unit2BatteryPack3SOC = Register(38439, 1, datatypes.DataType.UINT16_BE, 10, "%", AccessType.RO, None) - Unit2BatteryPack3ChargeDischargePower = Register(38443, 2, datatypes.DataType.INT32_BE, 100, "kW", AccessType.RO, None) + Unit2BatteryPack3ChargeDischargePower = Register(38443, 2, datatypes.DataType.INT32_BE, 0.1, "W", AccessType.RO, None) Unit2BatteryPack3TotalCharge = Register(38448, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack3TotalDischarge = Register(38450, 2, datatypes.DataType.INT32_BE, 100, "kWh", AccessType.RO, None) Unit2BatteryPack3MinimumTemperature = Register(38463, 1, datatypes.DataType.INT16_BE, 10, "°C", AccessType.RO, None) diff --git a/tests/test_sun2000_modbus.py b/tests/test_sun2000_modbus.py index 06b6f0c..436d2b6 100644 --- a/tests/test_sun2000_modbus.py +++ b/tests/test_sun2000_modbus.py @@ -64,21 +64,24 @@ def test_init(self): self.assertEqual(self.test_inverter.inverter.timeout, 3) self.assertEqual(self.test_inverter.wait, 0) self.assertEqual(self.test_inverter.unit, 1) - self.assertEqual(self.test_inverter.connected, False) + self.assertEqual(self.test_inverter.isConnected(), False) @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_connect_success(self): self.test_inverter.connect() - self.assertTrue(self.test_inverter.connected) + self.assertTrue(self.test_inverter.isConnected()) @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_fail ) def test_connect_fail(self): self.test_inverter.connect() - self.assertFalse(self.test_inverter.connected) + self.assertFalse(self.test_inverter.isConnected()) @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_fail @@ -93,10 +96,12 @@ def test_read_raw_value_string_from_disconnected_unit(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_string_from_unavailable_unit(self): self.test_inverter.connect() self.assertRaises(ModbusIOException, self.test_inverter.read_raw_value, InverterEquipmentRegister.Model) - self.assertFalse(self.test_inverter.connected) @patch( 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers_ConnectionException @@ -104,10 +109,12 @@ def test_read_raw_value_string_from_unavailable_unit(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_string_connection_unexpectedly_closed(self): self.test_inverter.connect() self.assertRaises(ConnectionException, self.test_inverter.read_raw_value, InverterEquipmentRegister.Model) - self.assertFalse(self.test_inverter.connected) @patch( 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers @@ -115,6 +122,9 @@ def test_read_raw_value_string_connection_unexpectedly_closed(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_string(self): self.test_inverter.connect() result = self.test_inverter.read_raw_value(InverterEquipmentRegister.Model) @@ -126,6 +136,9 @@ def test_read_raw_value_string(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_uint16be(self): self.test_inverter.connect() result = self.test_inverter.read_raw_value(InverterEquipmentRegister.ModelID) @@ -137,6 +150,9 @@ def test_read_raw_value_uint16be(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_uint32be(self): self.test_inverter.connect() result = self.test_inverter.read_raw_value(InverterEquipmentRegister.RatedPower) @@ -148,10 +164,13 @@ def test_read_raw_value_uint32be(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_uint32be(self): self.test_inverter.connect() result = self.test_inverter.read(InverterEquipmentRegister.RatedPower) - self.assertEqual(result, 10.0) + self.assertEqual(result, 10000.0) @patch( 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers @@ -159,10 +178,13 @@ def test_read_uint32be(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_formatted_uint32be(self): self.test_inverter.connect() result = self.test_inverter.read_formatted(InverterEquipmentRegister.RatedPower) - self.assertEqual(result, "10.0 kW") + self.assertEqual(result, "10000.0 W") @patch( 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers @@ -170,6 +192,9 @@ def test_read_formatted_uint32be(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_bitfield16(self): self.test_inverter.connect() result = self.test_inverter.read_raw_value(InverterEquipmentRegister.State1) @@ -181,6 +206,9 @@ def test_read_raw_value_bitfield16(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_bitfield16(self): self.test_inverter.connect() result = self.test_inverter.read(InverterEquipmentRegister.State1) @@ -192,6 +220,9 @@ def test_read_bitfield16(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_formatted_bitfield16(self): self.test_inverter.connect() result = self.test_inverter.read_formatted(InverterEquipmentRegister.State1) @@ -203,6 +234,9 @@ def test_read_formatted_bitfield16(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_raw_value_with_mapping(self): self.test_inverter.connect() result = self.test_inverter.read_raw_value(InverterEquipmentRegister.DeviceStatus) @@ -214,6 +248,9 @@ def test_read_raw_value_with_mapping(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_formatted_with_mapping(self): self.test_inverter.connect() result = self.test_inverter.read_formatted(InverterEquipmentRegister.DeviceStatus) @@ -225,6 +262,9 @@ def test_read_formatted_with_mapping(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_returns_float(self): self.test_inverter.connect() result = self.test_inverter.read(MeterEquipmentRegister.ActivePower) @@ -237,6 +277,9 @@ def test_read_returns_float(self): @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_range_returns_range_of_register_values(self): self.test_inverter.connect() result = self.test_inverter.read_range(30000, quantity=35) @@ -282,23 +325,27 @@ def test_read_range_from_disconnected_unit(self): self.assertRaises(ValueError, self.test_inverter.read_range, 30000, quantity=35) @patch( - 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers_ModbusIOException + 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers_ConnectionException ) @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) def test_read_range_from_unavailable_unit(self): self.test_inverter.connect() - self.assertRaises(ModbusIOException, self.test_inverter.read_range, 30000, quantity=35) - self.assertFalse(self.test_inverter.connected) + self.assertRaises(ConnectionException, self.test_inverter.read_range, 30000, quantity=35) @patch( - 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers_ConnectionException + 'pymodbus.client.sync.ModbusTcpClient.read_holding_registers', sun2000mock.mock_read_holding_registers_ModbusIOException ) @patch( 'pymodbus.client.sync.ModbusTcpClient.connect', sun2000mock.connect_success ) - def test_read_range_from_unavailable_unit(self): + @patch( + 'pymodbus.client.sync.ModbusTcpClient.is_socket_open', sun2000mock.connect_success + ) + def test_read_range_from_unavailable_unit2(self): self.test_inverter.connect() - self.assertRaises(ConnectionException, self.test_inverter.read_range, 30000, quantity=35) - self.assertFalse(self.test_inverter.connected) + self.assertRaises(ModbusIOException, self.test_inverter.read_range, 30000, quantity=35)