Advertisement
eSbek

Modbus_write_1.09_by_user_variable

Sep 12th, 2020
525
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.33 KB | None | 0 0
  1. # Modbus RTU/ASCII/TCP - Universal WRITE Plugin for Domoticz
  2. #
  3. # Author: Sebastiaan Ebeltjes / domoticx.nl
  4. # Serial HW: USB RS485-Serial Stick, like: http://domoticx.nl/webwinkel/index.php?route=product/product&product_id=386
  5. #
  6. # Dependancies:
  7. # - pymodbus AND pymodbusTCP:
  8. #   - Install for python 3.x with: sudo pip3 install -U pymodbus pymodbusTCP
  9. #
  10. # NOTE: Some "name" fields are abused to put in more options ;-)
  11. #
  12. """
  13. <plugin key="ModbusWRITE-using-UserVariable" name="Modbus RTU/ASCII/TCP - WRITE using User Variable v1.0.9" author="S. Ebeltjes / domoticx.nl" version="1.0.8" externallink="" wikilink="https://github.com/DomoticX/domoticz-modbus/">
  14.    <params>
  15.        <param field="Mode4" label="Debug" width="120px">
  16.            <options>
  17.                <option label="True" value="debug"/>
  18.                <option label="False" value="normal"  default="true" />
  19.            </options>
  20.        </param>
  21.        <param field="Mode1" label="Method" width="120px" required="true">
  22.            <options>
  23.                <option label="RTU" value="rtu" default="true"/>
  24.                <option label="ASCII" value="ascii"/>
  25.                <option label="RTU over TCP" value="rtutcp"/>
  26.                <option label="TCP/IP" value="tcpip"/>
  27.            </options>
  28.        </param>
  29.        <param field="SerialPort" label="Serial Port" width="120px" required="true"/>
  30.        <param field="Mode2" label="Baudrate" width="70px" required="true">
  31.            <options>
  32.                <option label="1200" value="1200"/>
  33.                <option label="2400" value="2400"/>
  34.                <option label="4800" value="4800"/>
  35.                <option label="9600" value="9600" default="true"/>
  36.                <option label="14400" value="14400"/>
  37.                <option label="19200" value="19200"/>
  38.                <option label="38400" value="38400"/>
  39.                <option label="57600" value="57600"/>
  40.                <option label="115200" value="115200"/>
  41.            </options>
  42.        </param>
  43.        <param field="Mode3" label="Port settings" width="260px" required="true">
  44.            <options>
  45.                <option label="StopBits 1 / ByteSize 7 / Parity: None" value="S1B7PN"/>
  46.                <option label="StopBits 1 / ByteSize 7 / Parity: Even" value="S1B7PE"/>
  47.                <option label="StopBits 1 / ByteSize 7 / Parity: Odd" value="S1B7PO"/>
  48.                <option label="StopBits 1 / ByteSize 8 / Parity: None" value="S1B8PN" default="true"/>
  49.                <option label="StopBits 1 / ByteSize 8 / Parity: Even" value="S1B8PE"/>
  50.                <option label="StopBits 1 / ByteSize 8 / Parity: Odd" value="S1B8PO"/>
  51.                <option label="StopBits 2 / ByteSize 7 / Parity: None" value="S2B7PN"/>
  52.                <option label="StopBits 2 / ByteSize 7 / Parity: Even" value="S2B7PE"/>
  53.                <option label="StopBits 2 / ByteSize 7 / Parity: Odd" value="S2B7PO"/>
  54.                <option label="StopBits 2 / ByteSize 8 / Parity: None" value="S2B8PN"/>
  55.                <option label="StopBits 2 / ByteSize 8 / Parity: Even" value="S2B8PE"/>
  56.                <option label="StopBits 2 / ByteSize 8 / Parity: Odd" value="S2B8PO"/>
  57.            </options>
  58.        </param>
  59.        <param field="Address" label="Device address /ID(TCP)" width="120px" required="true"/>
  60.        <param field="Port" label="Port (TCP)" value="502" width="75px"/>
  61.        <param field="Username" label="Modbus Function" width="280px" required="true">
  62.            <options>
  63.                <option label="Write Single Coil (Function 5)" value="5"/>
  64.                <option label="Write Single Holding Register (Function 6)" value="6" default="true"/>
  65.                <option label="Write Multiple Coils (Function 15)" value="15"/>
  66.                <option label="Write Registers (Function 16)" value="16"/>
  67.            </options>
  68.        </param>
  69.        <param field="Password" label="Register number" width="75px" required="true"/>
  70.        <param field="Mode5" label="User Variable IDX" width="75px"/>
  71.        <param field="Mode6" label="Payload OFF (not used)" width="75px"/>
  72.    </params>
  73. </plugin>
  74. """
  75. import Domoticz
  76.  
  77. import sys
  78.  
  79. #DSM 6.2 python 3.5.1
  80. #sys.path.append('/usr/local/lib/python3.5/site-packages')
  81. #sys.path.append('/volume1/@appstore/py3k/usr/local/lib/python3.5/site-packages')
  82.  
  83. #Windows 10 Python 3.5.1
  84. sys.path.append("C:\Program Files (x86)\Python35-32\Lib\site-packages")
  85.  
  86. #Linux
  87. #sys.path.append('/usr/local/lib/python3.4/dist-packages')
  88. #sys.path.append('/usr/local/lib/python3.5/dist-packages')
  89.  
  90. from pymodbus.client.sync import ModbusSerialClient
  91. from pymodbus.client.sync import ModbusTcpClient
  92. from pyModbusTCP.client import ModbusClient
  93. from pymodbus.transaction import ModbusRtuFramer
  94.  
  95. from pymodbus.constants import Endian
  96. from pymodbus.payload import BinaryPayloadBuilder
  97.  
  98. import json
  99. import requests
  100.  
  101.  
  102. result=""
  103.  
  104.  
  105. class BasePlugin:
  106.     enabled = False
  107.     def __init__(self):
  108.         return
  109.  
  110.     def onStart(self):
  111.         # Domoticz.Log("onStart called")
  112.         if Parameters["Mode4"] == "debug": Domoticz.Debugging(1)
  113.         if (len(Devices) == 0): Domoticz.Device(Name="ModbusDEV-WRITE", Unit=1, TypeName="Switch", Image=0, Used=1).Create() # Used=1 to add a switch immediatly!
  114.         DumpConfigToLog()
  115.         Domoticz.Log("Modbus RS485 RTU/ASCII/TCP - Universal WRITE loaded.")
  116.  
  117.     def onStop(self):
  118.         Domoticz.Log("onStop called")
  119.  
  120.     def onConnect(self, Connection, Status, Description):
  121.         Domoticz.Log("onConnect called")
  122.         return
  123.  
  124.     def onMessage(self, Connection, Data, Status, Extra):
  125.         Domoticz.Log("onMessage called")
  126.  
  127.     #def onHeartbeat(self, Unit, Command, Level, Hue):
  128.     def onCommand(self, Unit, Command, Level, Hue):
  129.         Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
  130.  
  131.         # Wich serial port settings to use?
  132.         if (Parameters["Mode3"] == "S1B7PN"): StopBits, ByteSize, Parity = 1, 7, "N"
  133.         if (Parameters["Mode3"] == "S1B7PE"): StopBits, ByteSize, Parity = 1, 7, "E"
  134.         if (Parameters["Mode3"] == "S1B7PO"): StopBits, ByteSize, Parity = 1, 7, "O"
  135.         if (Parameters["Mode3"] == "S1B8PN"): StopBits, ByteSize, Parity = 1, 8, "N"
  136.         if (Parameters["Mode3"] == "S1B8PE"): StopBits, ByteSize, Parity = 1, 8, "E"
  137.         if (Parameters["Mode3"] == "S1B8PO"): StopBits, ByteSize, Parity = 1, 8, "O"
  138.         if (Parameters["Mode3"] == "S2B7PN"): StopBits, ByteSize, Parity = 2, 7, "N"
  139.         if (Parameters["Mode3"] == "S2B7PE"): StopBits, ByteSize, Parity = 2, 7, "E"
  140.         if (Parameters["Mode3"] == "S2B7PO"): StopBits, ByteSize, Parity = 2, 7, "O"
  141.         if (Parameters["Mode3"] == "S2B8PN"): StopBits, ByteSize, Parity = 2, 8, "N"
  142.         if (Parameters["Mode3"] == "S2B8PE"): StopBits, ByteSize, Parity = 2, 8, "E"
  143.         if (Parameters["Mode3"] == "S2B8PO"): StopBits, ByteSize, Parity = 2, 8, "O"
  144.  
  145.        
  146.         response = requests.get("http://127.0.0.1:8084/json.htm?type=command&param=getuservariable&idx="+str(Parameters["Mode5"]))
  147.         todos = json.loads(response.text)
  148.         VariableValue = todos['result'][0]['Value']
  149.        
  150.         Domoticz.Log("User Variable: "+VariableValue)
  151.         # Which payload to execute?
  152.         # payload = Level # Set payload from slider/scroll
  153.         # Set payload if a button has been pressed
  154.        
  155.        
  156.        
  157.         #if (str(Command) == "On"): payload = Parameters["Mode5"]
  158.         if (str(Command) == "On"): payload = hex(int(VariableValue))
  159.  
  160.         #if (str(Command) == "Off"): payload = Parameters["Mode6"]
  161.         if (str(Command) == "Off"): payload = hex(int(VariableValue))
  162.  
  163.         # Split address to support TCP/IP device ID
  164.         AddressData = Parameters["Address"].split("/") # Split on "/"
  165.         UnitAddress = AddressData[0]
  166.  
  167.         # Is there a unit ID given after the IP? (e.g. 192.168.2.100/56)
  168.         UnitIdForIp = 1 # Default
  169.         if len(AddressData) > 1:
  170.           UnitIdForIp = AddressData[1]
  171.        
  172.         ###################################
  173.         # pymodbus: RTU / ASCII
  174.         ###################################
  175.         if (Parameters["Mode1"] == "rtu" or Parameters["Mode1"] == "ascii"):
  176.           Domoticz.Debug("MODBUS DEBUG USB SERIAL HW - Port="+Parameters["SerialPort"]+" BaudRate="+Parameters["Mode2"]+" StopBits="+str(StopBits)+" ByteSize="+str(ByteSize)+" Parity="+Parity)
  177.           Domoticz.Debug("MODBUS DEBUG USB SERIAL CMD - Method="+Parameters["Mode1"]+" Address="+UnitAddress+" Register="+Parameters["Password"]+" Function="+Parameters["Username"]+" PayLoadON="+Parameters["Mode5"]+" PayLoadOFF="+Parameters["Mode6"])
  178.           try:
  179.             client = ModbusSerialClient(method=Parameters["Mode1"], port=Parameters["SerialPort"], stopbits=StopBits, bytesize=ByteSize, parity=Parity, baudrate=int(Parameters["Mode2"]), timeout=1, retries=2)
  180.           except:
  181.             Domoticz.Log("Error opening Serial interface on "+Parameters["SerialPort"])
  182.             Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  183.  
  184.         ###################################
  185.         # pymodbus: RTU over TCP
  186.         ###################################
  187.         if (Parameters["Mode1"] == "rtutcp"):
  188.           Domoticz.Debug("MODBUS DEBUG TCP CMD - Method="+Parameters["Mode1"]+" Address="+UnitAddress+" Port="+Parameters["Port"]+" PayLoadON="+Parameters["Mode5"]+" PayLoadOFF="+Parameters["Mode6"])
  189.           try:
  190.             client = ModbusTcpClient(host=UnitAddress, port=int(Parameters["Port"]), framer=ModbusRtuFramer, auto_open=True, auto_close=True, timeout=5)
  191.           except:
  192.             Domoticz.Log("Error opening TCP interface on adress: "+UnitAddress)
  193.             Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  194.  
  195.         ###################################
  196.         # pymodbusTCP: TCP/IP
  197.         ###################################
  198.         if (Parameters["Mode1"] == "tcpip"):
  199.           Domoticz.Debug("MODBUS DEBUG TCP CMD - Method="+Parameters["Mode1"]+", Address="+UnitAddress+", Port="+Parameters["Port"]+", Unit ID="+UnitIdForIp+", Register="+Parameters["Password"]+", Data type="+Parameters["Mode6"])
  200.           try:
  201.             client = ModbusClient(host=UnitAddress, port=int(Parameters["Port"]), unit_id=UnitIdForIp, auto_open=True, auto_close=True, timeout=5)
  202.           except:
  203.             Domoticz.Log("Error opening TCP/IP interface on address: "+UnitAddress)
  204.             Devices[1].Update(0, "0") # Update device in Domoticz
  205.  
  206.         ###################################
  207.         # pymodbus section
  208.         ###################################
  209.         if (Parameters["Mode1"] == "rtu" or Parameters["Mode1"] == "ascii" or Parameters["Mode1"] == "rtutcp"):
  210.           try:
  211.             # Which function to execute? RTU/ASCII/RTU over TCP
  212.             if (Parameters["Username"] == "5"): result = client.write_coil(int(Parameters["Password"]), int(payload, 16), unit=int(UnitIdForIp))
  213.             if (Parameters["Username"] == "6"): result = client.write_register(int(Parameters["Password"]), int(payload, 16), unit=int(UnitIdForIp))
  214.             if (Parameters["Username"] == "15"): result = client.write_coils(int(Parameters["Password"]), int(payload, 16), unit=int(UnitIdForIp))
  215.             if (Parameters["Username"] == "16"): result = client.write_registers(int(Parameters["Password"]), int(payload, 16), unit=int(UnitIdForIp))
  216.             client.close()
  217.  
  218.             if (str(Command) == "On"): Devices[1].Update(1, "1") # Update device to ON in Domoticz
  219.             if (str(Command) == "Off"): Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  220.           except:
  221.             Domoticz.Log("Modbus error communicating! (RTU/ASCII/RTU over TCP), check your settings!")
  222.             Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  223.  
  224.         ###################################
  225.         # pymodbusTCP section
  226.         ###################################
  227.         if (Parameters["Mode1"] == "tcpip"):
  228.           try:
  229.             # Which function to execute? TCP/IP
  230.             if (Parameters["Username"] == "5"): data = client.write_single_coil(int(Parameters["Password"]), int(payload))
  231.             if (Parameters["Username"] == "6"): data = client.write_single_register(int(Parameters["Password"]), int(payload))
  232.             if (Parameters["Username"] == "15"): data = client.write_multiple_coils(int(Parameters["Password"]), [payload])  # TODO split up multiple bytes to proper array.
  233.             if (Parameters["Username"] == "16"): data = client.write_multiple_registers(int(Parameters["Password"]), [payload]) # TODO split up multiple bytes to proper array.
  234.             client.close()
  235.  
  236.             if (str(Command) == "On"): Devices[1].Update(1, "1") # Update device to ON in Domoticz
  237.             if (str(Command) == "Off"): Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  238.           except:
  239.             Domoticz.Log("Modbus error communicating! (TCP/IP), check your settings!")
  240.             Devices[1].Update(0, "0") # Update device to OFF in Domoticz
  241.  
  242.     def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
  243.         Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
  244.  
  245.     def onDisconnect(self, Connection):
  246.         Domoticz.Log("onDisconnect called")
  247.  
  248.     def onHeartbeat(self):
  249.         # Domoticz.Log("onHeartbeat called")
  250.         return
  251.  
  252.     def UpdateDevice(Unit, nValue, sValue):
  253.         # Make sure that the Domoticz device still exists (they can be deleted) before updating it
  254.         if (Unit in Devices):
  255.           if (Devices[Unit].nValue != nValue) or (Devices[Unit].sValue != sValue):
  256.             Devices[Unit].Update(nValue, str(sValue))
  257.             Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
  258.         return
  259.  
  260. global _plugin
  261. _plugin = BasePlugin()
  262.  
  263. def onStart():
  264.     global _plugin
  265.     _plugin.onStart()
  266.  
  267. def onStop():
  268.     global _plugin
  269.     _plugin.onStop()
  270.  
  271. def onConnect(Connection, Status, Description):
  272.     global _plugin
  273.     _plugin.onConnect(Connection, Status, Description)
  274.  
  275. def onMessage(Connection, Data, Status, Extra):
  276.     global _plugin
  277.     _plugin.onMessage(Connection, Data, Status, Extra)
  278.  
  279. def onCommand(Unit, Command, Level, Hue):
  280.     global _plugin
  281.     _plugin.onCommand(Unit, Command, Level, Hue)
  282.  
  283. def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
  284.     global _plugin
  285.     _plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)
  286.  
  287. def onDisconnect(Connection):
  288.     global _plugin
  289.     _plugin.onDisconnect(Connection)
  290.  
  291. def onHeartbeat():
  292.     global _plugin
  293.     _plugin.onHeartbeat()
  294.  
  295.     # Generic helper functions
  296. def DumpConfigToLog():
  297.     for x in Parameters:
  298.         if Parameters[x] != "":
  299.             Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
  300.     Domoticz.Debug("Device count: " + str(len(Devices)))
  301.     for x in Devices:
  302.         Domoticz.Debug("Device:           " + str(x) + " - " + str(Devices[x]))
  303.         Domoticz.Debug("Device ID:       '" + str(Devices[x].ID) + "'")
  304.         Domoticz.Debug("Device Name:     '" + Devices[x].Name + "'")
  305.         Domoticz.Debug("Device nValue:    " + str(Devices[x].nValue))
  306.         Domoticz.Debug("Device sValue:   '" + Devices[x].sValue + "'")
  307.         Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
  308.     return
  309.  
Advertisement
Comments
  • RichardLuijsterburg
    2 years (edited)
    # text 0.38 KB | 0 0
    1. Hi,
    2.  
    3. I've added the plugin but it added a switch (boolian).
    4.  
    5. And it doesn't update a value.
    6.  
    7. I've used the option Write single Holding register.
    8.  
    9. The version of Domoticz is:
    10.  
    11. Version: 2022.1
    12. Build Hash: c9526851b
    13. Compile Date: 2022-01-31 09:34:32
    14. dzVents Version: 3.1.8
    15. Python Version: 3.8.10 (default, Jun 22 2022, 20:18:18) [GCC 9.4.0]
    16.  
    17. best regards
    18.  
    19. Richard Luijsterburg
Add Comment
Please, Sign In to add comment
Advertisement