Compare commits

...

14 Commits

Author SHA1 Message Date
43b880b4f3 Fix for pymodbus 3.5.* 2023-09-23 16:13:15 +02:00
ecb2810c3b Fix for pymodbus 3.5
This module will works ONLY with pymodbus 3.5+
2023-09-23 16:11:50 +02:00
971398505c Added ESS Battery Life SoC Limit (GX >= 2.80 ONLY !)
Added modbus retry (2 times max) if there is an error during decode of message (this can be rare but)
Refactor the code using the modbus function
Added string converted data from Average() class
Commented the Max Class since it is not used in this module
Bumped version
2022-10-21 12:08:42 +02:00
b649318aae Removed unused function
Removed debug
Refactored modbus gettering data (with one try and catch errors)
2022-10-19 21:15:55 +02:00
61deb97761 Oooops 2022-10-06 23:01:10 +02:00
49ad271301 MOar features 2022-10-06 22:59:19 +02:00
36e39fe662 Added ESS Battery Life State 2022-09-30 13:12:04 +02:00
17c2309a28 Added VE.Bus State 2022-09-29 21:43:53 +02:00
0292bcf8d8 Added Alert device : Grid lost. 2022-09-28 22:48:42 +02:00
788a0de2c9 New additions. 2022-09-26 21:44:09 +02:00
f792f52d1f Start of Multiplus-II plugin 2022-09-26 18:03:59 +02:00
95fd9ab85d Added some shots of the GX configuration 2022-09-21 11:52:11 +02:00
730d8c6f39 Format 2022-09-21 11:47:42 +02:00
ce21385a52 README :) 2022-09-21 11:46:33 +02:00
11 changed files with 601 additions and 89 deletions

11
Modbus.md Normal file
View File

@ -0,0 +1,11 @@
# Victron GX Modbus configuration
Here is some screenshot on my Modbus configuration on my Victron Energy GX
Adapt the configuration of your plugins uppon YOUR GX system.
![](screenshots/gx-modbus1.png)
![](screenshots/gx-modbus2.png)
![](screenshots/gx-modbus3.png)
![](screenshots/gx-modbus4.png)
![](screenshots/gx-modbus5.png)

View File

@ -1,3 +1,85 @@
# victron-energy-domoticz
Domoticz plugins for Victron Energy Multiplus and serveral modules that can be reached using modbus
Domoticz plugins for Victron Energy Multiplus and serveral modules that can be reached using modbus
## Requirements
You need to have a Victron Energy GX (what ever the one you have) and have setup
IP on the system.
See [Modbus Configuration](Modbus.md) for more information
## Installation of plugin
Install `pip3`:
``` shell
sudo apt install python3-pip
```
Install `domoticz`:
``` shell
curl -sSL install.domoticz.com | sudo bash
```
Make sure that the `plugins` folder exists in the `domoticz` folder.
Install the plugin:
``` shell
cd domoticz
git clone https://github.com/xbeaudouin/victron-energy-domoticz.git
cd plugins
ln -s ../victron-energy-domoticz/mppt .
```
Go to the plugin folder and install all required addons:
``` shell
cd domoticz/plugins/mppt
sudo pip3 install -r requirements.txt
```
Once that is done, restart domoticz:
``` shell
sudo service domoticz.sh restart
```
## MPPT plugin
Once plugins installed, a new hardware will be available : 'Victron Energy MPPT over GX + Modbus"
To add the MPPT plugin add the following :
- Name : Victron_MPPT_1 (for example)
- GX IP Address : the IP address of your GX
- GX port Number : 502 should good (this is the default, but in case of this change)
- Modbus address : 229 (or depending of your setup you can have several MPPT on your system, adapt it as you need)
- If you want plenty of debug stuff (usefull to fix a bug) you can enable that.
### MPPT Screenshot
MPPT Setup
![](screenshots/mppt-setup.png)
MPPT Devices
![](screenshots/mppt-devices.png)
MPPT Voltage
![](screenshots/mppt-voltage.png)
MPPT Current
![](screenshots/mppt-current.png)
MPPT Power
![](screenshots/mppt-power.png)
MPPT kWh
![](screenshots/mppt-kWh.png)

View File

@ -7,7 +7,7 @@ Requirements:
2. pymodbus AND pymodbusTCP
"""
"""
<plugin key="VictronEnergy_GX_MPPT" name="Victron Energy MPPT over GX + Modbus" author="Xavier Beaudouin" version="0.0.1" externallink="https://github.com/xbeaudouin/victron-energy-domoticz/mppt">
<plugin key="VictronEnergy_GX_MPPT" name="Victron Energy MPPT over GX + Modbus" author="Xavier Beaudouin" version="0.0.2" externallink="https://github.com/xbeaudouin/victron-energy-domoticz/mppt">
<params>
<param field="Address" label="GX IP Address" width="150px" required="true" />
<param field="Port" label="GX Modbus Port Number" width="100px" required="true" default="502" />
@ -31,6 +31,7 @@ sys.path.append('/usr/local/lib/python3.6/dist-packages')
sys.path.append('/usr/local/lib/python3.7/dist-packages')
sys.path.append('/usr/local/lib/python3.8/dist-packages')
sys.path.append('/usr/local/lib/python3.9/dist-packages')
sys.path.append('/usr/local/lib/python3.10/dist-packages')
import pymodbus
@ -98,7 +99,6 @@ class Maximum:
# Plugin itself
class BasePlugin:
#enabled = False
def __init__(self):
# Voltage for last 5 minutes
self.voltage=Average()
@ -138,7 +138,6 @@ class BasePlugin:
Domoticz.Debug("Query IP " + self.IPAddress + ":" + str(self.IPPort) +" on device : "+str(self.MBAddr))
# Create the devices if they does not exists
# TODO: refactor this.
if 1 not in Devices:
Domoticz.Device(Name="Voltage", Unit=1, TypeName="Voltage", Used=0).Create()
if 2 not in Devices:
@ -147,7 +146,6 @@ class BasePlugin:
Options = { "Custom": "1;W" }
Domoticz.Device(Name="Power", Unit=3, TypeName="Custom", Used=0, Options=Options).Create()
if 4 not in Devices:
#Domoticz.Device(Name="Total Energy", Unit=4, Type=0xfa, Subtype=0x01, Used=0).Create()
Domoticz.Device(Name="Total Energy", Unit=4, Type=243, Subtype=29, Used=0).Create()
return
@ -156,22 +154,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):
Domoticz.Debug(" Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.MBAddr))
try:
@ -184,73 +166,30 @@ class BasePlugin:
Devices[3].Update(1, "0")
Devices[4].Update(1, "0")
# TODO: catch errors
total_e = "0"
power = "0"
# Voltage
data = client.read_holding_registers(776, 1)
Domoticz.Debug("Data from register 776: "+str(data))
# Unsigned 16
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
# Value
value = decoder.decode_16bit_int()
# Scale factor / 100
value = round (value / 100.0, 3)
Domoticz.Debug("Value after conversion : "+str(value))
Domoticz.Debug("-> Calculating average")
value = round (getmodbus16(776, client) / 100.0, 3)
self.voltage.update(value)
value = self.voltage.get()
Domoticz.Debug(" = {}".format(value))
Devices[1].Update(1, str(value))
# Current
data = client.read_holding_registers(777, 1)
Domoticz.Debug("Data from register 777: "+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")
value = round (getmodbus16(777,client) / 10.0, 3)
self.current.update(value)
value = self.current.get()
Domoticz.Debug(" = {}".format(value))
Devices[2].Update(1, str(value))
# Power
data = client.read_holding_registers(789, 1)
Domoticz.Debug("Data from register 789: "+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")
value = round (getmodbus16(789, client) / 10.0, 3)
self.power.update(value)
value = self.power.get()
Domoticz.Debug(" = {}".format(value))
Devices[3].Update(1, str(value))
power = str(value)
# Total Energy
data = client.read_holding_registers(790, 1)
Domoticz.Debug("Data from register 790: "+str(data))
# Unsigned 32
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
# Value
value = decoder.decode_16bit_int()
Domoticz.Debug(" = {}".format(value))
# Scale * 100 (to have Wh)
total_e = str(value*100)
# Do insert data on counters
#Devices[4].Update(1, sValue=total_e+";0;0;0;"+power+";0")
total_e = str(getmodbus16(790,client)*100)
Devices[4].Update(1, sValue=power+";"+total_e)
@ -265,31 +204,11 @@ 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()
# Generic helper functions
# Generic helper functions
def DumpConfigToLog():
for x in Parameters:
if Parameters[x] != "":
@ -303,3 +222,26 @@ def DumpConfigToLog():
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
return
# get Modbug 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)
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)
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

1
multiplus/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

474
multiplus/plugin.py Normal file
View File

@ -0,0 +1,474 @@
#!/usr/bin/env python
"""
Victron Energy Multiplus II plugin
Author: Xavier Beaudouin
Requirements:
1. multiplus + GX
2. pymodbus AND pymodbusTCP
"""
"""
<plugin key="VictronEnergy_MultiplusII" name="Victron Energy Multiplus II + Modbus" author="Xavier Beaudouin" version="0.0.2" externallink="https://github.com/xbeaudouin/victron-energy-domoticz/mppt">
<params>
<param field="Address" label="GX IP Address" width="150px" required="true" />
<param field="Port" label="GX Modbus Port Number" width="100px" required="true" default="502" />
<param field="Mode3" label="GX Modbus address" width="100px" required="true" default="100" />
<param field="Mode4" label="Multiplus Modbus address" width="100px" required="true" default="228" />
<param field="Mode5" label="Battery Modbus address" width="100px" required="true" default="225" />
<param field="Mode6" label="Debug" width="100px">
<options>
<option label="True" value="Debug"/>
<option label="False" value="Normal" default="true" />
</options>
</param>
</params>
</plugin>
"""
import Domoticz
import sys
sys.path.append('/usr/local/lib/python3.4/dist-packages')
sys.path.append('/usr/local/lib/python3.5/dist-packages')
sys.path.append('/usr/local/lib/python3.6/dist-packages')
sys.path.append('/usr/local/lib/python3.7/dist-packages')
sys.path.append('/usr/local/lib/python3.8/dist-packages')
sys.path.append('/usr/local/lib/python3.9/dist-packages')
sys.path.append('/usr/local/lib/python3.10/dist-packages')
import pymodbus
from pyModbusTCP.client import ModbusClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
#
# 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.
#
# The Average class can be used to calculate the average value based on a sliding window of samples.
# The number of samples stored depends on the interval used to collect the value from the inverter itself.
#
class Average:
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("Average: {} - {} values".format(self.get(), len(self.samples)))
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.
#
# The Maximum class can be used to calculate the highest value based on a sliding window of samples.
# 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)
# Plugin itself
class BasePlugin:
def __init__(self):
# AC IN Voltage for last 5 minutes
self.acInVoltage=Average()
# AC IN Current for last 5 minutes
self.acInCurrent=Average()
# AC IN Power for last 5 minutes
self.acInPower=Average()
# AC IN Frequency for last 5 minutes
self.acInFrequency=Average()
# AC Out Voltage for last 5 minutes
self.acOutVoltage=Average()
# AC Out Current for last 5 minutes
self.acOutCurrent=Average()
# AC Out Power for last 5 minutes
self.acOutPower=Average()
# AC Out Frequency for last 5 minutes
self.acOutFrequency=Average()
# Battery Voltage for last 5 minutes
self.batteryVoltage=Average()
# Battery Current for last 5 minutes
self.batteryCurrent=Average()
# Battery SOC for last 5 minutes
self.batterySoc=Average()
# Battery Temperature for last 5 minutes
self.batteryTemp=Average()
# Grid Power for last 5 minutes
self.gridpower=Average()
# Consumption for last 5 minutes
self.conso=Average()
# PV on output for last 5 minutes
self.pv=Average()
# Battery power on last 5 minutes
self.batteryPower=Average()
return
def onStart(self):
try:
Domoticz.Log("Victron Energy Multiplus-II Modbus loaded!, using python v" + sys.version[:6] + " and pymodbus v" + pymodbus.__version__)
except:
Domoticz.Log("Victron Energy Multiplus-II Modbus loaded!")
# Check dependancies
try:
if (float(Parameters["DomoticzVersion"][:6]) < float("2020.2")): Domoticz.Error("WARNING: Domoticz version is outdated or not supported. Please update!")
if (float(sys.version[:1]) < 3): Domoticz.Error("WARNING: Python3 should be used !")
if (float(pymodbus.__version__[:3]) < float("2.3")): Domoticz.Error("WARNING: pymodbus version is outdated, please update!")
except:
Domoticz.Error("Warning ! Dependancies could not be checked !")
# Parse parameters
# Debug
if Parameters["Mode6"] == "Debug":
Domoticz.Debugging(1)
else:
Domoticz.Debugging(0)
self.IPAddress = Parameters["Address"]
self.IPPort = Parameters["Port"]
self.MBAddr = int(Parameters["Mode3"])
self.MultiAddr = int(Parameters["Mode4"])
self.BattAddr = int(Parameters["Mode5"])
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
# Multiplus Devices
if 1 not in Devices:
Domoticz.Device(Name="Voltage IN L1", Unit=1, TypeName="Voltage", Used=0).Create()
if 2 not in Devices:
Domoticz.Device(Name="Current IN L1", Unit=2, TypeName="Current (Single)", Used=0).Create()
if 3 not in Devices:
Options = { "Custom": "1;W" }
Domoticz.Device(Name="Power IN L1", Unit=3, TypeName="Custom", Used=0, Options=Options).Create()
if 4 not in Devices:
Options = { "Custom": "1;Hz" }
Domoticz.Device(Name="Frequency IN L1", Unit=4, TypeName="Custom", Used=0, Options=Options).Create()
if 5 not in Devices:
Domoticz.Device(Name="Voltage OUT L1", Unit=5, TypeName="Voltage", Used=0).Create()
if 6 not in Devices:
Domoticz.Device(Name="Current OUT L1", Unit=6, TypeName="Current (Single)", Used=0).Create()
if 7 not in Devices:
Options = { "Custom": "1;W" }
Domoticz.Device(Name="Power OUT L1", Unit=7, TypeName="Custom", Used=0, Options=Options).Create()
if 8 not in Devices:
Options = { "Custom": "1;Hz" }
Domoticz.Device(Name="Frequency OUT L1", Unit=8, TypeName="Custom", Used=0, Options=Options).Create()
if 9 not in Devices:
Domoticz.Device(Name="Grid Lost", Unit=9, TypeName="Alert", Used=0).Create()
if 10 not in Devices:
Domoticz.Device(Name="VE.Bus State", Unit=10, TypeName="Text", Used=0).Create()
# Battery
if 20 not in Devices:
Domoticz.Device(Name="Battery Voltage", Unit=20, TypeName="Voltage", Used=0).Create()
if 21 not in Devices:
Domoticz.Device(Name="Battery Current", Unit=21, TypeName="Current (Single)", Used=0).Create()
if 22 not in Devices:
Domoticz.Device(Name="Battery SOC", Unit=22, TypeName="Percentage", Used=0).Create()
if 23 not in Devices:
Domoticz.Device(Name="Battery Temperature", Unit=23, TypeName="Temperature", Used=0).Create()
# 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()
if 31 not in Devices:
Options = { "Custom": "1;W" }
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()
if 33 not in Devices:
Options = { "Custom": "1;W" }
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()
if 35 not in Devices:
Domoticz.Device(Name="ESS Battery Life SoC Limit", Unit=35, TypeName="Percentage", Used=0).Create()
return
def onStop(self):
Domoticz.Debugging(0)
def onHeartbeat(self):
# Multiplus devices
Domoticz.Debug("Multiplus Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.MultiAddr))
try:
client = ModbusClient(host=self.IPAddress, port=self.IPPort, unit_id=self.MultiAddr, auto_open=True, auto_close=True, timeout=2)
except:
Domoticz.Error("Error connecting to TCP/Interface on address : "+self.IPaddress+":"+str(self.IPPort))
# Set value to 0 -> Error on all devices
Devices[1].Update(1, "0")
Devices[2].Update(1, "0")
Devices[3].Update(1, "0")
Devices[4].Update(1, "0")
Devices[5].Update(1, "0")
Devices[6].Update(1, "0")
Devices[7].Update(1, "0")
Devices[8].Update(1, "0")
Devices[9].Update(1, "0")
Devices[10].Update(1, "0")
# Ac In Voltage
self.acInVoltage.update(round(getmodbus16(3, client)/10.0, 3))
Devices[1].Update(1, self.acInVoltage.strget())
# Ac In Current
self.acInCurrent.update(round(getmodbus16(6, client)/10.0, 3))
Devices[2].Update(1, self.acInCurrent.strget())
# Ac In Power
self.acInPower.update(round(getmodbus16(12, client)/0.1, 3))
Devices[3].Update(1, self.acInPower.strget())
# Ac In Frequency
self.acInFrequency.update(round(getmodbus16(9, client)/100.0, 3))
Devices[4].Update(1, self.acInFrequency.strget())
# Ac Out Voltage
self.acOutVoltage.update(round(getmodbus16(15, client)/10.0, 3))
Devices[5].Update(1, self.acOutVoltage.strget())
# Ac Out Current
self.acOutCurrent.update(round(getmodbus16(18, client)/10.0, 3))
Devices[6].Update(1, self.acOutCurrent.strget())
# Ac Out Power
self.acOutPower.update(round(getmodbus16(23, client)/0.1, 3))
Devices[7].Update(1, self.acOutPower.strget())
# Ac Out Frequency
self.acOutFrequency.update(round(getmodbus16(21, client)/100.0, 3))
Devices[8].Update(1, self.acOutFrequency.strget())
# Grid lost
value = getmodbus16(61, client)
if value == 0:
Devices[9].Update(nValue=value, sValue="Ok")
elif value == 2:
Devices[9].Update(nValue=value, sValue="Alert - Grid Lost")
else:
Devices[9].Update(nValue=3, sValue="Unknown state ?")
# VE.Bus state
value = getmodbus16(31, client)
vebus = 'Unknown?'
if value == 0:
vebus = 'Off'
elif value == 1:
vebus = 'Low Power'
elif value == 2:
vebus = 'Fault'
elif value == 3:
vebus = 'Bulk'
elif value == 4:
vebus = 'Absorption'
elif value == 5:
vebus = 'Float'
elif value == 6:
vebus = 'Storage'
elif value == 7:
vebus = 'Equalize'
elif value == 8:
vebus = 'Passthru'
elif value == 9:
vebus = 'Inverting'
elif value == 10:
vebus = 'Power assist'
elif value == 11:
vebus = 'Power supply'
Devices[10].Update(1, str(value)+": "+vebus)
# Multiplus devices
Domoticz.Debug("Multiplus Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.BattAddr))
try:
battery = ModbusClient(host=self.IPAddress, port=self.IPPort, unit_id=self.BattAddr, auto_open=True, auto_close=True, timeout=2)
except:
Domoticz.Error("Error connecting to TCP/Interface on address : "+self.IPaddress+":"+str(self.IPPort))
# Set value to 0 -> Error on all devices
Devices[20].Update(1, "0")
Devices[21].Update(1, "0")
Devices[22].Update(1, "0")
Devices[23].Update(1, "0")
# Battery Voltage
self.batteryVoltage.update(round(getmodbus16(259, battery)/100.0, 3))
Devices[20].Update(1, self.batteryVoltage.strget())
# Battery Current
self.batteryCurrent.update(round(getmodbus16(261, battery)/10.0,3))
Devices[21].Update(1, self.batteryCurrent.strget())
# Battery SOC
self.batterySoc.update(round(getmodbus16(266, battery)/10.0,3))
Devices[22].Update(1, self.batterySoc.strget())
# Battery Temperature
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))
try:
victron = ModbusClient(host=self.IPAddress, port=self.IPPort, unit_id=self.MBAddr, auto_open=True, auto_close=True, timeout=2)
except:
Domoticz.Error("Error connecting to TCP/Interface on address : "+self.IPaddress+":"+str(self.IPPort))
# Set value to 0 -> Error on all devices
Devices[30].Update(1, "0")
Devices[31].Update(1, "0")
Devices[32].Update(1, "0")
Devices[33].Update(1, "0")
Devices[34].Update(1, "0")
Devices[35].Update(1, "0")
# Grid Power L1
self.gridpower.update(getmodbus16(820, victron))
Devices[30].Update(1, self.gridpower.strget())
# Consumption L1
self.conso.update(getmodbus16(817, victron))
Devices[31].Update(1, self.conso.strget())
# PV on Output
self.pv.update(getmodbus16(808, victron))
Devices[32].Update(1, self.pv.strget())
# Battery Power
self.batteryPower.update(getmodbus16(842, victron))
Devices[33].Update(1, self.batteryPower.strget())
# ESS Battery State
value = getmodbus16(2900, victron)
batterystate = "Unknown?"
onbattery = 0
if value == 0:
batterystate = "Unused, Battery Life Disabled"
elif value == 1:
batterystate = "Restarted"
elif value == 2:
batterystate = "Self-compsumption"
onbattery = 1
elif value == 3:
batterystate = "Self-compsumption, SoC exceeds 85%"
onbattery = 1
elif value == 4:
batterystate = "Self-compsumption, SoC at 100%"
onbattery = 1
elif value == 5:
batterystate = "Discharge disabled"
elif value == 6:
batterystate = "Force Charge"
elif value == 7:
batterystate = "Sustain"
elif value == 9:
batterystate = "Keep batteries charged"
elif value == 10:
batterystate = "Battery Life disabled"
elif value == 11:
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 "onbattery" variable
# ESS Battery Life SoC Limit
value = (getmodbus16(2903, victron) / 10.0)
Devices[35].Update(1, str(value))
global _plugin
_plugin = BasePlugin()
def onStart():
global _plugin
_plugin.onStart()
def onStop():
global _plugin
_plugin.onStop()
def onHeartbeat():
global _plugin
_plugin.onHeartbeat()
# Generic helper functions
def DumpConfigToLog():
for x in Parameters:
if Parameters[x] != "":
Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
Domoticz.Debug("Device count: " + str(len(Devices)))
for x in Devices:
Domoticz.Debug("Device: " + str(x) + " - " + str(Devices[x]))
Domoticz.Debug("Device ID: '" + str(Devices[x].ID) + "'")
Domoticz.Debug("Device Name: '" + Devices[x].Name + "'")
Domoticz.Debug("Device nValue: " + str(Devices[x].nValue))
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)
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)
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

View File

@ -0,0 +1,2 @@
pymodbus
pymodbusTCP

BIN
screenshots/gx-modbus1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
screenshots/gx-modbus2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
screenshots/gx-modbus3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
screenshots/gx-modbus4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
screenshots/gx-modbus5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB