Weeder I/O Board

Python and python framework

Moderator: leecollings

Post Reply
smartel
Posts: 6
Joined: Monday 24 December 2018 0:29
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Quebec,Canada
Contact:

Weeder I/O Board

Post by smartel »

Hello All,

I am in the process of creating a plugin to integrate a Weeder board WDTIO like this one : https://weedtech.com/digital-in.html

Here it is :

Code: Select all

#           Weeder Plugin
#
#           Author:     Sergio
#
#
#   Plugin parameter definition below will be parsed during startup and copied into Manifest.xml, this will then drive the user interface in the Hardware web page
#
"""
<plugin key="Weeder" name="Weeder WDTIO Board" author="serge" version="0.0.1" externallink="http://www.weedtech.com/">
    <description>
Weeder Board <br/><br/>
&quot;Inputs&quot; and &quot;Outputs&quot; need to have '|' delimited letters of inputs and outputs<br/>
    </description>
    
    
    <params>
        <param field="SerialPort" label="Serial Port" width="150px" required="true" default="/dev/tty/weeder"/>
		
		<param field="Mode4" label="Inputs (Zone letter seperated by | )" width="550px" required="false" default="A|B|C|D|E|F"/>
        <param field="Mode5" label="Outputs (Zone letter seperated by | )" width="550px" required="false" default="G|H|I|J|K|L"/>
        <param field="Mode6" label="Debug" width="100px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal"  default="true" />
                <option label="Logging" value="File"/>
            </options>
        </param>
    </params>
</plugin>
"""

import Domoticz
import os
import time
enabled = False

serialBuffer = ""

class BasePlugin:
    SerialConn = None
    outputs = []
    inputs = []
    def __init__(self):
        return

    def onStart(self):
        Domoticz.Log("onStart called")
        
        if (Parameters["Mode6"] != "Normal"):
            Domoticz.Debugging(1)  
        
        DumpConfigToLog()
        self.inputs = Parameters["Mode4"].split('|')
        self.outputs = Parameters["Mode5"].split('|')
        Domoticz.Log("Discovered inputs : " + str (len(self.inputs)) + "Discovered outputs : "+ str (len(self.outputs)))
        LogMessage("Discovered inputs : " + str (len(self.inputs)) + "Discovered outputs : "+ str (len(self.outputs)))
        if (len(Devices) ==0):
            createDevices()
        self.SerialConn = Domoticz.Connection(Name="Weeder", Transport="Serial", Protocol="line", Address=Parameters["SerialPort"], Baud=9600)
        self.SerialConn.Connect()
        self.SerialConn.Send("AX0\r",Delay=2)
        Domoticz.Heartbeat(30)
        initialize()
        Domoticz.Log("Plugin has (on start)" + str(len(Devices)) + " devices associated with it.")
        LogMessage("Plugin has (on start)" + str(len(Devices)) + " devices associated with it.")
        return

    def createDevices(self):
        LogMessage("Create devices called")
        for zone in range (0,len(self.inputs)): 
            Domoticz.Log("Created input "+self.inputs[zone])
            LogMessage("Created input "+self.inputs[zone])
            Domoticz.Device(Name= "Input Zone "+ self.inputs[zone],Unit=zone+1,TypeName="Switch").Create()
            
        for out in range (0,len(self.outputs)): 
            LogMessage("Created output "+self.outputs[out])
            Domoticz.Log("Created output "+self.outputs[out])
            Domoticz.Device(Name= "Output Zone "+ self.outputs[out],Unit=len(self.inputs)+out+1,TypeName="Switch").Create()            

    def onStop(self):
        Domoticz.Log("onStop called")
        LogMessage("onStop called")

    def onConnect(self, Connection, Status, Description):
        Domoticz.Log("onConnect called")
        LogMessage("onStop called")
        if (Status == 0):
            Domoticz.Log("Connected successfully to: "+Parameters["SerialPort"])
            LogMessage("Connected successfully to: "+Parameters["SerialPort"])
        else:
            Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["SerialPort"])
            LogMessage("Failed to connect ("+str(Status)+") to: "+Parameters["SerialPort"]+" with error: "+Description)
        return True

    def onMessage(self, Connection, Data):
        global serialBuffer
        serialBuffer  += Data.decode("ascii")
        messages = serialBuffer.split("\r")
        i=0
        while (i < len(messages) - 1):
            strData = messages[i]
            #if ("A!" in strData):
            if (len(strData) != 3):
                LogMessage("Weeder Board A reset")
                LogMessage("Received [{0}] handling ...".format(strData.replace("\r", "<cr>")))
                os.system("sudo monit restart all")

            else:
                board = strData[0]
                zone = strData[1]
                state = strData[2]
                LogMessage("Received [{0}] handling ...".format(strData.replace("\r", "<cr>")))

                if (state == "L"):
                    LogMessage("Weeder Zone "+zone+" state: "+state)
                    updateDevice(zoneAlphaToDecimal(zone),0,"Ferme")
                elif (state == "H"):
                    LogMessage("Weeder Zone "+zone+" state: "+state)
                    updateDevice(zoneAlphaToDecimal(zone),1,"Ouvert")
 
                i += 1

            serialBuffer = messages[len(messages) - 1]
        return

    def initialize(self):
        LogMessage("Initialize called")
        for zone in range (0,len(self.inputs)): # Zone A-N
            LogMessage("Initialized input "+self.inputs[zone])
            self.SerialConn.Send("AS"+self.inputs[zone]+"\r",Delay=5)
            #time.sleep(5)
    def updateDevice(self,Unit,nValue,sValue):
        if (Unit in Devices):
            if (Devices[Unit].nValue != nValue) or (Devices[Unit].sValue != sValue):
                Devices[Unit].Update(nValue, str(sValue))
                LogMessage("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
                Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
        return

    def onCommand(self, Unit, Command, Level, Hue):
        Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
        LogMessage("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
        newstate=1 if Command=="On" else 0
        newstateName="On" if Command=="On" else "Off"
        if (Command == "On" and zoneDecimalToAlpha(Unit) in self.outputs):
            self.SerialConn.Send("AH"+zoneDecimalToAlpha(Unit)+"\r",Delay=1)
            Devices[Unit].Update(nValue=newstate, sValue=newstateName)
        if (Command == "Off" and zoneDecimalToAlpha(Unit) in self.outputs):
            self.SerialConn.Send("AL"+zoneDecimalToAlpha(Unit)+"\r",Delay=1)
            Devices[Unit].Update(nValue=newstate, sValue=newstateName)

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
        Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
        LogMessage("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
    
    def onDisconnect(self, Connection):
        Domoticz.Log("onDisconnect called")
        LogMessage("onDisconnect called")
        self.SerialConn.Disconnect
        for Device in Devices:
            Devices[Device].Update(nValue=Devices[Device].nValue, sValue=Devices[Device].sValue, TimedOut=1)
        Domoticz.Log("Connection '"+Connection.Name+"' disconnected.")
        return

    def onHeartbeat(self):
        # if (self.SerialConn.Connected()):
        #     Domoticz.Debug("onHeartbeat called")
        # else:
        #     Domoticz.Log("onHeartbeat called reconnect")
        #     SerialConn.Connect()
        return True
   
global _plugin
_plugin = BasePlugin()

def onStart():
    global _plugin
    _plugin.onStart()

def initialize():
    global _plugin
    _plugin.initialize()

def createDevices():
    global _plugin
    _plugin.createDevices()

def updateDevice(Unit,nValue,sValue):
    global _plugin
    _plugin.updateDevice(Unit,nValue,sValue)

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
def DumpConfigToLog():
    for x in Parameters:
        if Parameters[x] != "":
            LogMessage( "'" + x + "':'" + str(Parameters[x]) + "'")
    LogMessage("Device count: " + str(len(Devices)))
    for x in Devices:
        LogMessage("Device:           " + str(x) + " - " + str(Devices[x]))
        LogMessage("Device ID:       '" + str(Devices[x].ID) + "'")
        LogMessage("Device Name:     '" + Devices[x].Name + "'")
        LogMessage("Device nValue:    " + str(Devices[x].nValue))
        LogMessage("Device sValue:   '" + Devices[x].sValue + "'")
        LogMessage("Device LastLevel: " + str(Devices[x].LastLevel))
    return

def LogMessage(Message):
    if Parameters["Mode6"] == "File":
        f = open(Parameters["HomeFolder"]+"weederplugin.log","a")
        f.write(Message+"\r\n")
        f.close()
    Domoticz.Debug(Message)

def zoneAlphaToDecimal(Alpha): 
    switcher = { 
        "A" : 1, 
        "B" : 2,
        "C" : 3,
        "D" : 4,
        "E" : 5,
        "F" : 6,
        "G" : 7,
        "H" : 8,
        "I" : 9,
        "J" : 10,
        "K" : 11,
        "L" : 12,
        "M" : 13,
        "N" : 14
    }
    return switcher.get(Alpha, "invalid")

def zoneDecimalToAlpha(Decimal): 
    switcher = { 
        1 : "A", 
        2 : "B",
        3 : "C",
        4 : "D",
        5 : "E",
        6 : "F",
        7 : "G",
        8 : "H",
        9 : "I",
        10 : "J",
        11 : "K",
        12 : "L",
        13 : "M",
        14 : "N"
    }
    return switcher.get(Decimal, "invalid")
The problem I am facing, is in the initialize process, I would like to introduce a delay between my commands, if I do this :

Code: Select all

    def initialize(self):
        LogMessage("Initialize called")
        for zone in range (0,len(self.inputs)): # Zone A-N
            LogMessage("Initialized input "+self.inputs[zone])
            self.SerialConn.Send("AS"+self.inputs[zone]+"\r",Delay=5)
            #time.sleep(5)
My entire initilization process is delayed by 5 seconds (I made it 5 seconds for a test I actually need 1/2 second), not the individual commands.
The same thing happens if I introduce a sleep:

Code: Select all

time.sleep(5)


I know it is not recommended, it was for a test, but it does not work either. My commands are all issued at the same time, and the board does not respond to 100% of my commands.

It is my first plugin, comments are more than welcome

Thank you!
smartel
Posts: 6
Joined: Monday 24 December 2018 0:29
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Quebec,Canada
Contact:

Re: Weeder I/O Board

Post by smartel »

For those interested I made a version using the onHeartbeat() to initialize my Inputs, I adjust the heartbeat delay until all inputs are initialized, here is the modified version:

Comments are still welcome to improve!

Code: Select all

#           Weeder Plugin
#
#           
#
#
#   Plugin parameter definition below will be parsed during startup and copied into Manifest.xml, this will then drive the user interface in the Hardware web page
#
"""
<plugin key="Weeder" name="Weeder WDTIO Board" author="smartel" version="1.0.0" externallink="http://www.weedtech.com/">
    <description>
Weeder Board <br/><br/>
&quot;Inputs&quot; and &quot;Outputs&quot; need to have '|' delimited letters of inputs and outputs<br/>
    </description>
    
    
    <params>
        <param field="SerialPort" label="Serial Port" width="150px" required="true" default="/dev/tty/weeder"/>
		
		<param field="Mode4" label="Inputs (Zone letter seperated by | )" width="550px" required="false" default="A|B|C|D|E|F"/>
        <param field="Mode5" label="Outputs (Zone letter seperated by | )" width="550px" required="false" default="G|H|I|J|K|L"/>
        <param field="Mode6" label="Debug" width="100px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal"  default="true" />
                <option label="Logging" value="File"/>
            </options>
        </param>
    </params>
</plugin>
"""

import Domoticz
import os
import time
enabled = False

serialBuffer = ""

class BasePlugin:
    SerialConn = None
    outputs = []
    inputs = []
    hbCounter = 0
    def __init__(self):
        return

    def onStart(self):
        Domoticz.Log("onStart called")
        
        if (Parameters["Mode6"] != "Normal"):
            Domoticz.Debugging(1)  
        
        DumpConfigToLog()
        self.inputs = Parameters["Mode4"].split('|')
        self.outputs = Parameters["Mode5"].split('|')
        Domoticz.Log("Discovered inputs : " + str (len(self.inputs)) + " Discovered outputs : "+ str (len(self.outputs)))
        LogMessage("Discovered inputs : " + str (len(self.inputs)) + " Discovered outputs : "+ str (len(self.outputs)))
        if (len(Devices) ==0):
            createDevices()
        self.SerialConn = Domoticz.Connection(Name="Weeder", Transport="Serial", Protocol="line", Address=Parameters["SerialPort"], Baud=9600)
        self.SerialConn.Connect()
        self.SerialConn.Send("AX0\r",Delay=2)
        Domoticz.Heartbeat(3)
        initialize()
        Domoticz.Log("Plugin has (on start)" + str(len(Devices)) + " devices associated with it.")
        LogMessage("Plugin has (on start)" + str(len(Devices)) + " devices associated with it.")
        return

    def createDevices(self):
        LogMessage("Create devices called")
        for zone in range (0,len(self.inputs)): 
            Domoticz.Log("Created input "+self.inputs[zone])
            LogMessage("Created input "+self.inputs[zone])
            Domoticz.Device(Name= "Input Zone "+ self.inputs[zone],Unit=zone+1,TypeName="Switch").Create()
            
        for out in range (0,len(self.outputs)): 
            LogMessage("Created output "+self.outputs[out])
            Domoticz.Log("Created output "+self.outputs[out])
            Domoticz.Device(Name= "Output Zone "+ self.outputs[out],Unit=len(self.inputs)+out+1,TypeName="Switch").Create()            

    def onStop(self):
        Domoticz.Log("onStop called")
        LogMessage("onStop called")

    def onConnect(self, Connection, Status, Description):
        Domoticz.Log("onConnect called")
        LogMessage("onStop called")
        if (Status == 0):
            Domoticz.Log("Connected successfully to: "+Parameters["SerialPort"])
            LogMessage("Connected successfully to: "+Parameters["SerialPort"])
        else:
            Domoticz.Log("Failed to connect ("+str(Status)+") to: "+Parameters["SerialPort"])
            LogMessage("Failed to connect ("+str(Status)+") to: "+Parameters["SerialPort"]+" with error: "+Description)
        return True

    def onMessage(self, Connection, Data):
        global serialBuffer
        serialBuffer  += Data.decode("ascii")
        messages = serialBuffer.split("\r")
        i=0
        while (i < len(messages) - 1):
            strData = messages[i]
            #if ("A!" in strData):
            if (len(strData) != 3):
                LogMessage("Weeder Board A reset")
                Domoticz.Log("Weeder Board A reset")
                LogMessage("Received [{0}] handling ...".format(strData.replace("\r", "<cr>")))
                os.system("sudo monit restart all")

            else:
                board = strData[0]
                zone = strData[1]
                state = strData[2]
                LogMessage("Received [{0}] handling ...".format(strData.replace("\r", "<cr>")))
                Domoticz.Log("Received [{0}] handling ...".format(strData.replace("\r", "<cr>")))
                if (state == "L"):
                    LogMessage("Weeder Zone "+zone+" state: "+state)
                    Domoticz.Log("Weeder Zone "+zone+" state: "+state)
                    updateDevice(zoneAlphaToDecimal(zone),0,"Ferme")
                elif (state == "H"):
                    LogMessage("Weeder Zone "+zone+" state: "+state)
                    Domoticz.Log("Weeder Zone "+zone+" state: "+state)
                    updateDevice(zoneAlphaToDecimal(zone),1,"Ouvert")
 
                i += 1

            serialBuffer = messages[len(messages) - 1]
        return

    def initialize(self):
        LogMessage("Initialize called")
     
        for zone in range (0,len(self.inputs)): # Zone A-N
            LogMessage("Initialized input "+self.inputs[zone])
            self.SerialConn.Send("AS"+self.inputs[zone]+"\r",Delay=5)

    def updateDevice(self,Unit,nValue,sValue):
        if (Unit in Devices):
            if (Devices[Unit].nValue != nValue) or (Devices[Unit].sValue != sValue):
                Devices[Unit].Update(nValue, str(sValue))
                LogMessage("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
                Domoticz.Log("Update "+str(nValue)+":'"+str(sValue)+"' ("+Devices[Unit].Name+")")
        return

    def onCommand(self, Unit, Command, Level, Hue):
        Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
        LogMessage("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
        newstate=1 if Command=="On" else 0
        newstateName="On" if Command=="On" else "Off"
        if (Command == "On" and zoneDecimalToAlpha(Unit) in self.outputs):
            self.SerialConn.Send("AH"+zoneDecimalToAlpha(Unit)+"\r",Delay=1)
            Devices[Unit].Update(nValue=newstate, sValue=newstateName)
        if (Command == "Off" and zoneDecimalToAlpha(Unit) in self.outputs):
            self.SerialConn.Send("AL"+zoneDecimalToAlpha(Unit)+"\r",Delay=1)
            Devices[Unit].Update(nValue=newstate, sValue=newstateName)

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
        Domoticz.Log("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
        LogMessage("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(Priority) + "," + Sound + "," + ImageFile)
    
    def onDisconnect(self, Connection):
        Domoticz.Log("onDisconnect called")
        LogMessage("onDisconnect called")
        self.SerialConn.Disconnect
        for Device in Devices:
            Devices[Device].Update(nValue=Devices[Device].nValue, sValue=Devices[Device].sValue, TimedOut=1)
        Domoticz.Log("Connection '"+Connection.Name+"' disconnected.")
        return

    def onHeartbeat(self):
        
        LogMessage("onHeartbeat called :" + str(self.hbCounter) + " times")
        if (zoneDecimalToAlpha(self.hbCounter+1) in self.inputs):
            self.SerialConn.Send("AS"+self.inputs[self.hbCounter]+"\r",Delay=1)
            LogMessage("Re-Initialized input "+self.inputs[self.hbCounter])
        self.hbCounter +=1

        if (self.hbCounter == 15):
            Domoticz.Heartbeat(30)
            Domoticz.Log("Finished Re-Initalization ")
            LogMessage("Finished Re-Initalization ")

        return True
   
global _plugin
_plugin = BasePlugin()

def onStart():
    global _plugin
    _plugin.onStart()

def initialize():
    global _plugin
    _plugin.initialize()

def createDevices():
    global _plugin
    _plugin.createDevices()

def updateDevice(Unit,nValue,sValue):
    global _plugin
    _plugin.updateDevice(Unit,nValue,sValue)

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
def DumpConfigToLog():
    for x in Parameters:
        if Parameters[x] != "":
            LogMessage( "'" + x + "':'" + str(Parameters[x]) + "'")
    LogMessage("Device count: " + str(len(Devices)))
    for x in Devices:
        LogMessage("Device:           " + str(x) + " - " + str(Devices[x]))
        LogMessage("Device ID:       '" + str(Devices[x].ID) + "'")
        LogMessage("Device Name:     '" + Devices[x].Name + "'")
        LogMessage("Device nValue:    " + str(Devices[x].nValue))
        LogMessage("Device sValue:   '" + Devices[x].sValue + "'")
        LogMessage("Device LastLevel: " + str(Devices[x].LastLevel))
    return

def LogMessage(Message):
    if Parameters["Mode6"] == "File":
        f = open(Parameters["HomeFolder"]+"weederplugin.log","a")
        f.write(Message+"\r\n")
        f.close()
    Domoticz.Debug(Message)

def zoneAlphaToDecimal(Alpha): 
    switcher = { 
        "A" : 1, 
        "B" : 2,
        "C" : 3,
        "D" : 4,
        "E" : 5,
        "F" : 6,
        "G" : 7,
        "H" : 8,
        "I" : 9,
        "J" : 10,
        "K" : 11,
        "L" : 12,
        "M" : 13,
        "N" : 14
    }
    return switcher.get(Alpha, "invalid")

def zoneDecimalToAlpha(Decimal): 
    switcher = { 
        1 : "A", 
        2 : "B",
        3 : "C",
        4 : "D",
        5 : "E",
        6 : "F",
        7 : "G",
        8 : "H",
        9 : "I",
        10 : "J",
        11 : "K",
        12 : "L",
        13 : "M",
        14 : "N"
    }
    return switcher.get(Decimal, "invalid")
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest