EventGhost/EventGhost

View on GitHub
plugins/RFXcom_xPL/__init__.py

Summary

Maintainability
F
4 days
Test Coverage
#
# This file is a plugin for EventGhost.
# plugins/RFXcom_xPL/__init__.py
#
# Copyright (C) 2009
# Walter Kraembring
#
##############################################################################
#
# Acknowledgements: xPL code and main idea is based on doghouse xPL plugin
#
##############################################################################
# Revision history:
#
# 2011-12-16  Fixed to work with -translate switch
# 2009-12-28  HeartBeat monitoring thread and restart function introduced
# 2009-12-19  0.4.0 compatible GUID added
# 2009-12-02  Walter Kraembring: First version
##############################################################################

eg.RegisterPlugin(
    name = "RFXcom_xPL",
    guid = '{74895CCC-CCB4-4E23-B0AA-DC98385972BF}',
    author = "Walter Kraembring",
    version = "0.0.3",
    canMultiLoad = False,
    kind = "external",
    description = (
        "Sends and receives RFXCOM messages using "
        "xPLRFX from xPL Monkey."
        '<br\n><br\n>'
        '<center><img src="xPL_plugin.png" /></center>'
    ),
    url = "http://www.eventghost.net/forum/viewtopic.php?f=9&t=2077",
)

import os, time, string, select, re
from socket import *
from threading import Event, Thread



class Text:
    xplDeviceName = "Enter the xpl-name of the RFXCOM device "
    portNumber = "Select the lowest port number to use "
    logToFile = "Log events to file: "
    debug = "Show all raw xPL messages: "

    class sendRFXcom_x10_basic:
        textBoxName = "Enter a descriptive name for the action"
        textBoxSchema = "Select the xPL schema to be used"
        textBoxProtocol = "Select the device protocol to be used"
        textBoxHouseCode = "Select the house code of the device"
        textBoxDeviceCode = "Select the device code of the device"
        textBoxCommand = "Select the commmand to send"
        textBoxLevel = "Select the dim/bright level"

    class sendRFXcom_homeeasy_basic:
        textBoxName = "Enter a descriptive name for the action"
        textBoxSchema = "Select the xPL schema to be used"
        textBoxAddress = "Type/paste the unit address to be used (0x....)"
        textBoxDeviceUnit = "Select the unit code of the device"
        textBoxCommand = "Select the commmand to send"
        textBoxLevel = "Select the dim/bright level"



class RFXcom(eg.PluginClass):
    text = Text
    # Define initial value for heartbeat monitoring
    xplsourceHB = 10


    def __init__(self):
        self.LocalIP=gethostbyname(gethostname())
        self.hostname="RFXcom."+str(gethostname())
        self.AddAction(sendRFXcom_x10_basic)
        self.AddAction(sendRFXcom_homeeasy_basic)
        #self.AddAction(Restart)


    def __start__(self, xplDeviceName, portNbr, bLogToFile, bDebug):
        self.UDPSock = socket(AF_INET, SOCK_DGRAM)
        # Initialise the socket
        self.xplDeviceName = xplDeviceName
        self.port = portNbr
        self.bLogToFile = bLogToFile
        self.bDebug = bDebug

        bound = 0
        while bound == 0 :
            bound = 1
            try :
                addr = ('0.0.0.0', self.port)
                self.UDPSock.bind(addr)
            except :
                bound = 0
                self.port += 1

        print "RFXcom_xPL plugin, bound to port " + str(self.port)
        print "RFXcom is started"

        # start the heartbeat thread
        self.hbThreadEvent = Event()
        hbThread = Thread(
            target=self.SendHeartbeat,
            args=(self.hbThreadEvent,)
        )
        hbThread.start()

        # start the heartbeat monitoring thread
        self.hbThreadMonitor = Event()
        hbMonitor = Thread(
            target=self.MonitorHeartbeat,
            args=(self.hbThreadMonitor,)
        )
        hbMonitor.start()

        # start the main thread that scans for incoming RFXcom xPL msgs
        self.mainThreadEvent = Event()
        mainThread = Thread(
            target=self.main,
            args=(self.mainThreadEvent,)
        )
        mainThread.start()


    def __stop__(self):
        self.hbThreadMonitor.set()
        self.hbThreadEvent.set()
        self.mainThreadEvent.set()
        hbSock = socket(AF_INET, SOCK_DGRAM)
        hbSock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        msg = (
            "xpl-stat\n{\nhop=1\nsource="
            +str(self.hostname)
            +"\ntarget=*\n}\nhbeat.end\n{\ninterval=5\nport="
        )
        msg = (
            msg
            +str(self.port)
            +"\nremote-ip="
            +str(self.LocalIP)
            +"\nversion=1.2\n}\n"
        )
        hbSock.sendto(msg,("255.255.255.255", 3865))
        hbSock.sendto(msg,("255.255.255.255", self.port))
        self.UDPSock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        time.sleep(2.0)


    def __close__(self):
        print "RFXcom_xPL is closed."


    def Restart(self):
        self.__stop__()
        self.__start__(self.xplDeviceName, self.port, self.bLogToFile, self.bDebug)


    # Sub routine for sending a heartbeat
    def SendHeartbeat(self, hbThreadEvent) :
        hbSock = socket(AF_INET, SOCK_DGRAM)
        hbSock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        while not hbThreadEvent.isSet():
            msg = (
                "xpl-stat\n{\nhop=1\nsource="
                +str(self.hostname)
                +"\ntarget=*\n}\nhbeat.app\n{\ninterval=5\nport="
            )
            msg = (
                msg
                +str(self.port)
                +"\nremote-ip="
                +str(self.LocalIP)
                +"\nversion=1.2\n}\n"
            )
            hbSock.sendto(msg,("255.255.255.255", 3865))
            hbThreadEvent.wait(5*60.0)


    # Sub routine for monitoring heartbeats
    def MonitorHeartbeat(self, hbThreadMonitor) :
        while not hbThreadMonitor.isSet():
            hbThreadMonitor.wait(60.0)
            #print self.xplsourceHB
            if self.xplsourceHB > 0:
                self.xplsourceHB -= 1
            else:
                self.xplsourceHB = 10
                self.Restart()


    # Main Loop
    def main(self,mainThreadEvent):
        messageOld = ""
        while not mainThreadEvent.isSet():
            readable, writeable, errored = select.select([self.UDPSock],[],[],60)
            if len(readable) == 1 :
                data,addr = self.UDPSock.recvfrom(1500)
                message = ""
                message = str(data)
                message = message.splitlines()
                xpltype = message[0]
                msgheader = message[2:5]
                xplsource = message[3].rsplit("=")[1]
                xpltarget = message[4].rsplit("=")[1]
                msgschema = message[6]
                xplschema = msgschema.rsplit(".")
                msgbody = message[8:-1]
                msgbody2 = ""

                if self.bDebug:
                    print message

                if msgschema <> "hbeat.app" :
                    if xplsource == self.xplDeviceName:
                        if message <> messageOld:
                            for element in msgbody:
                                msgbody2 = msgbody2 + element + ","
                            self.TriggerEvent(
                                xpltype
                                +":"
                                +msgschema
                                +":"
                                +xplsource
                                +":"
                                +xpltarget
                                +":"
                                +msgbody2
                            )
                            messageOld = message

                            if self.xplsourceHB <10:
                                self.xplsourceHB += 1

                            if self.bLogToFile:
                                logStr = (
                                    str(xpltype)
                                    +"|"
                                    +str(msgschema)
                                    +"|"
                                    +str(xplsource)
                                    +"|"
                                    +str(xpltarget)
                                    +"|"
                                    +str(msgbody2)
                                )
                                self.LogToFile(logStr)

                if msgschema == "hbeat.app" :
                    if xplsource == self.xplDeviceName:
                        #print "This is the heartbeat from: ", xplsource
                        self.xplsourceHB += int(message[8].rsplit("=")[1])
                        if self.xplsourceHB > 10:
                            self.xplsourceHB = 10


    def LogToFile(self, s):
        timeStamp = str(
            time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        )
        logStr = timeStamp+"\t"+s+"<br\n>"
        fileHandle = None

        if eg.WindowsVersion >= 'Vista':
            progData = os.environ['ALLUSERSPROFILE']
            if (
                not os.path.exists(progData+"/EventGhost/Log")
                and not os.path.isdir(progData+"/EventGhost/Log")
            ):
                os.makedirs(progData+"/EventGhost/Log")
            fileHandle = open (
                progData+'/EventGhost/Log/RFXCOM_'+self.name+'.html', 'a'
            )
            fileHandle.write ( logStr )
            fileHandle.close ()
        else:
            if not os.path.exists('Log') and not os.path.isdir('Log'):
                os.mkdir('Log')
            fileHandle = open ( 'Log/RFXCOM_'+self.name+'.html', 'a' )
            fileHandle.write ( logStr )
            fileHandle.close ()


    def Configure(
        self,
        xplDeviceName = "mal-rfxcom.",
        portNbr = 50005,
        bLogToFile = False,
        bDebug = False,
        *args
    ):
        panel = eg.ConfigPanel(self, resizable=True)
        mySizer = wx.GridBagSizer(5, 5)

        xplDeviceNameCtrl = wx.TextCtrl(panel, -1, xplDeviceName)
        xplDeviceNameCtrl.SetInitialSize((250,-1))
        mySizer.Add(wx.StaticText(panel, -1, self.text.xplDeviceName), (1,0))
        mySizer.Add(xplDeviceNameCtrl, (1,1))

        portCtrl = panel.SpinIntCtrl(portNbr, 50000, 50050)
        portCtrl.SetInitialSize((75,-1))
        mySizer.Add(wx.StaticText(panel, -1, self.text.portNumber), (2,0))
        mySizer.Add(portCtrl, (2,1))

        bLogToFileCtrl = wx.CheckBox(panel, -1, "")
        bLogToFileCtrl.SetValue(bLogToFile)
        mySizer.Add(wx.StaticText(panel, -1, self.text.logToFile), (5,0))
        mySizer.Add(bLogToFileCtrl, (5,1))

        bDebugCtrl = wx.CheckBox(panel, -1, "")
        bDebugCtrl.SetValue(bDebug)
        mySizer.Add(wx.StaticText(panel, -1, self.text.debug), (6,0))
        mySizer.Add(bDebugCtrl, (6,1))

        panel.sizer.Add(mySizer, 1, flag = wx.EXPAND)

        while panel.Affirmed():
            xplDeviceName = xplDeviceNameCtrl.GetValue()
            portNbr = portCtrl.GetValue()
            bLogToFile = bLogToFileCtrl.GetValue()
            bDebug = bDebugCtrl.GetValue()

            panel.SetResult(
                        xplDeviceName,
                        portNbr,
                        bLogToFile,
                        bDebug,
                        *args
            )



class sendRFXcom_x10_basic(eg.ActionClass):
    name = "RFXCOM x10 basic"
    description = "Action to send RFXCOM messages using xPLRFX"
    iconFile = 'xPL'


    def __call__(self, name, schema, protocol, house, pdevice, command):
        xPLTarget = self.plugin.xplDeviceName

        if command == "on" or command == "off":
            xPLMsg = (
                "command="
                +command
                +"\n"
                +"device="
                +house
                +pdevice
                +"\n"
                +"protocol="
                +protocol
            )

        msg = (
            "xpl-cmnd"
            +"\n{\nhop=1\nsource="
            +self.plugin.hostname
            +"\ntarget="
            +xPLTarget
            +"\n}\n"
            +str(schema)
            +"\n{\n"
            +xPLMsg
            +"\n}\n"
        )
        addr = ("255.255.255.255",3865)
        self.plugin.UDPSock.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
        self.plugin.UDPSock.sendto(msg,addr)
        time.sleep(0.1)

#        for i in range(0, 1):
#            self.plugin.UDPSock.sendto(msg,addr)
#            time.sleep(1.0)


    # Get the choice from dropdown and perform some action
    def OnChoice(self, event):
        choice = event.GetSelection()
        event.Skip()
        return choice


    def Configure(
        self,
        name="Give me a name",
        schema="",
        protocol="",
        house="",
        pdevice="",
        command=""
        ):

        text = self.text
        panel = eg.ConfigPanel(self)
        plugin = self.plugin

        # Create a textfield for action name
        nameCtrl = wx.TextCtrl(panel, -1, name)

        staticBox = wx.StaticBox(panel, -1, text.textBoxName)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer0 = wx.BoxSizer(wx.HORIZONTAL)
        sizer0.Add(nameCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer0, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for schema
        schemaCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = ['x10.basic']
        schemaCtrl.AppendItems(strings=list)
        if list.count(schema)==0:
            schemaCtrl.Select(n=0)
        else:
            schemaCtrl.SetSelection(int(list.index(schema)))
        schemaCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        staticBox = wx.StaticBox(panel, -1, text.textBoxSchema)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(schemaCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer2, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for protocol
        protocolCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            'Nexa',
            'Intertechno',
            'Proove',
            'HomeEasy'
        ]
        protocolCtrl.AppendItems(strings=list)
        if list.count(protocol)==0:
            protocolCtrl.Select(n=0)
        else:
            protocolCtrl.SetSelection(int(list.index(protocol)))
        protocolCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        staticBox = wx.StaticBox(panel, -1, text.textBoxProtocol)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(protocolCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer2, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for house code
        houseCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
        ]
        houseCtrl.AppendItems(strings=list)
        if list.count(house)==0:
            houseCtrl.Select(n=0)
        else:
            houseCtrl.SetSelection(int(list.index(house)))
        houseCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        staticBox = wx.StaticBox(panel, -1, text.textBoxHouseCode)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer3.Add(houseCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer3, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for device code
        deviceCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            '1', '2', '3', '4', '5', '6', '7', '8', '9',
            '10', '11', '12', '13', '14', '15', '16'
        ]
        deviceCtrl.AppendItems(strings=list)
        if list.count(pdevice)==0:
            deviceCtrl.Select(n=0)
        else:
            deviceCtrl.SetSelection(int(list.index(pdevice)))

        staticBox = wx.StaticBox(panel, -1, text.textBoxDeviceCode)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer3.Add(deviceCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer3, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)
        deviceCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        # Create a dropdown for command
        commandCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            'on',
            'off'
        ]
        commandCtrl.AppendItems(strings=list)
        if list.count(command)==0:
            commandCtrl.Select(n=0)
        else:
            commandCtrl.SetSelection(int(list.index(command)))

        staticBox = wx.StaticBox(panel, -1, text.textBoxCommand)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer5 = wx.BoxSizer(wx.HORIZONTAL)
        sizer5.Add(commandCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer5, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)
        commandCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        while panel.Affirmed():
            panel.SetResult(
                nameCtrl.GetValue(),
                schemaCtrl.GetStringSelection(),
                protocolCtrl.GetStringSelection(),
                houseCtrl.GetStringSelection(),
                deviceCtrl.GetStringSelection(),
                commandCtrl.GetStringSelection()
            )



class sendRFXcom_homeeasy_basic(eg.ActionClass):
    name = "RFXCOM HOME easy basic"
    description = "Action to send RFXCOM messages using xPLRFX"
    iconFile = 'xPL'


    def __call__(self, name, schema, address, unit, command, level):
        xPLTarget = self.plugin.xplDeviceName

        if command == "on" or command == "off":
            xPLMsg = (
                "command="
                +command
                +"\n"
                +"address="
                +address
                +"\n"
                +"unit="
                +unit
            )

        if command == "preset":
            xPLMsg = (
                "command="
                +command
                +"\n"
                +"level="
                +level
                +"\n"
                +"address="
                +address
                +"\n"
                +"unit="
                +unit
            )

        msg = (
            "xpl-cmnd"
            +"\n{\nhop=1\nsource="
            +self.plugin.hostname
            +"\ntarget="
            +xPLTarget
            +"\n}\n"
            +str(schema)
            +"\n{\n"
            +xPLMsg
            +"\n}\n"
        )
        addr = ("255.255.255.255",3865)
        self.plugin.UDPSock.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
        self.plugin.UDPSock.sendto(msg,addr)
        time.sleep(0.1)

#        for i in range(0, 3):
#            self.plugin.UDPSock.sendto(msg,addr)
#            time.sleep(0.5)

    # Get the choice from dropdown and perform some action
    def OnChoice(self, event):
        choice = event.GetSelection()
        event.Skip()
        return choice

    def Configure(
        self,
        name="Give me a name",
        schema="",
        address="0x...",
        unit="",
        command="",
        level=""
        ):

        text = self.text
        panel = eg.ConfigPanel(self)
        plugin = self.plugin

        # Create a textfield for action name
        nameCtrl = wx.TextCtrl(panel, -1, name)

        staticBox = wx.StaticBox(panel, -1, text.textBoxName)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer0 = wx.BoxSizer(wx.HORIZONTAL)
        sizer0.Add(nameCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer0, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for schema
        schemaCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = ['homeeasy.basic']
        schemaCtrl.AppendItems(strings=list)
        if list.count(schema)==0:
            schemaCtrl.Select(n=0)
        else:
            schemaCtrl.SetSelection(int(list.index(schema)))
        schemaCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        staticBox = wx.StaticBox(panel, -1, text.textBoxSchema)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(schemaCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer1, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a textfield for address
        addressCtrl = wx.TextCtrl(panel, -1, address)

        staticBox = wx.StaticBox(panel, -1, text.textBoxAddress)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(addressCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer2, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)

        # Create a dropdown for device unit
        deviceCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            '0', '1', '2', '3', '4', '5', '6', '7', '8',
            '9', '10', '11', '12', '13', '14', '15', 'group'
        ]
        deviceCtrl.AppendItems(strings=list)
        if list.count(unit)==0:
            deviceCtrl.Select(n=0)
        else:
            deviceCtrl.SetSelection(int(list.index(unit)))

        staticBox = wx.StaticBox(panel, -1, text.textBoxDeviceUnit)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer3.Add(deviceCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer3, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)
        deviceCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        # Create a dropdown for command
        commandCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = ['on', 'off', 'preset']
        commandCtrl.AppendItems(strings=list)
        if list.count(command)==0:
            commandCtrl.Select(n=0)
        else:
            commandCtrl.SetSelection(int(list.index(command)))

        staticBox = wx.StaticBox(panel, -1, text.textBoxCommand)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer4 = wx.BoxSizer(wx.HORIZONTAL)
        sizer4.Add(commandCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer4, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)
        commandCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        # Create a dropdown for dim level
        levelCtrl = wx.Choice(parent=panel, pos=(10,10))
        list = [
            '0', '1', '2', '3', '4', '5', '6', '7', '8',
            '9', '10', '11', '12', '13', '14', '15'
        ]
        levelCtrl.AppendItems(strings=list)
        if list.count(level)==0:
            levelCtrl.Select(n=0)
        else:
            levelCtrl.SetSelection(int(list.index(level)))

        staticBox = wx.StaticBox(panel, -1, text.textBoxLevel)
        staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
        sizer5 = wx.BoxSizer(wx.HORIZONTAL)
        sizer5.Add(levelCtrl, 1, wx.EXPAND)
        staticBoxSizer.Add(sizer5, 0, wx.EXPAND|wx.ALL, 5)
        panel.sizer.Add(staticBoxSizer, 0, wx.EXPAND)
        levelCtrl.Bind(wx.EVT_CHOICE, self.OnChoice)

        while panel.Affirmed():
            panel.SetResult(
                nameCtrl.GetValue(),
                schemaCtrl.GetStringSelection(),
                addressCtrl.GetValue(),
                deviceCtrl.GetStringSelection(),
                commandCtrl.GetStringSelection(),
                levelCtrl.GetStringSelection()
            )



class Restart(eg.ActionClass):

    def __call__(self):
        self.plugin.xplsourceHB = 0