Ankündigung

Einklappen
Keine Ankündigung bisher.

Smartmeter Plugin - Tester gesucht

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Robert
    antwortet
    Code:
    __init__() got an unexpected keyword argument 'cycle'
    zumindest mein Code verwendet "update_cycle"...

    Einen Kommentar schreiben:


  • freetzmaster
    antwortet
    Könnt ihr mir bei eurem Plugin mal weiterhelfen hab das aktuelle Beta image 2.7 mit python3 am laufen und bekomm da eine Fehlermeldung.

    Mein Zähler: EMH ITZ
    Leser ist über udev-Regel eingebunden

    item.conf
    Code:
    [EG]
        [[UV]]
            [[[Haushaltszaehler]]]
                [[[[Zaehlerstand]]]]
                    type = num
                    dlms_code = "1.8.1"
                    visu = yes
                    history = yes
    plugin.conf
    Code:
    [smartmeter]
        class_name = DLMS
        class_path = plugins.dlms
        serialport = "/dev/haushalt"
        baudrate = auto
        cycle = 60
    Log
    Code:
    2013-11-02 21:15:54,742 ERROR    Main         Plugin smartmeter exception: __init__() got an unexpected keyword argument 'cycle' -- plugin.py:__init__:57
    Traceback (most recent call last):
      File "/usr/smarthome/lib/plugin.py", line 53, in __init__
        plugin_thread = Plugin(smarthome, plugin, classname, classpath, args)
      File "/usr/smarthome/lib/plugin.py", line 80, in __init__
        exec("self.plugin = {0}.{1}(smarthome{2})".format(classpath, classname, args))
      File "<string>", line 1, in <module>
    TypeError: __init__() got an unexpected keyword argument 'cycle'

    Einen Kommentar schreiben:


  • mhknx
    antwortet
    Hi,

    hab mir nach dem Zählerumbau bei mir am Wochenende das Plugin mal angeschaut. Meine Zähler sind von easymeter (Q3D). Diese lassen sich einfach ohne Initialisierung und Baudratenumstellung auslesen. Daher ist der aktuelle Stand meines Plugins auch deutlich "einfacher". Ich halte daher verschiedene Plugins für verschiedene Zähler für den besseren Weg, als ein All-In-One Plugin. Wie seht ihr das?

    Gruß
    Mark

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Mein einer Zähler braucht 100 Sekunden bei 300 Baud .. das ist kein Scherz. Davon ist der einzige sinnvolle Wert der Zählerstand. Wenn der Rest hier wieder stabil läuft versuche ich das mal zu debuggen.

    Ich finde die Auswertung darf ruhig nach dem kompletten Auslesen stattfinden. Das macht spätere Features deutlich einfacher. Ich meld mich dann wieder.

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Hi Mirko, schade...

    Wäre toll wenn du dann mit ein bisschen Ruhe draufschauen würdest - denke eine Version fürs GIT wäre schön.

    Habe noch mal nachgelegt: STX/ETX Auswertung, die Leseschleifen werden vorzeitig verlassen wenn alles da (keine Timeouts notwendig, evtl. toleranter für langsame Zähler und dennoch schnell), Überprüfung der Checksumme (damit keine falschen Daten übernommen werden), Entfernung von Echo-Zeichen (zumindest mein Lesekopf braucht das).

    Bin jetzt runter auf 1,7s (von über 14s am Anfang!)

    Noch schneller ginge es wohl, wenn man eine dedizierte Routine für die Baudratenerkennung baut und dann die "Updates" in einem Schreibvorgang ohne Abwarten der Antwort abhandelt.

    PHP-Code:
    #!/usr/local/bin/python

    import logging
    import time
    import serial
    import re

    logger 
    logging.getLogger('DLMS')

    class 
    DLMS():
        
    def __init__(selfsmarthomeserialportbaudrate "300"update_cycle "20"):
            
    self._sh smarthome
            self
    ._update_cycle int(update_cycle)
            
    self._obis_codes = {}
            
    self._serial serial.Serial(serialportint(baudrate), bytesize serial.SEVENBITSparity serial.PARITY_EVENtimeout 2)
            
    self._request bytearray('\x06000\r\n''ascii')

        
    def run(self):
            
    self.alive True 
            self
    ._sh.scheduler.add('DLMS'self._update_valuesprio 5cycle self._update_cycle)

        
    def stop(self): 
            
    self.alive False
            self
    ._serial.close()
            
    self._sh.scheduler.remove('DLMS')

        
    def _update_values(self):
            
    logger.debug("dlms: update")
            
    start time.time()
            
    init_seq bytes('/?!\r\n''ascii')
            
    self._serial.flushInput()
            
    self._serial.write(init_seq)
            
    response bytes()
            
    prev_length 0
            
    try:
                while 
    self.alive:
                    
    response += self._serial.read()
                    
    length len(response)
                    if (
    length == prev_length) or ((length len(init_seq)) and (response[-1] == 0x0a)): # break if timeout or newline-character
                        
    break
                    
    prev_length length
            except Exception 
    as e:
                
    logger.warning("dlms: {0}".format(e))
            
    # remove echoed chars if present
            
    if (init_seq == response[:len(init_seq)]):
                
    response response[len(init_seq):]
            if (
    len(response) >= 5) and ((response[4] - 0x30in range(6)):
                
    baud_capable 300 * (<< (response[4] - 0x30))
                if 
    baud_capable self._serial.baudrate:
                    try:
                        
    logger.debug("dlms: meter returned capability for higher baudrate {}".format(baud_capable))
                        
    self._request[2] = response[4]  # change request to set higher baudrate
                        
    self._serial.write(self._request)
                        
    logger.debug("dlms: trying to switch baudrate")
                        
    switch_start time.time()
                        
    # Alt1:
                        #self._serial.baudrate = baud_capable
                        # Alt2:
                        #settings = self._serial.getSettingsDict()
                        #settings['baudrate'] = baud_capable
                        #self._serial.applySettingsDict(settings)
                        # Alt3:
                        
    port self._serial.port
                        self
    ._serial.close()
                        
    del self._serial
                        logger
    .debug("dlms: socket closed - creating new one")
                        
    self._serial serial.Serial(portbaud_capablebytesize serial.SEVENBITSparity serial.PARITY_EVENtimeout 2)
                        
    logger.debug("dlms: Switching took: {:.2f}s".format(time.time() - switch_start))
                        
    logger.debug("dlms: switch done")
                    
    except Exception as e:
                        
    logger.warning("dlms: {0}".format(e))
                        return
                else:
                    
    self._serial.write(self._request)
            
    response bytes()
            
    prev_length 0
            
    try:
                while 
    self.alive:
                    
    response += self._serial.read()
                    
    length len(response)
                    if (
    length == prev_length) or ((length >= 2) and (response[-2] == 0x03)): # break if timeout or "ETX"
                        
    break
                    
    prev_length length
            except Exception 
    as e:
                
    logger.warning("dlms: {0}".format(e))
            
    logger.debug("dlms: Reading took: {:.2f}s".format(time.time() - start))
            
    # remove echoed chars if present
            
    if (self._request == response[:len(self._request)]):
                
    response response[len(self._request):]
            
    # perform checks (start with STX, end with ETX, checksum match)
            
    checksum 0
            
    for i in response[1:]:
                
    checksum ^= i
            
    if (len(response) < 5) or (response[0] != 0x02) or (response[-2] != 0x03) or (checksum != 0x00):
                
    logger.warning("dlms: checksum/protocol error: response={} checksum={}".format(' '.join(hex(i) for i in response), checksum))
                return
            
    #print(str(response[1:-4], 'ascii'))
            
    for line in re.split('\r\n'str(response[1:-4], 'ascii')):
                
    #if re.match('[0-9]+\.[0-9]\.[0-9](.+)', line): # allows only x.y.z(foo)
                
    if re.match('[0-9]+\.[0-9].+(.+)'line): # allows also x.y(foo)
                    
    try:
                        
    data re.split(r'[(*)]+'line)
                        
    logger.debug("dlms: {} = {} {}".format(data[0], data[1], data[2]))
                        if 
    data[0in self._obis_codes:
                            for 
    item in self._obis_codes[data[0]]['items']:
                                
    item(data[1], 'DLMS''OBIS {}'.format(data[0]))
                    
    except Exception as e:
                        
    logger.warning("dlms: line={} exception={}".format(linee))

        
    def parse_item(selfitem):
            if 
    'dlms_obis_code' in item.conf:
                
    logger.debug("parse item: {0}".format(item))
                
    obis_code item.conf['dlms_obis_code']
                if 
    not obis_code in self._obis_codes:
                    
    self._obis_codes[obis_code] = {'items': [item], 'logics': []}
                else:
                    
    self._obis_codes[obis_code]['items'].append(item)
            return 
    None

    if __name__ == '__main__':
        
    logging.basicConfig(level=logging.DEBUG)
        
    myplugin Plugin('DLMS')
        
    myplugin.run() 

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Zitat von Robert Beitrag anzeigen
    Hast du meine letzte Version mal angeschaut (hatte die nachträglich editiert um nicht den Thread vollzuspammen)? Die funktioniert mit Baudratenumstellung wirklich klasse bei mir. Lesevorgang 2,7s, mit Möglichkeit dass auf 2,1s zu drücken.
    Ja erst nach meinem Post entdeckt. Läuft aber hier nicht. Kann das aber heute nicht mehr debuggen. Ab und zu kamen mal Werte rein.

    Einen Kommentar schreiben:


  • mhknx
    antwortet
    Smartmeter Plugin - Tester gesucht

    Da kann ich leider nix beitragen, außer mit meinen Zählern zu testen.

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Meine Kernfrage: Baudratenumstellung?
    Hast du meine letzte Version mal angeschaut (hatte die nachträglich editiert um nicht den Thread vollzuspammen)? Die funktioniert mit Baudratenumstellung wirklich klasse bei mir. Lesevorgang 2,7s, mit Möglichkeit dass auf 2,1s zu drücken.

    Ich hack da gleich noch eben die STX/ETX-Erkennung und Checksumme rein, dann kann es auch Übertragungsfehler erkennen und müsste noch mal 0,3s schneller sein.

    Grüße
    Robert

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Meine Kernfrage: Baudratenumstellung?

    Einen Kommentar schreiben:


  • mhknx
    antwortet
    Hi,

    habe das Auslesen meines Stromzählers über ein ein fast baugleiches Plugin umgesetzt. Das Plugin werkelt schon seit 3 Monaten stabil herum, hat es aber aus Zeitgründen noch nicht in "öffentliche" Codequalität geschafft. Ich lese die Werte so aus:

    def update_status(self):

    # open serial connection
    self.ser.open()
    self.ser.flushInput()

    # wait for start of next datablock
    while True:
    line = self.ser.readline()
    if line.find('!') >= 0:
    break

    # read next datablock
    datablock=[]

    while True:
    line = self.ser.readline()
    datablock.append(line)
    if line.find('!') >= 0:
    break

    #close serial connection
    self.ser.close()

    Der Rest ist fast identisch. Hoffe am Wochenende kann ich den zweiten in Betrieb nehmen, die Unterstützung für mehrere fehlt bei meiner Variante noch. Sollte aber nicht schwer sein. Dann kann ich die Variante auch mal mit testen.

    Gruß
    Mark

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Abei eine Version mit automatischer Umschaltung (drei verschiedene Methoden inkl. Holzhammer)

    Funktioniert sogar mit meinem Zähler (Landis&Gyr ZMD120) mit 4800 Baud perfekt - danke für das Bestehen auf Umschaltung! Ich hatte schon aufgegeben meinen Zähler umzuschalten! :-D

    Übrigens läuft mein Zähler dann mit 4800 Baud weiter - also es muss nich jedesmal zurückgeschaltet werden!? Evtl. funktioniert das auch mit deinem?

    [bitte Plugin-Code von nächster Seite nehmen]

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Den Holzhammer hatte ich auch schon ... ich weiß aber nicht mehr ob in Perl und/oder Python. Aber vielleicht hat sich ja bei Python3 was getan oder ich war wirklich zu dämlich ... das will ich nicht ausschließen.

    Ja man könnten nen eigenen branch dafür machen. Selbst bei meinen 2 Zählern mag einer und der andere nicht mit dem was hier gepostet ist. Ich hab beruflich nur ne anstrengende Woche vor mir und Familie gibts ja auch noch.

    Sollten wir aber gemeinsam verfolgen denn was bei einem geht muss längst nicht auch beim anderen laufen. Daher wäre es schon gut wenn da mehrere Leute mittesten.

    @greentux: Vielleicht kann Dein Bekannter seinen Code schonmal posten und wir testen den?

    Grüße

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Die gepostete Version sollte eigentlich als Plugin auch lauffähig sein. Ich muss die Tage mal in den untiefen der Cloud suchen ob ich noch was aktuelleres habe. Ich glaube der Code ist nicht in develop - nur hier.
    Stimmt. Als ich den THread hier vor dem Posten wiederenddeckt habe habe ich mir nen Wolf gesucht, wo ich die andere Version her habe. Irgendwo im volkszähaehler.org/ip-Symcom/sonstwo hattest du das nämlich auch gepostet.

    Zitat von JuMi2006 Beitrag anzeigen
    Da gibt es 2 Varianten. Einmal gibt es Zähler di die Werte von anderen angeschlossenen Zählern mit übermitteln, zum zweiten dann eben die Differenzierung zwischen zwei "normalen" Zählern. Für zweitens würde ja eine Zähler-ID in der intem.conf reichen.
    Die erste Variante kenne ich gar nicht. Bei der zweiten: Ja stimmt, wenn man die korrekte Zählerkennung wie mit DLMS ausgelesen weiß, wäre das natürlich die logischte ID für die Items.

    Zitat von JuMi2006 Beitrag anzeigen
    Baudratenumstellung:
    Die brauchts imho. Wie gesagt, ich habs weder in Perl noch Python hinbekommen. Die man pages glaube ich dabei wirklich verstanden zu haben und Beispielcodes im Netz deckten sich mit meinem Vorgehen.
    Zumindest die Holzhammermathode mit Close, del, neuer Klasse sollte ja funktionieren!?

    Zitat von JuMi2006 Beitrag anzeigen
    Testen kann ich das frühestens Mitte der Woche. Die Zähler sind vom Timing her aber auch höchst unterschiedlich (Zeit zwischen Initialisierung und Befehl Baudrate) und machen es damit nicht einfacher. Manch Zähler will auch unbedingt ein ACK haben (000 bis 040) und verweigert sonst auch ohne Baudratenumstellung die Ausgabe, das fehlt hier auf jeden Fall.
    Passt schon. Die Zähler sind da wohl in der Tat recht eigen. Schön wäre es halt, wenn man eine für zumindest bei unsere Zähler lauffähige Version ins GIT packen könnte, um diese auch standardmäßig anderen Benutzern anzubieten (ohne hier im Forum zu suchen & c&p).

    Grüße
    Robert

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Kurz ein paar Stichpunkte:

    Plugin:
    Die gepostete Version sollte eigentlich als Plugin auch lauffähig sein. Ich muss die Tage mal in den untiefen der Cloud suchen ob ich noch was aktuelleres habe. Ich glaube der Code ist nicht in develop - nur hier.

    Mehrere Zähler:
    Da gibt es 2 Varianten. Einmal gibt es Zähler di die Werte von anderen angeschlossenen Zählern mit übermitteln, zum zweiten dann eben die Differenzierung zwischen zwei "normalen" Zählern. Für zweitens würde ja eine Zähler-ID in der intem.conf reichen.

    Baudratenumstellung:
    Die brauchts imho. Wie gesagt, ich habs weder in Perl noch Python hinbekommen. Die man pages glaube ich dabei wirklich verstanden zu haben und Beispielcodes im Netz deckten sich mit meinem Vorgehen.

    Generell:
    Testen kann ich das frühestens Mitte der Woche. Die Zähler sind vom Timing her aber auch höchst unterschiedlich (Zeit zwischen Initialisierung und Befehl Baudrate) und machen es damit nicht einfacher. Manch Zähler will auch unbedingt ein ACK haben (000 bis 040) und verweigert sonst auch ohne Baudratenumstellung die Ausgabe, das fehlt hier auf jeden Fall.

    Einen Kommentar schreiben:


  • Robert
    antwortet
    Hi!

    Ich hab heute mal Mirkos Code genommen (leider hatte ich nur eine Vorversion ohne vollständige Plugin-Funktionalität gefunden - ist dieses Plugin nicht im develop?) und für meine Zwecke angepasst. Da mein Zähler nur 300 Baud kann ist die Baudraten-Umschaltung entfallen (konnte ich nicht testen - mein Zähler würde aber korrekt als 300 baud erkannt). Ansonsten Umsetzung auf Python 3.2/3.3 und python3-serial (ohne socat). Das Splitting/Matching habe ich per RegEx etwas strenger gemacht.

    Evtl. kann man ein Best-of machen und das einchecken? Mirko?

    Mehrere Zähler: Wäre nett, aber evtl. sollte das über einen Modifikation der Plugin-Engine gelöst werden, so dass man einfach das Plugin in der Config zweimal instanziiert? Dann im Plugin und jedem Item nur je ein Attribut "dlms_meter", was matchen muss?

    plugin/dlms/__init__.py
    PHP-Code:
    #!/usr/local/bin/python

    import logging
    import time
    import serial
    import re

    logger 
    logging.getLogger('DLMS')

    class 
    DLMS():
        
    def __init__(selfsmarthomeserialportbaudrate "300"update_cycle "60"):
            
    self._sh smarthome
            self
    ._update_cycle int(update_cycle)
            
    self._obis_codes = {}
            
    self._serial serial.Serial(serialportint(baudrate), bytesize serial.SEVENBITSparity serial.PARITY_EVENtimeout 2)

        
    def run(self):
            
    self.alive True 
            self
    ._sh.scheduler.add('DLMS'self._update_valuesprio 5cycle self._update_cycle)

        
    def stop(self): 
            
    self.alive False 
            self
    ._sh.scheduler.remove('DLMS')

        
    def _update_values(self):
            
    logger.debug("dlms: update")
            
    request "/?!\r\n"
            
    start time.time()
            
    self._serial.write(bytes(request'utf-8'))
            
    response bytes()
            try:
                while 
    self.alive:
                    
    prev_len len(response)
                    
    response += self._serial.read()
                    if (
    prev_len == len(response)):
                        break
            
    except Exception as e:
                
    logger.warning("dlms: {0}".format(e))
            
    logger.debug("dlms: Reading took: {:.2f}s".format(time.time() - start))
            for 
    line in re.split('\r\n'str(response,'ascii')):
    #            if re.match('[0-9]+\.[0-9]\.[0-9](.+)', line): # für x.y.z(foo)
                
    if re.match('[0-9]+\.[0-9].+(.+)'line): # für x.y(foo)
                    
    try:
                        
    data re.split(r'[(*)]+'line)
                        
    logger.debug("dlms: {} = {} {}".format(data[0], data[1], data[2]))
                        if 
    data[0in self._obis_codes:
                            for 
    item in self._obis_codes[data[0]]['items']:
                                
    item(data[1], 'DLMS''OBIS {}'.format(data[0]))
                    
    except Exception as e:
                        
    logger.warning("dlms: line={} exception={}".format(linee))

        
    def parse_item(selfitem):
            if 
    'dlms_obis_code' in item.conf:
                
    logger.debug("parse item: {0}".format(item))
                
    obis_code item.conf['dlms_obis_code']
                if 
    not obis_code in self._obis_codes:
                    
    self._obis_codes[obis_code] = {'items': [item], 'logics': []}
                else:
                    
    self._obis_codes[obis_code]['items'].append(item)
            return 
    None

    if __name__ == '__main__':
        
    logging.basicConfig(level=logging.DEBUG)
        
    myplugin Plugin('DLMS')
        
    myplugin.run() 
    etc/plugin.conf
    Code:
    [dlms]
        class_name = DLMS
        class_path = plugins.dlms
        serialport = /dev/ttyO1
    items/demo.conf
    PHP-Code:
    [Stromzaehler]
      [[
    Bezug]]
        [[[
    Energie]]]
          
    type num
          dlms_obis_code 
    1.8.1
        
    [[[Leistung]]]
          
    type num
          
    eval = "(3600000 / sh.Stromzaehler.Bezug.Energie.prev_change()) if not isinstance(sh.Stromzaehler.Bezug.Energie.prev_change(), datetime.datetime) else 0"
          
    eval_trigger Stromzaehler.Bezug.Energie
      
    [[Lieferung]]
        [[[
    Energie]]]
          
    type num
          dlms_obis_code 
    2.8.1
        
    [[[Leistung]]]
          
    type num
          
    eval = "(3600000 / sh.Stromzaehler.Lieferung.Energie.prev_change()) if not isinstance(sh.Stromzaehler.Lieferung.Energie.prev_change(), datetime.datetime) else 0"
          
    eval_trigger Stromzaehler.Lieferung.Energie 
    Die Leistungen muss ich mir leider berechnen, da mein "Sparzähler" tatsächlich nur die Zählerstände rausrückt... Die Rechnungen sind richtig, allerdings ist der erste berechnete Wert immer falsch, da die falsche Zeit zum Ansatz kommt (weil der erste "_change" durch den Start von sh.py kommt und nicht durch einen echten Wechsel). Dass kann man nur verhindern, indem man die Zeit von zwei echten Zählerstandswechseln abwartet.

    Grüße
    Robert

    Einen Kommentar schreiben:

Lädt...
X