Compare commits
11 Commits
10c1ee029b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 68812528c1 | |||
| db4287ef71 | |||
| cefb281872 | |||
| b6a4f22721 | |||
| 7a4961060b | |||
| 834bb8474b | |||
| 675322145a | |||
| dbf068b662 | |||
| 304b388253 | |||
| 2eb8eda139 | |||
| 74b2e1e964 |
130
.gitignore
vendored
Normal file
130
.gitignore
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
screenshots/Thumbs.db
|
||||||
16
README.md
16
README.md
@ -79,21 +79,7 @@ sudo service domoticz.sh restart
|
|||||||
```
|
```
|
||||||
## ScreenShots
|
## ScreenShots
|
||||||
|
|
||||||
Hardware page showing a configured PW21 to get data from a SDM120c
|
TODO
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The devices page show all Domoticz devices that were created for it.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The voltage graphs.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Total power meter with Return energy (usefull for PV/Wind turbine generators)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Documentation about the registers used.
|
## Documentation about the registers used.
|
||||||
|
|
||||||
|
|||||||
402
plugin.py
402
plugin.py
@ -7,11 +7,17 @@ Requirements:
|
|||||||
2. pymodbus AND pymodbusTCP
|
2. pymodbus AND pymodbusTCP
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
<plugin key="DS238_ModbusTCP" name="SDM120c ModbusTCP" author="Xavier Beaudouin" version="0.0.1" externallink="https://github.com/xbeaudouin/domoticz-sdm120c-modbus-tcp">
|
<plugin key="SDM120c_ModbusTCP" name="SDM120c ModbusTCP" author="Xavier Beaudouin" version="0.0.1" externallink="https://github.com/xbeaudouin/domoticz-sdm120c-modbus-tcp">
|
||||||
<params>
|
<params>
|
||||||
<param field="Address" label="IP Address" width="150px" required="true" />
|
<param field="Address" label="IP Address" width="150px" required="true" />
|
||||||
<param field="Port" label="Port Number" width="100px" required="true" default="502" />
|
<param field="Port" label="Port Number" width="100px" required="true" default="502" />
|
||||||
<param field="Mode3" label="Modbus address" width="100px" required="true" default="1" />
|
<param field="Mode3" label="Modbus address" width="100px" required="true" default="1" />
|
||||||
|
<param field="Mode5" label="Collect extended data" width="100px">
|
||||||
|
<options>
|
||||||
|
<option label="Enabled" value="Moredata"/>
|
||||||
|
<option label="Disabled" value="Regular" default="true" />
|
||||||
|
</options>
|
||||||
|
</param>
|
||||||
<param field="Mode6" label="Debug" width="100px">
|
<param field="Mode6" label="Debug" width="100px">
|
||||||
<options>
|
<options>
|
||||||
<option label="True" value="Debug"/>
|
<option label="True" value="Debug"/>
|
||||||
@ -31,6 +37,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.7/dist-packages')
|
||||||
sys.path.append('/usr/local/lib/python3.8/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.9/dist-packages')
|
||||||
|
sys.path.append('/usr/local/lib/python3.10/dist-packages')
|
||||||
|
|
||||||
import pymodbus
|
import pymodbus
|
||||||
|
|
||||||
@ -67,70 +74,51 @@ class Average:
|
|||||||
def get(self):
|
def get(self):
|
||||||
return sum(self.samples) / len(self.samples)
|
return sum(self.samples) / len(self.samples)
|
||||||
|
|
||||||
#
|
def strget(self):
|
||||||
# Domoticz shows graphs with intervals of 5 minutes.
|
return str(sum(self.samples) / len(self.samples))
|
||||||
# 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
|
# Plugin itself
|
||||||
class BasePlugin:
|
class BasePlugin:
|
||||||
#enabled = False
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Voltage for last 5 minutes
|
# Voltage for last 5 minutes
|
||||||
self.voltage=Average()
|
self.voltage=Average()
|
||||||
# Current for last 5 minutes
|
# Current for last 5 minutes
|
||||||
self.current=Average()
|
self.current=Average()
|
||||||
|
# Apparent Power for last 5 minutes
|
||||||
|
self.apparent_power=Average()
|
||||||
# Active power for last 5 minutes
|
# Active power for last 5 minutes
|
||||||
self.active_power=Average()
|
self.active_power=Average()
|
||||||
# Reactive power for last 5 minutes
|
# Reactive power for last 5 minutes
|
||||||
self.reactive_power=Average()
|
self.reactive_power=Average()
|
||||||
# Power factor for last 5 minutes
|
# Power factor for last 5 minutes
|
||||||
self.power_factor=Average()
|
self.power_factor=Average()
|
||||||
|
# Phase Angle for last 5 minutes
|
||||||
|
self.phase_angle=Average()
|
||||||
# Frequency for last 5 minutes
|
# Frequency for last 5 minutes
|
||||||
self.frequency=Average()
|
self.frequency=Average()
|
||||||
|
|
||||||
|
# Used only when Moredata is set...
|
||||||
|
# Total demand power for last 5 minutes
|
||||||
|
self.total_demand_power=Average()
|
||||||
|
# Import demand power for last 5 minutes
|
||||||
|
self.import_demand_power=Average()
|
||||||
|
# Export demand power for last 5 minutes
|
||||||
|
self.export_demand_power=Average()
|
||||||
|
# Total Demand Current for last 5 minutes
|
||||||
|
self.total_demand_current=Average()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def onStart(self):
|
def onStart(self):
|
||||||
try:
|
Domoticz.Log("SDM120c Energy Meter TCP loaded!")
|
||||||
Domoticz.Log("DS238-2 ZN/S Energy Meter TCP loaded!, using python v" + sys.version[:6] + " and pymodbus v" + pymodbus.__version__)
|
|
||||||
except:
|
|
||||||
Domoticz.Log("DS238-2 ZN/S Energy Meter TCP loaded!")
|
|
||||||
|
|
||||||
# Check dependancies
|
# Check dependancies
|
||||||
try:
|
try:
|
||||||
if (float(Parameters["DomoticzVersion"][:6]) < float("2020.2")): Domoticz.Error("WARNING: Domoticz version is outdated or not supported. Please update!")
|
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(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:
|
except:
|
||||||
Domoticz.Error("Warning ! Dependancies could not be checked !")
|
Domoticz.Error("Warning ! Dependancies could not be checked !")
|
||||||
|
|
||||||
# Parse parameters
|
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
if Parameters["Mode6"] == "Debug":
|
if Parameters["Mode6"] == "Debug":
|
||||||
Domoticz.Debugging(1)
|
Domoticz.Debugging(1)
|
||||||
@ -144,31 +132,63 @@ class BasePlugin:
|
|||||||
Domoticz.Debug("Query IP " + self.IPAddress + ":" + str(self.IPPort) +" on device : "+str(self.MBAddr))
|
Domoticz.Debug("Query IP " + self.IPAddress + ":" + str(self.IPPort) +" on device : "+str(self.MBAddr))
|
||||||
|
|
||||||
# Create the devices if they does not exists
|
# Create the devices if they does not exists
|
||||||
# TODO: refactor this.
|
|
||||||
if 1 not in Devices:
|
if 1 not in Devices:
|
||||||
Domoticz.Device(Name="Total Energy", Unit=1, Type=0xfa, Subtype=0x01, Used=0).Create()
|
Domoticz.Device(Name="Voltage", Unit=1, TypeName="Voltage", Used=0).Create()
|
||||||
if 2 not in Devices:
|
if 2 not in Devices:
|
||||||
Domoticz.Device(Name="Export Energy", Unit=2, Type=0xfa, Subtype=0x01, Used=0).Create()
|
Domoticz.Device(Name="Current", Unit=2, TypeName="Current (Single)", Used=0).Create()
|
||||||
if 3 not in Devices:
|
if 3 not in Devices:
|
||||||
Domoticz.Device(Name="Import Energy", Unit=3, Type=0xfa, Subtype=0x01, Used=0).Create()
|
|
||||||
if 4 not in Devices:
|
|
||||||
Domoticz.Device(Name="Voltage", Unit=4, TypeName="Voltage", Used=0).Create()
|
|
||||||
if 5 not in Devices:
|
|
||||||
Domoticz.Device(Name="Current", Unit=5, TypeName="Current (Single)", Used=0).Create()
|
|
||||||
if 6 not in Devices:
|
|
||||||
Options = { "Custom": "1;W" }
|
Options = { "Custom": "1;W" }
|
||||||
Domoticz.Device(Name="Active Power", Unit=6, TypeName="Custom", Used=0, Options=Options).Create()
|
Domoticz.Device(Name="Active Power", Unit=3, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
if 7 not in Devices:
|
if 4 not in Devices:
|
||||||
|
Options = { "Custom": "1;VA" }
|
||||||
|
Domoticz.Device(Name="Apparent Power", Unit=4, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 5 not in Devices:
|
||||||
Options = { "Custom": "1;VAr" }
|
Options = { "Custom": "1;VAr" }
|
||||||
Domoticz.Device(Name="Reactive Power", Unit=7, TypeName="Custom", Used=0, Options=Options).Create()
|
Domoticz.Device(Name="Reactive Power", Unit=5, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
if 8 not in Devices:
|
if 6 not in Devices:
|
||||||
Options = { "Custom": "1;PF" }
|
Options = { "Custom": "1;PF" }
|
||||||
Domoticz.Device(Name="Power Factor", Unit=8, TypeName="Custom", Used=0, Options=Options).Create()
|
Domoticz.Device(Name="Power Factor", Unit=6, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
if 9 not in Devices:
|
if Parameters["Mode5"] == "Moredata":
|
||||||
|
if 7 not in Devices:
|
||||||
|
Options = { "Custom": "1;Deg" }
|
||||||
|
Domoticz.Device(Name="Phase Angle", Unit=7, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
|
||||||
|
if 8 not in Devices:
|
||||||
Options = { "Custom": "1;Hz" }
|
Options = { "Custom": "1;Hz" }
|
||||||
Domoticz.Device(Name="Frequency", Unit=9, TypeName="Custom", Used=0, Options=Options).Create()
|
Domoticz.Device(Name="Frequency", Unit=8, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 9 not in Devices:
|
||||||
|
Domoticz.Device(Name="Import Energy", Unit=9, Type=243, Subtype=29, Used=0).Create()
|
||||||
if 10 not in Devices:
|
if 10 not in Devices:
|
||||||
Domoticz.Device(Name="Total Power Meter",Unit=10,Type=0xfa, Subtype=0x01, Used=0).Create()
|
Domoticz.Device(Name="Export Energy", Unit=10, Type=243, Subtype=29, Used=0).Create()
|
||||||
|
# 11 will be not used Import Energy (Reactive) / kVArh
|
||||||
|
# 12 will be not used Export Energy (Reactive) / kVArh
|
||||||
|
if Parameters["Mode5"] == "Moredata":
|
||||||
|
if 13 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Total Demand Power", Unit=13, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 14 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Maximum Total Demand Power", Unit=14, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 15 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Import Demand Power", Unit=15, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 16 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Maximum Import Demand Power", Unit=16, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 17 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Export Demand Power", Unit=17, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 18 not in Devices:
|
||||||
|
Options = { "Custom": "1;W" }
|
||||||
|
Domoticz.Device(Name="Maximum Export Demand Power", Unit=18, TypeName="Custom", Used=0, Options=Options).Create()
|
||||||
|
if 19 not in Devices:
|
||||||
|
Domoticz.Device(Name="Total Demand Current", Unit=19, TypeName="Current (Single)", Used=0).Create()
|
||||||
|
if 20 not in Devices:
|
||||||
|
Domoticz.Device(Name="Maximum Total Demand Current", Unit=20, TypeName="Current (Single)", Used=0).Create()
|
||||||
|
|
||||||
|
if 21 not in Devices:
|
||||||
|
Domoticz.Device(Name="Total Energy", Unit=21, Type=0xfa, Subtype=0x01, Used=0).Create()
|
||||||
|
# 22 will not be used Total Energy (Reactive)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -176,22 +196,6 @@ class BasePlugin:
|
|||||||
def onStop(self):
|
def onStop(self):
|
||||||
Domoticz.Debugging(0)
|
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):
|
def onHeartbeat(self):
|
||||||
Domoticz.Debug(" Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.MBAddr))
|
Domoticz.Debug(" Interface : IP="+self.IPAddress +", Port="+str(self.IPPort)+" ID="+str(self.MBAddr))
|
||||||
try:
|
try:
|
||||||
@ -205,150 +209,109 @@ class BasePlugin:
|
|||||||
Devices[4].Update(1, "0")
|
Devices[4].Update(1, "0")
|
||||||
Devices[5].Update(1, "0")
|
Devices[5].Update(1, "0")
|
||||||
Devices[6].Update(1, "0")
|
Devices[6].Update(1, "0")
|
||||||
|
if Parameters["Mode5"] == "Moredata":
|
||||||
Devices[7].Update(1, "0")
|
Devices[7].Update(1, "0")
|
||||||
|
|
||||||
Devices[8].Update(1, "0")
|
Devices[8].Update(1, "0")
|
||||||
Devices[9].Update(1, "0")
|
Devices[9].Update(1, "0")
|
||||||
Devices[10].Update(1, "0")
|
Devices[10].Update(1, "0")
|
||||||
|
if Parameters["Mode5"] == "Moredata":
|
||||||
|
Devices[13].Update(1, "0")
|
||||||
|
Devices[14].Update(1, "0")
|
||||||
|
Devices[15].Update(1, "0")
|
||||||
|
Devices[16].Update(1, "0")
|
||||||
|
Devices[17].Update(1, "0")
|
||||||
|
Devices[18].Update(1, "0")
|
||||||
|
Devices[19].Update(1, "0")
|
||||||
|
Devices[20].Update(1, "0")
|
||||||
|
|
||||||
# TODO: catch errors
|
Devices[21].Update(1, "0")
|
||||||
# 3 counters
|
|
||||||
total_e = "0"
|
|
||||||
export_e = "0"
|
|
||||||
import_e = "0"
|
|
||||||
export_w = 0
|
|
||||||
import_w = 0
|
|
||||||
power = "0"
|
|
||||||
|
|
||||||
# Total Energy
|
|
||||||
data = client.read_holding_registers(0, 2)
|
|
||||||
Domoticz.Debug("Data from register 0: "+str(data))
|
|
||||||
# Unsigned 32
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
|
||||||
# Value
|
|
||||||
value = decoder.decode_32bit_int()
|
|
||||||
total_e = str(value)
|
|
||||||
|
|
||||||
# Export Energy
|
|
||||||
data = client.read_holding_registers(0x8, 2)
|
|
||||||
Domoticz.Debug("Data from register 0x8: "+str(data))
|
|
||||||
# Unsigned 32
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
|
||||||
# Value
|
|
||||||
value = decoder.decode_32bit_int()
|
|
||||||
export_e = str(value)
|
|
||||||
|
|
||||||
# Import Energy
|
|
||||||
data = client.read_holding_registers(0xA, 2)
|
|
||||||
Domoticz.Debug("Data from register 0xA: "+str(data))
|
|
||||||
# Unsigned 32
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
|
||||||
# Value
|
|
||||||
value = decoder.decode_32bit_int()
|
|
||||||
import_e = str(value)
|
|
||||||
|
|
||||||
# Voltage
|
# Voltage
|
||||||
data = client.read_holding_registers(0xC, 1)
|
self.voltage.update(getmodbus(0x0000, client))
|
||||||
Domoticz.Debug("Data from register 0xC: "+str(data))
|
Devices[1].Update(1, self.voltage.strget())
|
||||||
# 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, 3)
|
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.voltage.update(value)
|
|
||||||
value = self.voltage.get()
|
|
||||||
Domoticz.Debug(" = {}".format(value))
|
|
||||||
Devices[4].Update(1, str(value))
|
|
||||||
|
|
||||||
# Current
|
# Current
|
||||||
data = client.read_holding_registers(0xD, 1)
|
self.current.update(getmodbus(0x0006, client))
|
||||||
Domoticz.Debug("Data from register 0xD: "+str(data))
|
Devices[2].Update(1, self.current.strget())
|
||||||
# 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, 3)
|
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.current.update(value)
|
|
||||||
value = self.current.get()
|
|
||||||
Domoticz.Debug(" = {}".format(value))
|
|
||||||
Devices[5].Update(1, str(value))
|
|
||||||
|
|
||||||
# Active Power
|
# Power (Active)
|
||||||
data = client.read_holding_registers(0xE, 1)
|
self.active_power.update(getmodbus(0x000c, client))
|
||||||
Domoticz.Debug("Data from register 0xE: "+str(data))
|
Devices[3].Update(1, self.active_power.strget())
|
||||||
# Unsigned 16
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
|
||||||
# Value
|
|
||||||
value = decoder.decode_16bit_int()
|
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.active_power.update(value)
|
|
||||||
value = self.active_power.get()
|
|
||||||
Domoticz.Debug(" = {}".format(value))
|
|
||||||
Devices[6].Update(1, str(value))
|
|
||||||
if value > 0.0:
|
|
||||||
import_w = value
|
|
||||||
if value < 0.0:
|
|
||||||
export_w = value
|
|
||||||
power = str(abs(value))
|
|
||||||
|
|
||||||
# Reactive Power
|
# Power (Apparent)
|
||||||
data = client.read_holding_registers(0xF, 1)
|
self.apparent_power.update(getmodbus(0x0012, client))
|
||||||
Domoticz.Debug("Data from register 0xF: "+str(data))
|
Devices[4].Update(1, self.apparent_power.strget())
|
||||||
# Unsigned 16
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
# Power (Reactive)
|
||||||
# Value
|
self.reactive_power.update(getmodbus(0x0018, client))
|
||||||
value = decoder.decode_16bit_int()
|
Devices[5].Update(1, self.reactive_power.strget())
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.reactive_power.update(value)
|
|
||||||
value = self.reactive_power.get()
|
|
||||||
Domoticz.Debug(" = {}".format(value))
|
|
||||||
Devices[7].Update(1, str(value))
|
|
||||||
|
|
||||||
# Power Factor
|
# Power Factor
|
||||||
data = client.read_holding_registers(0x10, 1)
|
self.power_factor.update(getmodbus(0x001e, client))
|
||||||
Domoticz.Debug("Data from register 0x10: "+str(data))
|
Devices[6].Update(1, self.power_factor.strget())
|
||||||
# Unsigned 16
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
if Parameters["Mode5"] == "Moredata":
|
||||||
# Value
|
# Phase Angle
|
||||||
value = decoder.decode_16bit_int()
|
self.phase_angle.update(getmodbus(0x0024, client))
|
||||||
# Scale factor / 1000
|
Devices[7].Update(1, self.phase_angle.strget())
|
||||||
value = round (value / 1000, 3)
|
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.power_factor.update(value)
|
|
||||||
value = self.power_factor.get()
|
|
||||||
Domoticz.Debug(" = {}".format(value))
|
|
||||||
Devices[8].Update(1, str(value))
|
|
||||||
|
|
||||||
# Frequency
|
# Frequency
|
||||||
data = client.read_holding_registers(0x11, 1)
|
self.frequency.update(getmodbus(0x0046, client))
|
||||||
Domoticz.Debug("Data from register 0x11: "+str(data))
|
Devices[8].Update(1, self.frequency.strget())
|
||||||
# Unsigned 16
|
|
||||||
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.Big, wordorder=Endian.Big)
|
# For Imported/Exported Energy (Active) and the last P1 Counter
|
||||||
# Value
|
power = self.active_power.get()
|
||||||
value = decoder.decode_16bit_int()
|
if power >= 0:
|
||||||
# Scale factor / 100
|
import_power = power
|
||||||
value = round (value / 100, 3)
|
else:
|
||||||
Domoticz.Debug("Value after conversion : "+str(value))
|
import_power = 0
|
||||||
Domoticz.Debug("-> Calculating average")
|
|
||||||
self.frequency.update(value)
|
if power < 0:
|
||||||
value = self.frequency.get()
|
export_power = abs(power)
|
||||||
Domoticz.Debug(" = {}".format(value))
|
else:
|
||||||
Devices[9].Update(1, str(value))
|
export_power = 0
|
||||||
|
|
||||||
|
# Imported Energy (Active)
|
||||||
|
import_e = str(getmodbus(0x0048, client)*1000)
|
||||||
|
Devices[9].Update(1, sValue=str(import_power)+";"+import_e)
|
||||||
|
|
||||||
|
# Exported Energy (Active)
|
||||||
|
export_e = str(getmodbus(0x004a, client)*1000)
|
||||||
|
Devices[10].Update(1, sValue=str(export_power)+";"+export_e)
|
||||||
|
|
||||||
|
if Parameters["Mode5"] == "Moredata":
|
||||||
|
# Total Demand Powier (Active)
|
||||||
|
self.total_demand_power.update(getmodbus(0x0054, client))
|
||||||
|
Devices[13].Update(1, self.total_demand_power.strget())
|
||||||
|
|
||||||
|
# Maximum Total Demand Power (Active)
|
||||||
|
Devices[14].Update(1, str(getmodbus(0x0056, client)))
|
||||||
|
|
||||||
|
# Import Demand Power (Active)
|
||||||
|
self.import_demand_power.update(getmodbus(0x0058, client))
|
||||||
|
Devices[15].Update(1, self.import_demand_power.strget())
|
||||||
|
|
||||||
|
# Maximum Import Demand Power (Active)
|
||||||
|
Devices[16].Update(1, str(getmodbus(0x005a, client)))
|
||||||
|
|
||||||
|
# Export Demand Power (Active)
|
||||||
|
self.import_demand_power.update(getmodbus(0x0058, client))
|
||||||
|
Devices[17].Update(1, self.import_demand_power.strget())
|
||||||
|
|
||||||
|
# Maxium Export Demand Power (Active)
|
||||||
|
Devices[18].Update(1, str(getmodbus(0x005e, client)))
|
||||||
|
|
||||||
|
# Total Demand Current
|
||||||
|
self.total_demand_current.update(getmodbus(0x0102, client))
|
||||||
|
Devices[19].Update(1, self.total_demand_current.strget())
|
||||||
|
|
||||||
|
# Maxium Total Demand Current
|
||||||
|
Devices[20].Update(1, str(getmodbus(0x0108, client)))
|
||||||
|
|
||||||
|
|
||||||
# Do insert data on counters
|
# P1 Counter with import and export values
|
||||||
Devices[1].Update(1, sValue=total_e+"0;0;0;0;"+power+";0")
|
Devices[21].Update(1, sValue=import_e+";0;"+export_e+";0;"+str(import_power)+";"+str(export_power))
|
||||||
Devices[2].Update(1, sValue=export_e+"0;0;0;0;"+str(abs(export_w))+";0")
|
|
||||||
Devices[3].Update(1, sValue=import_e+"0;0;0;0;"+str(abs(import_w))+";0")
|
|
||||||
Devices[10].Update(1, sValue=import_e+"0;0;"+export_e+"0;0;"+str(abs(import_w))+";"+str(abs(export_w)))
|
|
||||||
|
|
||||||
|
|
||||||
global _plugin
|
global _plugin
|
||||||
@ -362,26 +325,6 @@ def onStop():
|
|||||||
global _plugin
|
global _plugin
|
||||||
_plugin.onStop()
|
_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():
|
def onHeartbeat():
|
||||||
global _plugin
|
global _plugin
|
||||||
_plugin.onHeartbeat()
|
_plugin.onHeartbeat()
|
||||||
@ -400,3 +343,28 @@ def DumpConfigToLog():
|
|||||||
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
|
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
|
||||||
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
|
Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# get Modbus float 32 bits values
|
||||||
|
def getmodbus(register, client):
|
||||||
|
value = 0
|
||||||
|
try:
|
||||||
|
data = client.read_input_registers(register, 2)
|
||||||
|
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 = round(decoder.decode_32bit_float(), 3)
|
||||||
|
except:
|
||||||
|
Domoticz.Error("Error getting data from "+str(register) + ", try 1")
|
||||||
|
try:
|
||||||
|
data = client.read_input_registers(register, 2)
|
||||||
|
Domoticz.Debug("Data from register "+str(register)+": "+str(data))
|
||||||
|
decoder = BinaryPayloadDecoder.fromRegisters(data, byteorder=Endian.BIG, wordorder=Endian.BIG)
|
||||||
|
value = round(decoder.decode_32bit_float(), 3)
|
||||||
|
except:
|
||||||
|
Domoticz.Error("Error getting data from "+str(register) + ", try 2")
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
|
sdm_modbus
|
||||||
pymodbus
|
pymodbus
|
||||||
pymodbusTCP
|
|
||||||
|
|||||||
Reference in New Issue
Block a user