# -*- coding: utf-8 -*-
#
# This file is part of EventGhost.
# Copyright © 2005-2016 EventGhost Project <http://www.eventghost.org/>
#
# EventGhost is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 2 of the License, or (at your option)
# any later version.
#
# EventGhost is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with EventGhost. If not, see <http://www.gnu.org/licenses/>.



# Version 0.1.6: Configurable retry cycle added
# Verison 0.2.0: variable printname added,  fixed retry issue, multiload added, CreateMacrosOnAdd added
# Version 0.2.1: payload-tag added 

import eg

eg.RegisterPlugin(
	name = "ELV IPIO 88",
    	author = "Dieter Brillert",
    	version = "0.2.1",
    	kind = "external",
        canMultiLoad=True,
        createMacrosOnAdd=True,
        description = ("Very basic plugin to read the input states and "
	"set the output states of the ELV-IPIO88 Device. The plugin interacts "
	"with the web interface of the device. Please make sure that password "
	"check is DISABLED. The device has its own capabilities to combine "
	"input and output channels logicaly. Please be aware of that!"
	)
        
)

from threading import Event, Thread
import urllib2 as urllib

InStatus = []
OutStatus =[]
payloadtag =""

for r in range(9):
    InStatus.append("Null")
    OutStatus.append("Null")


class IPIO88(eg.PluginBase):

    def __init__(self):
        
        self.AddAction(GetAllInputStates)
        self.AddAction(GetInputState)
        self.AddAction(GetAllOutputStates)
        self.AddAction(GetOutputState)
        self.AddAction(SwitchAllOutputStatesOn)
        self.AddAction(SwitchAllOutputStatesOff)
        self.AddAction(SwitchOutputStateOn)
        self.AddAction(SwitchOutputStateOff)
        self.AddAction(DeleteAllinternalIOBindings)
        
    def __start__(self, ip, refresh, retry, payloadtag):

        global IP

        IP = ip
        self.payloadtag = payloadtag
        self.ip = ip
	
        self.stopThreadEvent = Event()
        thread = Thread(
            target=self.ThreadLoop,
            args=(self.stopThreadEvent,ip, refresh, retry, payloadtag )
        )
        thread.start()

    def __stop__(self):

        eg.Print (self.payloadtag +": Stopped reading from " +self.ip)
         
        self.stopThreadEvent.set()
                

    def ThreadLoop(self, stopThreadEvent, ip, refresh, retry, payloadtag):
               
        url = "http://%s/ipio.cgi" %ip

        print url

        suppress = True
        connect = None
        retrycount = 0
        
        eg.Print (payloadtag +": Start reading from IP "+ip)
        
        try:
            refreshtime = abs(float(refresh))
            eg.Print (payloadtag +": Refresh cycle time: %s s" %refreshtime)
        
        except ValueError:
            refreshtime = 2
            eg.PrintNotice (payloadtag +": Invalid refresh cycle time configured. -> Set to 2 s.")
                              
        
        try:
            retryNr = abs(int(retry))
            eg.Print (payloadtag +": Retry cycles: %s" %retryNr)
            
                   
        except ValueError:
            retryNr = 0
            eg.PrintNotice (payloadtag +": Invalid number of retry cycles configured. -> Set to 0")
            

        
        
                             	
	while not stopThreadEvent.isSet():	         
				
	               
            try:
                page = str(urllib.urlopen(url).read())
                
            except IOError:
                
                #print connect
                #print "try+"+str(retrycount)
                
                if retrycount < retryNr:
                    retrycount += 1               
                    eg.Print (payloadtag +": Try to connect to device ...")
                   
                 
                else:
                    
                    if connect !=False:
                        eg.PrintNotice (payloadtag +": Can't connect to device")
                        self.TriggerEvent("DeviceIO-Error")

                    connect = False
                    
                stopThreadEvent.wait(refreshtime)

            else:

                #print connect
                #print "reading"
                if retrycount > 0:
                    eg.Print (payloadtag +":... success, reconnected.")
                
                retrycount = 0
                
                if not connect:
                
                    self.TriggerEvent("DeviceConnected")
                
                connect = True
		
                for r in range(1,9):

        		string ="input name=\"in"+str(r)+"\" checked=\"checked\""	     		   
       			check = page.find(string)

        		if check == -1:
            			if InStatus[r] <> "off":
                			InStatus[r] = "off"
                                        
                                        if not suppress:
                                            self.TriggerEvent("Input%s.Off" %r, payload =payloadtag +"/In/%s/0" %r)
                			   
            
        
        		else:  
            			if InStatus[r] <> "on":
                			InStatus[r] = "on"
					
                                        if not suppress:
                                            self.TriggerEvent("Input%s.On" %r, payload =payloadtag +"/In/%s/1" %r)
                
        		string ="input name=\"out%s\" checked=\"checked\"" %r	     		   
       			check = page.find(string)

        		if check == -1:
            			if OutStatus[r] <> "off":
                			OutStatus[r] = "off"
                			
                                        if not suppress:
                                            self.TriggerEvent("Output%s.Off" %r, payload =payloadtag +"/Out/%s/0" %r)
                			    
            
        
        		else:  
            			if OutStatus[r] <> "on":
                			OutStatus[r] = "on"

                                        if not suppress:
					    self.TriggerEvent("Output%s.On" %r, payload =payloadtag +"/Out/%s/1" %r)
                			                           
      
        	suppress = False
	        
                stopThreadEvent.wait(refreshtime)

            

            


    def Configure(self, ip="192.168.100.1", refresh="2", retry ="0", payloadtag="IPIO88"):
		helpString = "Please configure the IP adress of the IPIO88 device you want to control.\n\n"
                helpString = helpString + "Format is xxx.xxx.xxx.xxx e.g: 192.168.100.1"

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		ipEdit=panel.TextCtrl(ip)
		refreshEdit=panel.TextCtrl(refresh)
                retryEdit=panel.TextCtrl(retry)
                payloadtagEdit=panel.TextCtrl(payloadtag)
	
                panel.AddLine(helpLabel)
		panel.AddLine("Device IP adress: ",ipEdit)
		panel.AddLine("Refresh Cycle Time (seconds): ",refreshEdit)
                panel.AddLine("Number of retry cycles before disconnect event is fired: ",retryEdit)
                panel.AddLine("Payload-tag to identify the device in event payload: ", payloadtagEdit)


                while panel.Affirmed():
			panel.SetResult(ipEdit.GetValue(), refreshEdit.GetValue(), retryEdit.GetValue(), payloadtagEdit.GetValue())

class GetAllInputStates(eg.ActionBase):
    name="Get all Input States"

    def __call__(self):

         for r in range(9):
             InStatus[r] = "?"

class GetInputState(eg.ActionBase):
    name="Get single Input State "

    def __call__(self, InChannel):

        try:
            InChannelRead = int(InChannel)
        
        except ValueError:
            eg.Print (payloadtag +": Invalid Channel Number!")
            
        else:

            if 1 <= InChannelRead <= 8:
                eg.Print (payloadtag +":Reading Input channel " + str(InChannelRead) +" ...")
            
                InStatus[int(InChannelRead)] = "?"
            
            else:
                eg.PrintNotice (payloadtag +": Invalid Channel Number!")


    def Configure(self, InChannel="1"):
		helpString = "Please configure Input Channel you want to read.\n\n"
                helpString = helpString + "Channel number between 1 and 8 is allowed."

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		inStringEdit=panel.TextCtrl(InChannel)
                panel.AddLine(helpLabel)
		panel.AddLine("Channel number: ",inStringEdit)
               
                while panel.Affirmed():
			panel.SetResult(inStringEdit.GetValue())

class GetAllOutputStates(eg.ActionBase):
    name="Get all Output States"

    def __call__(self):

         for r in range(9):
             OutStatus[r] = "?"


class GetOutputState(eg.ActionBase):
    name="Get single Output State "

    def __call__(self, OutChannel):

        try:
            OutChannelRead = int(OutChannel)
        
        except ValueError:
            eg.PrintNotice (payloadtag +": Invalid Channel Number!")
            
        else:

            if 1 <= OutChannelRead <= 8:
                eg.Print (payloadtag +": Reading Output channel Nr: " + str(OutChannelRead) +" ...")
            
                OutStatus[OutChannelRead] = "?"
            
            else:
                eg.PrintNotice (payloadtag +": Invalid Channel Number!")


    def Configure(self, OutChannel="1"):
		helpString = "Please configure Output channel you want to read.\n\n"
                helpString = helpString + "Channel number between 1 and 8 is allowed."

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		inStringEdit=panel.TextCtrl(OutChannel)
		panel.AddLine(helpLabel)
		panel.AddLine("Channel number: ",inStringEdit)

                while panel.Affirmed():
			panel.SetResult(inStringEdit.GetValue())


class SwitchAllOutputStatesOn(eg.ActionBase):
    name="Switch all Output States ON"

    def __call__(self):

            command = "http://"+IP+"/ipio.cgi?pg=main&out1=on&out2=on&out3=on&out4=on&out5=on&out6=on&out7=on&out8=on&end=main"
            urllib.urlopen(command)

class SwitchAllOutputStatesOff(eg.ActionBase):
    name="Switch all Output States OFF"

    def __call__(self):
            
            command = "http://"+IP+"/ipio.cgi?pg=main&out1=off&out2=off&out3=off&out4=off&out5=off&out6=off&out7=off&out8=off&end=main"
            urllib.urlopen(command)

class SwitchOutputStateOn(eg.ActionBase):
    name="Switch single Output State ON "

    def __call__(self, OutChannel):

        try:
            OutChannelRead = int(OutChannel)
        
        except ValueError:
            eg.Print (payloadtag +": Invalid Channel Number!")
            
        else:

            if 1 <= OutChannelRead <= 8:
                eg.Print (payloadtag +": Switch Output channel " + str(OutChannelRead) +" On ...")
            
                command = "http://"+IP+"/ipio.cgi?pg=main"
                
                for r in range(1,9):

                    if r == OutChannelRead:                           
                        command=command+"&out"+str(r)+"=on"
                    else:
                        command=command+"&out"+str(r)+"="+OutStatus[r]
                            
                command = command+"&end=main"
                
                urllib.urlopen(command)
            
            else:
                eg.PrintNotice (payloadtag +": Invalid Channel Number!")

    def Configure(self, OutChannel="1"):
		helpString = "Please configure Output channel you want to switch ON.\n\n"
                helpString = helpString + "Channel number between 1 and 8 is allowed."

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		inStringEdit=panel.TextCtrl(OutChannel)
		panel.AddLine(helpLabel)
		panel.AddLine("Channel number: ",inStringEdit)

                while panel.Affirmed():
			panel.SetResult(inStringEdit.GetValue())

class SwitchOutputStateOff(eg.ActionBase):
    name="Switch single Output State OFF"

    def __call__(self, OutChannel):

        try:
            OutChannelRead = int(OutChannel)
        
        except ValueError:
            eg.PrintNotice (payloadtag +": Invalid Channel Number!")
            
        else:

            if 1 <= OutChannelRead <= 8:
                eg.Print (payloadtag +": Switch Output channel " + str(OutChannelRead) +" OFF ...")
            
                command = "http://"+IP+"/ipio.cgi?pg=main"
                
                for r in range(1,9):

                    if r == OutChannelRead:                           
                        command=command+"&out"+str(r)+"=off"
                    else:
                        command=command+"&out"+str(r)+"="+OutStatus[r]
                            
                command = command+"&end=main"
                
                urllib.urlopen(command)
            
            else:
                eg.PrintNotice (payloadtag +": Invalid Channel Number!")

    def Configure(self, OutChannel="1"):
		helpString = "Please configure Output channel you want to switch OFF.\n\n"
                helpString = helpString + "Channel number between 1 and 8 is allowed."

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		inStringEdit=panel.TextCtrl(OutChannel)
		panel.AddLine(helpLabel)
		panel.AddLine("Channel number: ",inStringEdit)

                while panel.Affirmed():
			panel.SetResult(inStringEdit.GetValue())


class DeleteAllinternalIOBindings(eg.ActionBase):
    name="Delete all internal device I/O bindings"

    def __call__(self):

        command="http://"+IP+"/ioports.cgi?pg=io&1A=fallende+Flanke&1P=0&2A=positive+Logik&2P=0&3A=positive+Logik&3P=0&4A=positive+Logik&4P=0&5A=positive+Logik&5P=0&6A=positive+Logik&6P=0&7A=positive+Logik&7P=0&8A=positive+Logik&8P=0&set=%DCbernehmen&end=io"
        urllib.urlopen(command)

        eg.Print (payloadtag +": Bindings deleted")

    def Configure(self):
		helpString = "WARNING! Excecution of this action deletes all internal\n"
                helpString = helpString + "In/Out bindings set in the device internally. \n\n"
                helpString = helpString + "This CAN NOT BE UNDONE!\n\n"
                helpString = helpString + "Useful if you want to control the In -> Out behavior with EG soley."

		panel = eg.ConfigPanel(self)
		helpLabel=panel.StaticText(helpString)
		
		panel.AddLine(helpLabel)
	
                while panel.Affirmed():
			panel.SetResult()

      	 	

	

