Seite 1 von 1

SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 15:51
von acidbath
Hallo allerseits.

Also absoluter Python-Anfänger wollte ich mich mal wieder mit der Sprache beschäftigen und habe einen SimpleXMLRPCServer aufgesetzt, welchen ich zur Kontaktaufnahme mit meiner Hausautomation 'benötige'.
Den Server richte ich mit folgenden Zeilen ein:

Code: Alles auswählen

IP = '192.168.27.2'
PORT = 5544
server_for_ccu = SimpleXMLRPCServer((IP, PORT), logRequests=False)
server_for_ccu.register_instance(MyFuncs())
server_for_ccu.register_multicall_functions()
...
server_for_ccu.serve_forever()
Die CCU von HomeMatic benötigt nach meinem Wissen mindestens 4 Funktionen, um mit einem XMLRPC-Server gescheit kommunizieren zu können, welche ich hiermit bereitstelle:

Code: Alles auswählen

class MyFuncs:
    def event(*args):
        print '---'
        if len(args) >= 5:
            dp, param, value = args[2:5]
            zeitpunkt = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
            print 'Zeitpunkt : %s' % zeitpunkt
            print 'Datenpunkt: %s' % dp
            print 'Parameter : %s' % param
            print 'Wert      : %s' % value
            
            logging.info('%s: %s, %s = %s', zeitpunkt, dp, param, value)
            write_to_db(db_connection, zeitpunkt, dp, param, value)
            if dp == 'JEQ0738820:1' and param == 'BRIGHTNESS':
                logging.info('Außenhelligkeit: %s', value)
                print 'Außenhelligkeit:', value
        return ''

    def listDevices(*args):
        #for arg in args:
        #    print arg
        return ''

    def newDevices(*args):
        #for arg in args:
        #    print arg
        return ''

    def newDevice(*args):
        #for arg in args:
        #    print arg
        return ''
Interessant ist für mich für den Moment nur das Bekanntgeben von events, sprich irgendwelche Ereignisse in der Wohnung.
Wie man sieht, bastel ich genau damit gerade ein wenig rum und das logging mit dem Python-Logging-Modul klappt auch einwandfrei - was man für meinen Versuch, das ganze auch in eine sqlite-Datenbank zu packen leider nicht sagen kann. :cry:

write_to_db ist eine Funktion, die ich außerhalb der Klasse MyFync definiert habe:

Code: Alles auswählen

def write_to_db(conn, date_time, data_point, parameter, value):
    with conn:
        cur = conn.cursor()
        data = (date_time, data_point, parameter, value)
        cur.execute('insert into events values (?,?,?,?)', data)
Rufe ich diese Funktion außerhalb der event-Funktion auf, klappt das Abspeichern und die Werte landen in der Datenbank. Innerhalb der event-Funktion wird zwar keine Fehlermeldung ausgegeben, aber neue Daten erscheinen trotzdem nicht.

Generell finde ich das ganze recht unschön gelöst, weil ich keine Möglichkeit sehe, der Funktion event in der Klasse MyFunc einen Parameter für den Zugriff auf die Datenbank mitzugeben - die Parameter kommen ja vom entfernten XLM-Server/Client.
Deshalb die Frage, wie man das ganze sauber umsetzen würde?
Letztlich plane ich natürlich, auf gewisse events gesondert zu reagieren, so daß diese nicht nur in der Datenbank landen, sondern eben auch eine direkte Reaktion des Systems bewirken. Wie verpacke ich sowas sauber? Käme hier Interprozesskommunikation ins Spiel, sprich bräuchte ich einen weiteren XML-Server, der dann wiederum vom obigen Programm die events zugeschickt bekommt? Optimal fände ich schon eine Zwei-Teilung, sprich Empfang der events und direktes Wegschreiben in eine Datenbank in einem Skript und dann ein weiteres (oder gar mehrere), die sich gewisse events rauspicken und dann entsprechend aktiv werden.

Tausenddank,
Daniel

Re: SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 16:12
von BlackJack
@acidbath: In der Datenbankfunktion fehlt ein `commit()` damit die Daten auch tatsächlich in der Datenbank festgeschrieben werden.

Bei den Methoden ist das mit den ``*args`` sehr unschön. Mindestens `self` sollte man davon isoliert explizit entgegennehmen. Das löst dann auch die Frage wie man ein „konstantes” Argument dort hinein bekommt, denn die Argumente kommen ja nicht alle von aussen — das erste Argument ist das Exemplar von `MyFuncs`. Da kannst Du zum Beispiel die Datenbankverbindung an das Objekt binden und dann darauf zugreifen.

Erwarten die Aufrufe denn tatsächlich überall eine variable Anzahl von Argumenten? Das wäre IMHO eine komische API.

Den Zeitpunkt kann das Logging-Rahmenwerk schon ausgeben, dass muss man nicht immer manuell machen.

Re: SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 16:35
von acidbath
BlackJack hat geschrieben:In der Datenbankfunktion fehlt ein `commit()` damit die Daten auch tatsächlich in der Datenbank festgeschrieben werden.
Oh, okay, ich hatte angenommen, daß dies durch das with-statement automatisch erfolgen würde.
BlackJack hat geschrieben:Bei den Methoden ist das mit den ``*args`` sehr unschön. Mindestens `self` sollte man davon isoliert explizit entgegennehmen. Das löst dann auch die Frage wie man ein „konstantes” Argument dort hinein bekommt, denn die Argumente kommen ja nicht alle von aussen — das erste Argument ist das Exemplar von `MyFuncs`. Da kannst Du zum Beispiel die Datenbankverbindung an das Objekt binden und dann darauf zugreifen.
Ich hatte die Klasse 1:1 aus einem Fund im Internet übernommen, aus Sorge meine CCU abzuschießen; die soll ziemlich zickig reagieren, wenn der angegeben XML-RPC-Server nicht oder nicht angemessen reagiert. :D
Danke, das werde ich gleich direkt mal ausprobieren!
BlackJack hat geschrieben:Erwarten die Aufrufe denn tatsächlich überall eine variable Anzahl von Argumenten? Das wäre IMHO eine komische API.
Gute Frage, nächste Frage. :lol:
Laut offizieller Doku nicht:

Code: Alles auswählen

void event(String interface_id, String address, String value_key, ValueType value)
Werde ich dann gleich auch direkt mal korrigieren.
BlackJack hat geschrieben:Den Zeitpunkt kann das Logging-Rahmenwerk schon ausgeben, dass muss man nicht immer manuell machen.
Stimmt, hatte ich ursprünglich auch drin, letztlich soll das logging aber rausfliegen, sobald das Schreiben in die DB klappt - und für die benötige ich dann das Datum und die Uhrzeit.

Nochmals vielen Dank für die unglaubliche schnelle Hilfe, dann ist der Sonntagabend ja gerettet. :D
Insofern wünsche ich einen schönen ebensolchen!

Gruß,
Daniel

Re: SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 20:00
von av_jui
acidbath hat geschrieben:Hallo allerseits.

Also absoluter Python-Anfänger wollte ich mich mal wieder mit der Sprache beschäftigen und habe einen SimpleXMLRPCServer aufgesetzt, welchen ich zur Kontaktaufnahme mit meiner Hausautomation 'benötige'.
Den Server richte ich mit folgenden Zeilen ein:

Code: Alles auswählen

IP = '192.168.27.2'
PORT = 5544
server_for_ccu = SimpleXMLRPCServer((IP, PORT), logRequests=False)
server_for_ccu.register_instance(MyFuncs())
server_for_ccu.register_multicall_functions()
...
server_for_ccu.serve_forever()
Die CCU von HomeMatic benötigt nach meinem Wissen mindestens 4 Funktionen, um mit einem XMLRPC-Server gescheit kommunizieren zu können, welche ich hiermit bereitstelle:

Code: Alles auswählen

class MyFuncs:
    def event(*args):
        print '---'
        if len(args) >= 5:
            dp, param, value = args[2:5]
            zeitpunkt = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
            print 'Zeitpunkt : %s' % zeitpunkt
            print 'Datenpunkt: %s' % dp
            print 'Parameter : %s' % param
            print 'Wert      : %s' % value
            
            logging.info('%s: %s, %s = %s', zeitpunkt, dp, param, value)
            write_to_db(db_connection, zeitpunkt, dp, param, value)
            if dp == 'JEQ0738820:1' and param == 'BRIGHTNESS':
                logging.info('Außenhelligkeit: %s', value)
                print 'Außenhelligkeit:', value
        return ''

    def listDevices(*args):
        #for arg in args:
        #    print arg
        return ''

    def newDevices(*args):
        #for arg in args:
        #    print arg
        return ''

    def newDevice(*args):
        #for arg in args:
        #    print arg
        return ''
Interessant ist für mich für den Moment nur das Bekanntgeben von events, sprich irgendwelche Ereignisse in der Wohnung.
Wie man sieht, bastel ich genau damit gerade ein wenig rum und das logging mit dem Python-Logging-Modul klappt auch einwandfrei - was man für meinen Versuch, das ganze auch in eine sqlite-Datenbank zu packen leider nicht sagen kann. :cry:

write_to_db ist eine Funktion, die ich außerhalb der Klasse MyFync definiert habe:

Code: Alles auswählen

def write_to_db(conn, date_time, data_point, parameter, value):
    with conn:
        cur = conn.cursor()
        data = (date_time, data_point, parameter, value)
        cur.execute('insert into events values (?,?,?,?)', data)
Rufe ich diese Funktion außerhalb der event-Funktion auf, klappt das Abspeichern und die Werte landen in der Datenbank. Innerhalb der event-Funktion wird zwar keine Fehlermeldung ausgegeben, aber neue Datenerscheinen trotzdem nicht.

Generell finde ich das ganze recht unschön gelöst, weil ich keine Möglichkeit sehe, der Funktion event in der Klasse MyFunc einen Parameter für den Zugriff auf die Datenbank mitzugeben - die Parameter kommen ja vom entfernten XLM-Server/Client.
Deshalb die Frage, wie man das ganze sauber umsetzen würde?
Letztlich plane ich natürlich, auf gewisse events gesondert zu reagieren, so daß diese nicht nur in der Datenbank landen, sondern eben auch eine direkte Reaktion des Systems bewirken. Wie verpacke ich sowas sauber? Käme hier Interprozesskommunikation ins Spiel, sprich bräuchte ich einen weiteren XML-Server, der dann wiederum vom obigen Programm die events zugeschickt bekommt? Optimal fände ich schon eine Zwei-Teilung, sprich Empfang der events und direktes Wegschreiben in eine Datenbank in einem Skript und dann ein weiteres (oder gar mehrere), die sich gewisse events rauspicken und dann entsprechend aktiv werden.

Tausenddank,
Daniel
Ich hab da was für dich https://github.com/avjui/Homepy ich könnte noch hilfe gebrauchen

Re: SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 21:45
von acidbath
av_jui hat geschrieben:Ich hab da was für dich https://github.com/avjui/Homepy ich könnte noch hilfe gebrauchen
Danke, schaue ich mir mal an. Aber wie gesagt: ich bin blutiger Anfänger, ob ich da wirklich eine Hilfe sein kann, wage ich schwer zu bezweifeln...

Gruß,
Daniel

Re: SimpleXMLRPCServer

Verfasst: Sonntag 12. Mai 2013, 22:30
von av_jui
acidbath hat geschrieben:
av_jui hat geschrieben:Ich hab da was für dich https://github.com/avjui/Homepy ich könnte noch hilfe gebrauchen
Danke, schaue ich mir mal an. Aber wie gesagt: ich bin blutiger Anfänger, ob ich da wirklich eine Hilfe sein kann, wage ich schwer zu bezweifeln...

Gruß,
Daniel
Also du kannst es ja mal testen. Du kommst aufs Programm mit http://ip:8989. IP steht natürlich für den Rechner wo du es ausführst. Du mußt aber unter core/module/homematic/homematic.py deine IP der CCU anpassen. Momentan überarbeitet ich gerade das ganze. Ich hab angefangen ein plugin-framework zu erstellen.
Zum Thema Anfänger das bin ich auch ist mein erstes Projekt.

Re: SimpleXMLRPCServer

Verfasst: Montag 13. Mai 2013, 21:34
von Sirius3
@av_jui: schau mir gerade Dein Homepy an.
Da sind mir gleich ein paar Sachen aufgefallen:
in core/Helper.py zum Beispiel:
- es hat viel zu viele Leerzeilen
-nie nackte »except«s benutzen, weil alle Exceptions auch NameError und ähnliches gecatcht werden und so das Debuggen unmöglich wird.
- außer einem AttributeError sollte an der Stelle auch nichts auftreten.
- Du erzeugst »cleanstr« gibst aber »string« zurück??
- die Bezeichner halten sich auch an keine Konvention, und sind nicht mal konsistent
- wenn im »except« nur ein »raise« steht, ist es ziemlich überflüssig, ebenso das »del s« und damit das ganze »finally«
- die ganze »ParseTyps«-Funktion ist seltsam. »dict«s sind dazu da, dass man mit »key«s auf »value«s zugreift, warum also die »for«-Schleifen? Und warum doppelt über das selbe Dict?
- die Einrückung von »if serial…« ist falsch, die Variablen »serial«, »name« und »type« unter Umständen nicht definiert.
- die Einrückungen sind 4 Leerzeichen und keine Tabs.

core/Config.py:
- keine einzige Method der Klasse »Config« benutzt »self«, die Klasse ist also unnötig.
- nackte »except«s, siehe oben
- CheckSection: eine Modulkonstante! in einer Funktion eines anderen Moduls zu erzeugen macht den Code unwartbar.
- schau Dir mal »defaultdict« bzw. die »get«-Method von dict an.
- um Defaults zu setzen kannst Du sowas wie „config = dict(DEFAULTS).update(config)“ benutzen.

core/DBFunctions.py:
- niemals SQL-Befehle mit Stringverknüpfung erzeugen. Damit baust Du Dir riesige Sicherheitslücken ein. Es gibt Platzhalter und Variablen bei »execute«!.
- keine Method der DBFunctions-Klasse benutzt »self«, also richtige Klasse mit connection als Attribut.
- muß mich korrigieren, Du bindest völlig unsinnig lokale Variablen an »self«.

core/Logger.py:
- logging braucht keine unnötige Wrapperschicht.

Re: SimpleXMLRPCServer

Verfasst: Dienstag 14. Mai 2013, 09:54
von av_jui
Sirius3 hat geschrieben:@av_jui: schau mir gerade Dein Homepy an.
Da sind mir gleich ein paar Sachen aufgefallen:
Danke schonmal das du dir die Mühe gemacht hast mal drüber zu schauen. Da das ja mein Noob Projekt ist und ich täglich was neues Lerne wird auch ständig was geändert. Momentan bin ich dran ein plugin-framework einzurichten.
Gerne aber nähme ich Verbesserungsvorschläge an da ich ja wie gesagt eine Neuling bin.
Sirius3 hat geschrieben:in core/Helper.py zum Beispiel:
- es hat viel zu viele Leerzeilen
-nie nackte »except«s benutzen, weil alle Exceptions auch NameError und ähnliches gecatcht werden und so das Debuggen unmöglich wird.
- außer einem AttributeError sollte an der Stelle auch nichts auftreten.
- Du erzeugst »cleanstr« gibst aber »string« zurück??
- die Bezeichner halten sich auch an keine Konvention, und sind nicht mal konsistent
- wenn im »except« nur ein »raise« steht, ist es ziemlich überflüssig, ebenso das »del s« und damit das ganze »finally«
- die ganze »ParseTyps«-Funktion ist seltsam. »dict«s sind dazu da, dass man mit »key«s auf »value«s zugreift, warum also die »for«-Schleifen? Und warum doppelt über das selbe Dict?
- die Einrückung von »if serial…« ist falsch, die Variablen »serial«, »name« und »type« unter Umständen nicht definiert.
- die Einrückungen sind 4 Leerzeichen und keine Tabs.
zu core/Helper.py
Bin ich gerade am überarbeiten den das ParseType kommt dann sowieso ins homematic plugin.
Das mit den excepts sehe ich ein.
Zu den Einrückungen. Werden immer 4 Leerzeichen verwendet? Werden bei Python immer nur Leerzeichen verwendet ist Tab verpönt?
Sirius3 hat geschrieben: core/Config.py:
- keine einzige Method der Klasse »Config« benutzt »self«, die Klasse ist also unnötig.
- nackte »except«s, siehe oben
- CheckSection: eine Modulkonstante! in einer Funktion eines anderen Moduls zu erzeugen macht den Code unwartbar.
- schau Dir mal »defaultdict« bzw. die »get«-Method von dict an.
- um Defaults zu setzen kannst Du sowas wie „config = dict(DEFAULTS).update(config)“ benutzen.
Das mit self habe ich am Anfang nicht verstanden. Erst später darum fehlt es noch an einigen Stellen. :oops:
Das andere werde ich mir ansehen
Sirius3 hat geschrieben: core/DBFunctions.py:
- niemals SQL-Befehle mit Stringverknüpfung erzeugen. Damit baust Du Dir riesige Sicherheitslücken ein. Es gibt Platzhalter und Variablen bei »execute«!.
- keine Method der DBFunctions-Klasse benutzt »self«, also richtige Klasse mit connection als Attribut.
- muß mich korrigieren, Du bindest völlig unsinnig lokale Variablen an »self«.
Wird gerade überarbeitet. Es gibt dann nicht mehr so viele funktionen und die Stringverknüpfungen sind dann auch weck
Sirius3 hat geschrieben: core/Logger.py:
- logging braucht keine unnötige Wrapperschicht.
[/quote][/quote]
[/quote]
Was meinst du mit Wrapperschicht?

Und nochmal Danke fürs darüber fliegen.

Re: SimpleXMLRPCServer

Verfasst: Donnerstag 16. Mai 2013, 00:23
von Leonidas
av_jui hat geschrieben:Zu den Einrückungen. Werden immer 4 Leerzeichen verwendet? Werden bei Python immer nur Leerzeichen verwendet ist Tab verpönt?
Ja. Ein Guter(TM) Editor schreibt wenn man Tab drückt vier Leerzeichen raus und wenn man es mit der Backspace-Taste löscht dann werden die vier Spaces auf einmal gelöscht.