I have a plugin I'd like to contribute, please help!

Do you have questions about writing plugins or scripts in Python? Meet the coders here.
Post Reply
etc6849
Posts: 21
Joined: Fri Dec 28, 2012 5:12 pm

I have a plugin I'd like to contribute, please help!

Post by etc6849 » Thu Jan 17, 2013 4:37 am

I found that the svn is discussed here: http://sourceforge.net/projects/eventgh ... rce=navbar

I see that the most recent version of EventGhost is here:
http://www.eventghost.net/downloads/

Can anyone check out and add to the SVN? Will the changes automatically be included in the next build?

Here's my finalized code:

Code: Select all

# -*- coding: utf-8 -*-
#
# plugins/Premise/__init__.py
# 
# This file is a plugin for EventGhost.
# Copyright (C) 2005-2009 Lars-Peter Voss <bitmonster@eventghost.org>
#
# EventGhost is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation;
#
# 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 this program. If not, see <http://www.gnu.org/licenses/>.

eg.RegisterPlugin(
    name = "Premise",
    author = "etc6849", 
    version = "1.0",
    guid = "{85f83859-9bb5-4112-9be6-04c5ff5e6ce8}",
    canMultiLoad = False,
    description = (
        "Send and receive events or actions to and from a Premise Home Control"
        "Server.  Download Premise FREE at "
        "http://www.cocoontech.com/wiki/Premise"
    ),
    url = "http://cocoontech.com/forums/files/file/196-premiseeventghost/",
)

import eg
import select
import socket
import threading


# TX/RX actions and events to a Premise (SYS) server acting as the client.
# EventGhost plugin will act as the server.
# Note 1: SYS server refers to the Premise Home Control server.
# This code uses the following prefixes for variants (for ease of read):
# o = object (unless it's a function), ol = object list, s = string var, 
# i = integer var, b = boolean, f = floating pt var, t = tuple
class Premise(eg.PluginBase):

    def __init__(self, qSend = None):
        self.AddAction(SetValue)
        self.AddAction(SendEvent)
        print "Premise plugin: initialized." 

        
    def __start__(self, sPort):
        print "Premise plugin: started."
        self.oSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        # if connection issue, we do not leave socket in a timewait state
        self.oSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.iPort = int(sPort)
        self.sData = ""
        self.bError = False
        self.bStopping = False
        
        try:
            # bind the socket object to the address
            # use socket.gethostname method since we want socket to be seen by
            # outside world (e.g. Premise Home Control)
            self.oSocket.bind((socket.gethostname(), self.iPort))
        except:
            self.bError = True
            print "Premise plugin: SOCKET ERROR! Check port: ",str(self.iPort)
        
        # if we don't get a socket error, proceed
        if not self.bError:
            # listen for connection from SYS server (that acts as the client).
            self.oSocket.listen(5)
            print "Premise plugin is bound to port " + str(self.iPort) + "."
            self.oSocketInput = [self.oSocket]
            self.oSocketOutput = []
            
            # create a main thread event, main thread and then start
            self.oMainThreadEvent = threading.Event()
            oMainThread = threading.Thread(target=self.Main, 
                              args=(
                                  self.oMainThreadEvent,
                              )
                          )
            oMainThread.start()

            
    def __stop__(self):
        self.bStopping = True
        print "Premise plugin: stopped."
        print "Premise plugin: attempting to stop main thread..."
        try:
            self.oMainThreadEvent.set()
        except:
            print "Premise plugin: main thread was never started."

            
    # main loop
    def Main(self,oMainThreadEvent):
        print "Premise plugin: started main thread."
        # set client ready object to none
        self.oClientReady = None
        while not self.oMainThreadEvent.isSet():
            olClients_InputReady,olOutput, olInError =  select.select(
                                                            self.oSocketInput,
                                                            self.oSocketOutput,
                                                            [],
                                                            1
                                                        )
                  
            for oClientReady in olClients_InputReady:
                if oClientReady == self.oSocket:
                    self.oClientReady, tAddress = self.oSocket.accept()
                    print "Premise plugin: client added: ", self.oClientReady,\
                        tAddress
                    
                    # SYS server need to know it's connected to EventGhost
                    self.TriggerEvent("Connected")
                    
                    # if client not already in input list, add it
                    if not self.oSocketInput.count(self.oClientReady)>0:
                        self.oSocketInput.append(self.oClientReady)
                else:
                    
                    # receive upto 4096 bytes from the client that's ready
                    sData = oClientReady.recv(4096)
                    
                    # if data is received do something with it
                    if sData:
                        # get rid of any line terminators from the packet
                        sData = self.StripNoPrint(sData)
                        print sData
                        # process <ACTION> packet received from the SYS server
                        if "<ACTION>" in sData:
                            self.ProcessCmd_Action(sData)

                        # process <EVENT> packet received from the SYS server
                        if "<EVENT>" in sData:
                            self.ProcessCmd_Event(sData)
                        
                        # echo back data to all connected clients
                        self.SendDataToAllClients(sData)
                            
                    else:
                        if self.oSocketInput.count(oClientReady) > 0:
                            self.oMainThreadEvent.set()
                            
        # while there are connected clients, remove them all
        try:
            while self.oSocketInput.count(oClientReady) > 0:
                self.oSocketInput.remove(oClientReady)
                print "Premise plugin: Client removed: ", oClientReady
        except:
            print "Premise plugin: there were no clients to disconnect."
        
        # keep socket open so Premise can reconnect unless stopping plugin
        if not self.bStopping:
            self.__start__(self.iPort)
        else:
            try:
                self.oSocket.close()
                print "Premise plugin: the socket is now closed."
            except:
                print "Premise plugin: ERROR closing socket!"
            
        print "Main thread has ended."
        
        
    # define Configure panel    
    def Configure(self, sPort="1024"):
        oPanel = eg.ConfigPanel()
        sPortControl = oPanel.TextCtrl(sPort)
        oPanel.AddLine("EventGhost will serve the port below so that Premise"
                        " can connect as a client.")
        oPanel.AddLine("Port: ", sPortControl)
        while oPanel.Affirmed():
            oPanel.SetResult(sPortControl.GetValue())
                     
                     
    def StripNoPrint(self, str):
        sResults = ""
        # iterate through and remove all non-printable ascii characters
        for char in str:
            if ord(char) > 31 and ord(char) < 127:
                sResults += char
        return sResults
        
        
    # method to handle sending data.  sole entry point for accessing send()
    def SendDataToAllClients(self, sCommand):
        try:
            olClientConnections = self.oSocketInput[1:len(self.oSocketInput)]
            for oClientConnection in olClientConnections:
                oClientConnection.send(sCommand + "\r")
                print "Premise: sending " + sCommand    
        except:
            print "Premise plugin: connection ERROR while sending: ", sCommand
        return ""
        
        
    def ProcessCmd_Action(self, sCommand):
        # replace the first occurance of the tag with nothing
        sCommand = sCommand.replace("<ACTION>", "", 1)
        print "Premise: processing action " + sCommand
        
        # invoke an action based on received python code from the SYS server
        eg.plugins.EventGhost.PythonCommand(sCommand)
        return ""

        
    def ProcessCmd_Event(self, sCommand):
        # replace the first occurance of the tag with nothing
        sCommand = sCommand.replace("<EVENT>", "", 1)
        
        # remove all "<" from array
        sCommand = sCommand.replace("<", "")
        
        # split the packet into a list
        sCommand = sCommand.split(">")
        
        # trigger event in EG, process payload if it's present
        if sCommand[1] == "":
            self.TriggerEvent(sCommand[0])
        else:
            self.TriggerEvent(sCommand[0], payload = sCommand[1])
        return ""
        
        
    # send the event string and event payload (as a string) to the SYS server
    def SendCmd_Event(self, sEventString, sEventPaload):
        sCommand = "<EVENT>" + "<" + sEventString + "><" + sEventPaload + ">"
    
        # send EVENT packet to all connected SYS servers
        self.SendDataToAllClients(sCommand)

        
    # send setvalue request to SYS server
    def SendCmd_SetValue(self, sObjPath, sPropName, sPropValue, bForceState):
        sCommand = "<SETVAL>"
        if bForceState:
            sCommand = "<SETVALFORCE>"
        sCommand += "<" + sObjPath + "><" + sPropName + "><" + sPropValue + ">"
    
        # send SetValue packet to all connected SYS servers
        self.SendDataToAllClients(sCommand)
        return ""
 
            
            
# invokes the SetValue and SetValueForce methods within SYS 
class SetValue(eg.ActionClass):	
    description = (
        "Calls the SetValue method to change a device state on the connected "
        "Premise server using the supplied parameters."
    )
    
    def __call__(self, sObjPath, sPropName, sPropValue, bForceStateChange):
        self.plugin.SendCmd_SetValue(
            sObjPath, 
            sPropName, 
            sPropValue, 
            bForceStateChange
        )
     
     
    # build the configuration panel    
    def Configure(
        self, 
        sObjPath="Home.Living.Light",
        sPropName="PowerState",
        sPropValue="True",
        bForceStateChange=0
    ):
        panel = eg.ConfigPanel()

        sObjPathControl = panel.TextCtrl(sObjPath)
        sPropNameControl = panel.TextCtrl(sPropName)
        sPropValueControl = panel.TextCtrl(sPropValue)
        bForceStateChangeControl = panel.CheckBox(bForceStateChange)
        
        panel.AddLine(
            "Use options below to define the desired Premise server action.  "
            "Values are not case sensitive."
        )
        panel.AddLine("SYS Object Path: ", sObjPathControl)
        panel.AddLine("Property Name: ", sPropNameControl)
        panel.AddLine("Property Value: ", sPropValueControl)
        panel.AddLine("Force State Change: ", bForceStateChangeControl)
        
        while panel.Affirmed():
            panel.SetResult(
                sObjPathControl.GetValue(),
                sPropNameControl.GetValue(),
                sPropValueControl.GetValue(),
                bForceStateChangeControl.GetValue()
            )
            
           
           
# sends the string and payload of an EventGhost event to the SYS server. 
class SendEvent(eg.ActionClass):
    description = (
        "Sends to the Premise server the EventGhost event "
        "string and payload (as a string) that initiated the action.  "
        "To send all events: place this action under a macro folder along with "
        "an event named *"
    )
    
    
    def __call__(self):
        sEventString = str(eg.event.string)
        sEventPayload = str(eg.event.payload)
        self.plugin.SendCmd_Event(sEventString, sEventPayload)
FREE Premise home automation software: http://www.cocoontech.com/wiki/Premise
Download: http://www.mediapcforums.com/node/7
Open-source VRC0P (Z-Wave) driver:
https://code.google.com/p/zwave-driver- ... %2FViziaRF

User avatar
Pako
Plugin Developer
Posts: 2294
Joined: Sat Nov 11, 2006 1:31 pm
Location: Czech Republic
Contact:

Re: I have a plugin I'd like to contribute, please help!

Post by Pako » Thu Jan 17, 2013 7:41 am

etc6849 wrote:Can anyone check out and add to the SVN? Will the changes automatically be included in the next build?
Unfortunately, no.
I recently wrote an answer to a similar question.
You can read it here.
If we did not do it this way, today would EventGhost included much more plugins. I think it would not be good.
We make no exceptions, even for such plugins, where we can assume that it will use a large number of users.

Hopefully we ever manage to find someone who can finish the Bitmonster's intent.

Pako

krambriw
Plugin Developer
Posts: 2570
Joined: Sat Jun 30, 2007 2:51 pm
Location: Stockholm, Sweden
Contact:

Re: I have a plugin I'd like to contribute, please help!

Post by krambriw » Thu Jan 17, 2013 12:05 pm

I suggest you just do like the rest of us, create a new thread and publish your plugin in the "Plugin" section, describe the purpose, installation etc and then maintain accordingly, always put new versions in the first post like there are several examples of.

Thanks for sharing, every new plugin is good news and helps hopefully several other grateful users!

Best regards, Walter

Post Reply