diff --git a/multiplus/plugin.py b/multiplus/plugin.py index db06c4f..fadd22a 100644 --- a/multiplus/plugin.py +++ b/multiplus/plugin.py @@ -7,7 +7,7 @@ Requirements: 2. pymodbus AND pymodbusTCP """ """ - + @@ -70,6 +70,10 @@ class Average: def get(self): return sum(self.samples) / len(self.samples) + def strget(self): + return str(sum(self.samples) / len(self.samples)) + + # # Domoticz shows graphs with intervals of 5 minutes. # When collecting information from the inverter more frequently than that, then it makes no sense to only show the last value. @@ -78,30 +82,29 @@ class Average: # The number of samples stored depends on the interval used to collect the value from the inverter itself. # -class Maximum: - - def __init__(self): - self.samples = [] - self.max_samples = 30 - - def set_max_samples(self, max): - self.max_samples = max - if self.max_samples < 1: - self.max_samples = 1 - - def update(self, new_value, scale = 0): - self.samples.append(new_value * (10 ** scale)) - while (len(self.samples) > self.max_samples): - del self.samples[0] - - Domoticz.Debug("Maximum: {} - {} values".format(self.get(), len(self.samples))) - - def get(self): - return max(self.samples) +#class Maximum: +# +# def __init__(self): +# self.samples = [] +# self.max_samples = 30 +# +# def set_max_samples(self, max): +# self.max_samples = max +# if self.max_samples < 1: +# self.max_samples = 1 +# +# def update(self, new_value, scale = 0): +# self.samples.append(new_value * (10 ** scale)) +# while (len(self.samples) > self.max_samples): +# del self.samples[0] +# +# Domoticz.Debug("Maximum: {} - {} values".format(self.get(), len(self.samples))) +# +# def get(self): +# return max(self.samples) # Plugin itself class BasePlugin: - #enabled = False def __init__(self): # AC IN Voltage for last 5 minutes self.acInVoltage=Average() @@ -170,7 +173,6 @@ class BasePlugin: Domoticz.Debug("Query IP " + self.IPAddress + ":" + str(self.IPPort) +" on GX device : "+str(self.MBAddr)+" Multi Device : "+str(self.MultiAddr)+" and Battery : "+str(self.BattAddr)) # Create the devices if they does not exists - # TODO: refactor this. # Multiplus Devices if 1 not in Devices: Domoticz.Device(Name="Voltage IN L1", Unit=1, TypeName="Voltage", Used=0).Create() @@ -210,18 +212,20 @@ class BasePlugin: # Victron if 30 not in Devices: Options = { "Custom": "1;W" } - Domoticz.Device(Name="Grid Power L1", Unit=30, TypeName="Custom", Used=0, Options=Options).Create() + Domoticz.Device(Name="Grid Power L1", Unit=30, TypeName="Custom", Used=0, Options=Options).Create() if 31 not in Devices: Options = { "Custom": "1;W" } - Domoticz.Device(Name="Consumption L1", Unit=31, TypeName="Custom", Used=0, Options=Options).Create() + Domoticz.Device(Name="Consumption L1", Unit=31, TypeName="Custom", Used=0, Options=Options).Create() if 32 not in Devices: Options = { "Custom": "1;W" } - Domoticz.Device(Name="PV on Output", Unit=32, TypeName="Custom", Used=0, Options=Options).Create() + Domoticz.Device(Name="PV on Output", Unit=32, TypeName="Custom", Used=0, Options=Options).Create() if 33 not in Devices: Options = { "Custom": "1;W" } - Domoticz.Device(Name="Battery Power", Unit=33, TypeName="Custom", Used=0, Options=Options).Create() + Domoticz.Device(Name="Battery Power", Unit=33, TypeName="Custom", Used=0, Options=Options).Create() if 34 not in Devices: - Domoticz.Device(Name="ESS Battery Life State", Unit=34, TypeName="Text", Used=0).Create() + Domoticz.Device(Name="ESS Battery Life State", Unit=34, TypeName="Text", Used=0).Create() + if 35 not in Devices: + Domoticz.Device(Name="ESS Battery Life SoC Limit", Unit=35, TypeName="Percentage", Used=0).Create() return @@ -229,22 +233,6 @@ class BasePlugin: def onStop(self): Domoticz.Debugging(0) - def onConnect(self, Connection, Status, Description): - Domoticz.Log("onConnect called") - return - - def onMessage(self, Connection, Data): - Domoticz.Log("onMessage called") - - def onCommand(self, Unit, Command, Level, Hue): - Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level)) - - def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile): - Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile) - - def onDisconnect(self, Connection): - Domoticz.Log("onDisconnect called") - def onHeartbeat(self): # Multiplus devices @@ -266,138 +254,39 @@ class BasePlugin: Devices[10].Update(1, "0") # Ac In Voltage - data = client.read_holding_registers(3, 1) - Domoticz.Debug("Data from register 3: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acInVoltage.update(value) - value = self.acInVoltage.get() - Domoticz.Debug(" = {}".format(value)) - Devices[1].Update(1, str(value)) + self.acInVoltage.update(round(getmodbus16(3, client)/10.0, 3)) + Devices[1].Update(1, self.acInVoltage.strget()) # Ac In Current - data = client.read_holding_registers(6, 1) - Domoticz.Debug("Data from register 6: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acInCurrent.update(value) - value = self.acInCurrent.get() - Domoticz.Debug(" = {}".format(value)) - Devices[2].Update(1, str(value)) + self.acInCurrent.update(round(getmodbus16(6, client)/10.0, 3)) + Devices[2].Update(1, self.acInCurrent.strget()) # Ac In Power - data = client.read_holding_registers(12, 1) - Domoticz.Debug("Data from register 12: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 0.1 - value = round (value / 0.1, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acInPower.update(value) - value = self.acInPower.get() - Domoticz.Debug(" = {}".format(value)) - Devices[3].Update(1, str(value)) + self.acInPower.update(round(getmodbus16(12, client)/0.1, 3)) + Devices[3].Update(1, self.acInPower.strget()) # Ac In Frequency - data = client.read_holding_registers(9, 1) - Domoticz.Debug("Data from register 9: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acInFrequency.update(value) - value = self.acInFrequency.get() - Domoticz.Debug(" = {}".format(value)) - Devices[4].Update(1, str(value)) + self.acInFrequency.update(round(getmodbus16(9, client)/100.0, 3)) + Devices[4].Update(1, self.acInFrequency.strget()) # Ac Out Voltage - data = client.read_holding_registers(15, 1) - Domoticz.Debug("Data from register 15: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acOutVoltage.update(value) - value = self.acOutVoltage.get() - Domoticz.Debug(" = {}".format(value)) - Devices[5].Update(1, str(value)) + self.acOutVoltage.update(round(getmodbus16(15, client)/10.0, 3)) + Devices[5].Update(1, self.acOutVoltage.strget()) # Ac Out Current - data = client.read_holding_registers(18, 1) - Domoticz.Debug("Data from register 18: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acOutCurrent.update(value) - value = self.acOutCurrent.get() - Domoticz.Debug(" = {}".format(value)) - Devices[6].Update(1, str(value)) + self.acOutCurrent.update(round(getmodbus16(18, client)/10.0, 3)) + Devices[6].Update(1, self.acOutCurrent.strget()) # Ac Out Power - data = client.read_holding_registers(23, 1) - Domoticz.Debug("Data from register 23: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 0.1 - value = round (value / 0.1, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acOutPower.update(value) - value = self.acOutPower.get() - Domoticz.Debug(" = {}".format(value)) - Devices[7].Update(1, str(value)) + self.acOutPower.update(round(getmodbus16(23, client)/0.1, 3)) + Devices[7].Update(1, self.acOutPower.strget()) # Ac Out Frequency - data = client.read_holding_registers(21, 1) - Domoticz.Debug("Data from register 21: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.acOutFrequency.update(value) - value = self.acOutFrequency.get() - Domoticz.Debug(" = {}".format(value)) - Devices[8].Update(1, str(value)) + self.acOutFrequency.update(round(getmodbus16(21, client)/100.0, 3)) + Devices[8].Update(1, self.acOutFrequency.strget()) # Grid lost - data = client.read_holding_registers(64, 1) - Domoticz.Debug("Data from register 64: "+str(data)) - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - value = decoder.decode_16bit_int() + value = getmodbus16(61, client) if value == 0: Devices[9].Update(nValue=value, sValue="Ok") elif value == 2: @@ -406,10 +295,7 @@ class BasePlugin: Devices[9].Update(nValue=3, sValue="Unknown state ?") # VE.Bus state - data = client.read_holding_registers(31, 1) - Domoticz.Debug("Data from register 31: "+str(data)) - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - value = decoder.decode_16bit_int() + value = getmodbus16(31, client) vebus = 'Unknown?' if value == 0: vebus = 'Off' @@ -451,68 +337,20 @@ class BasePlugin: Devices[23].Update(1, "0") # Battery Voltage - data = battery.read_holding_registers(259, 1) - Domoticz.Debug("Data from register 259: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.batteryVoltage.update(value) - value = self.batteryVoltage.get() - Domoticz.Debug(" = {}".format(value)) - Devices[20].Update(1, str(value)) + self.batteryVoltage.update(round(getmodbus16(259, battery)/100.0, 3)) + Devices[20].Update(1, self.batteryVoltage.strget()) # Battery Current - data = battery.read_holding_registers(261, 1) - Domoticz.Debug("Data from register 261: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.batteryCurrent.update(value) - value = self.batteryCurrent.get() - Domoticz.Debug(" = {}".format(value)) - Devices[21].Update(1, str(value)) + self.batteryCurrent.update(round(getmodbus16(261, battery)/10.0,3)) + Devices[21].Update(1, self.batteryCurrent.strget()) # Battery SOC - data = battery.read_holding_registers(266, 1) - Domoticz.Debug("Data from register 266: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.batterySoc.update(value) - value = self.batterySoc.get() - Domoticz.Debug(" = {}".format(value)) - Devices[22].Update(1, str(value)) + self.batterySoc.update(round(getmodbus16(266, battery)/10.0,3)) + Devices[22].Update(1, self.batterySoc.strget()) # Battery Temperature - data = battery.read_holding_registers(262, 1) - Domoticz.Debug("Data from register 262: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.batteryTemp.update(value) - value = self.batteryTemp.get() - Domoticz.Debug(" = {}".format(value)) - Devices[23].Update(1, str(value)) + self.batteryTemp.update(round(getmodbus16(262, battery)/10.0,3)) + Devices[23].Update(1, self.batteryTemp.strget()) # Victron devices Domoticz.Debug("Multiplus Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.MBAddr)) @@ -526,78 +364,26 @@ class BasePlugin: Devices[32].Update(1, "0") Devices[33].Update(1, "0") Devices[34].Update(1, "0") + Devices[35].Update(1, "0") # Grid Power L1 - data = victron.read_holding_registers(820, 1) - Domoticz.Debug("Data from register 820: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - ##value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.gridpower.update(value) - value = self.gridpower.get() - Domoticz.Debug(" = {}".format(value)) - Devices[30].Update(1, str(value)) + self.gridpower.update(getmodbus16(820, victron)) + Devices[30].Update(1, self.gridpower.strget()) # Consumption L1 - data = victron.read_holding_registers(817, 1) - Domoticz.Debug("Data from register 817: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - #value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.conso.update(value) - value = self.conso.get() - Domoticz.Debug(" = {}".format(value)) - Devices[31].Update(1, str(value)) + self.conso.update(getmodbus16(817, victron)) + Devices[31].Update(1, self.conso.strget()) # PV on Output - data = victron.read_holding_registers(808, 1) - Domoticz.Debug("Data from register 808: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 100.0 - #value = round (value / 100.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.pv.update(value) - value = self.pv.get() - Domoticz.Debug(" = {}".format(value)) - Devices[32].Update(1, str(value)) + self.pv.update(getmodbus16(808, victron)) + Devices[32].Update(1, self.pv.strget()) # Battery Power - data = victron.read_holding_registers(842, 1) - Domoticz.Debug("Data from register 262: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() - # Scale factor / 10.0 - ##value = round (value / 10.0, 3) - Domoticz.Debug("Value after conversion : "+str(value)) - Domoticz.Debug("-> Calculating average") - self.batteryPower.update(value) - value = self.batteryPower.get() - Domoticz.Debug(" = {}".format(value)) - Devices[33].Update(1, str(value)) + self.batteryPower.update(getmodbus16(842, victron)) + Devices[33].Update(1, self.batteryPower.strget()) # ESS Battery State - data = victron.read_holding_registers(2900, 1) - Domoticz.Debug("Data from register 2900: "+str(data)) - # Unsigned 16 - decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) - # Value - value = decoder.decode_16bit_int() + value = getmodbus16(2900, victron) batterystate = "Unknown?" onbattery = 0 if value == 0: @@ -627,7 +413,11 @@ class BasePlugin: batterystate = "Battery Life disabled (low SoC)" Devices[34].Update(1, str(value)+": "+batterystate) # TODO: add a device to say on battery yes/no - # use the "onbatteryy" variable + # use the "onbattery" variable + + # ESS Battery Life SoC Limit + value = (getmodbus16(2903, victron) / 10.0) + Devices[35].Update(1, str(value)) global _plugin _plugin = BasePlugin() @@ -640,26 +430,6 @@ def onStop(): global _plugin _plugin.onStop() -def onConnect(Connection, Status, Description): - global _plugin - _plugin.onConnect(Connection, Status, Description) - -def onMessage(Connection, Data): - global _plugin - _plugin.onMessage(Connection, Data) - -def onCommand(Unit, Command, Level, Hue): - global _plugin - _plugin.onCommand(Unit, Command, Level, Hue) - -def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile): - global _plugin - _plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile) - -def onDisconnect(Connection): - global _plugin - _plugin.onDisconnect(Connection) - def onHeartbeat(): global _plugin _plugin.onHeartbeat() @@ -678,3 +448,25 @@ def DumpConfigToLog(): Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'") Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel)) return + + +# get Modbus 16 bits values +def getmodbus16(register, client): + value = 0 + try: + data = client.read_holding_registers(register, 1) + Domoticz.Debug("Data from register "+str(register)+": "+str(data)) + decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) + value = decoder.decode_16bit_int() + except: + Domoticz.Error("Error getting data from "+str(register) + ", try 1") + try: + data = client.read_holding_registers(register, 1) + Domoticz.Debug("Data from register "+str(register)+": "+str(data)) + decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big) + value = decoder.decode_16bit_int() + except: + Domoticz.Error("Error getting data from "+str(register) + ", try 2") + + return value +