Ankündigung

Einklappen
Keine Ankündigung bisher.

Smartmeter Plugin - Tester gesucht

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

    #16
    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() 

    Kommentar


      #17
      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.
      Umgezogen? Ja! ... Fertig? Nein!
      Baustelle 2.0 !

      Kommentar


        #18
        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

        Kommentar


          #19
          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'

          Kommentar


            #20
            Code:
            __init__() got an unexpected keyword argument 'cycle'
            zumindest mein Code verwendet "update_cycle"...

            Kommentar


              #21
              dein Code hab ich auch verwendet.

              Also bei Mirkos Plugin kommt der Fehler

              Code:
              2013-11-02 22:48:45,547 ERROR    Main         Plugin smartmeter exception: invalid syntax (__init__.py, line 44) -- 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 79, in __init__
                  exec("import {0}".format(classpath))
                File "<string>", line 1, in <module>
                File "/usr/smarthome/plugins/dlms/__init__.py", line 44
                  print self.id.strip()
                           ^
              SyntaxError: invalid syntax

              Kommentar


                #22
                Zitat von freetzmaster Beitrag anzeigen
                dein Code hab ich auch verwendet.
                als nächstes: in den items muss es nicht "dlms_code" sondern "dlms_obis_code" heißen...

                Hm, leider haben Mirko und ich geringfügige Abweichungen. Mirko, bist du einverstanden wenn ich den Code einchecke? Copyright Userforum oder so?

                Grüße
                Robert

                Kommentar


                  #23
                  Hm, benutz einfach mal:

                  plugin.conf
                  Code:
                  [dlms]
                      class_name = DLMS
                      class_path = plugins.dlms
                      serialport = <deine-schnittstelle>
                  item.conf
                  Code:
                  [Stromzaehler]
                    [[Bezug]]
                      [[[Energie]]]
                        type = num
                        dlms_obis_code = 1.8.1
                    [[Lieferung]]
                      [[[Energie]]]
                        type = num
                        dlms_obis_code = 2.8.1

                  Kommentar


                    #24
                    OK, das sieht schonmal sehr viel besser aus jetzt geh ich nochmal überprüfen ob der Leser richtig sitzt, weil folgendes kommt.

                    Code:
                    2013-11-02 22:57:28,135 DEBUG    DLMS         dlms: Reading took: 4.02s -- __init__.py:_update_values:86
                    2013-11-02 22:57:28,141 WARNING  DLMS         dlms: checksum/protocol error: response= checksum=0 -- __init__.py:_update_values:95

                    Kommentar


                      #25
                      @Robert: Check den ruhig mal ein. Mein Code war ja nix weiter als nen erster Wurf.
                      Umgezogen? Ja! ... Fertig? Nein!
                      Baustelle 2.0 !

                      Kommentar


                        #26
                        Ich vermute mein Protokoll ist anderes, hab es mal ausgelesen. In dem Datenblatt vom Zähler steht aber drin das er nach IEC 62053-21 arbeitet.
                        Ich erkenne bei mir zum Beispiel keine Prüfsumme. Aber der Anforderungsbefehl (/?! CR LF) ist der gleiche. Wie kann ich testen ob die Baudratenumstellung funktioniert. Teste aktuell mit TeraTerm am Laptop aber hab keine Idee wie ich das so schnell machen soll.

                        Hier mein Protokoll:
                        Code:
                        /?!
                        
                        /EMH4\@--ITZ-G003VE
                        
                        F.F(00000000)
                        
                        0.0.0(90015239)
                        
                        0.1.0(30)
                        
                        0.1.2*30(0131101000000)
                        
                        0.1.2*29(1131001000000)
                        
                        0.1.2*28(1130901000000)
                        
                        0.1.2*27(1130801000000)
                        
                        0.1.2*26(1130701000000)
                        
                        0.1.2*25(1130601000000)
                        
                        0.1.2*24(1130501000000)
                        
                        0.1.2*23(1130401000000)
                        
                        0.1.2*22(0130301000000)
                        
                        0.1.2*21(0130201000000)
                        
                        0.1.2*20(0130101000000)
                        
                        0.1.2*19(0121201000000)
                        
                        0.1.2*18(0121101000000)
                        
                        0.1.2*17(1121001000000)
                        
                        0.1.2*16(1120901000000)
                        
                        0.9.1(0162700)
                        
                        0.9.2(0131103)
                        
                        1.8.1(00007375*kWh)
                        
                        1.8.1*30(00007354*kWh)
                        
                        1.8.1*29(00007055*kWh)
                        
                        1.8.1*28(00006848*kWh)
                        
                        1.8.1*27(00006654*kWh)
                        
                        1.8.1*26(00006524*kWh)
                        
                        1.8.1*25(00006375*kWh)
                        
                        1.8.1*24(00006185*kWh)
                        
                        1.8.1*23(00005970*kWh)
                        
                        1.8.1*22(00005708*kWh)
                        
                        1.8.1*21(00005351*kWh)
                        
                        1.8.1*20(00004949*kWh)
                        
                        1.8.1*19(00004434*kWh)
                        
                        1.8.1*18(00004112*kWh)
                        
                        1.8.1*17(00003803*kWh)
                        
                        1.8.1*16(00003611*kWh)
                        
                        2.8.1(00020129*kWh)
                        
                        2.8.1*30(00020113*kWh)
                        
                        2.8.1*29(00019532*kWh)
                        
                        2.8.1*28(00018791*kWh)
                        
                        2.8.1*27(00017699*kWh)
                        
                        2.8.1*26(00016359*kWh)
                        
                        2.8.1*25(00015235*kWh)
                        
                        2.8.1*24(00014226*kWh)
                        
                        2.8.1*23(00013212*kWh)
                        
                        2.8.1*22(00012291*kWh)
                        
                        2.8.1*21(00012155*kWh)
                        
                        2.8.1*20(00012019*kWh)
                        
                        2.8.1*19(00011978*kWh)
                        
                        2.8.1*18(00011775*kWh)
                        
                        2.8.1*17(00011161*kWh)
                        
                        2.8.1*16(00010379*kWh)
                        
                        1.25(00.86*kW)
                        
                        31.25(002.93*A)
                        
                        51.25(000.16*A)
                        
                        71.25(000.48*A)
                        
                        32.25(233.2*V)
                        
                        52.25(233.7*V)
                        
                        72.25(235.2*V)
                        
                        !

                        Kommentar


                          #27
                          Fehlerbeschreibung?

                          Ausgabe vom Debug-Modus?

                          Zeile 97 mal einkommentieren.

                          Kommentar


                            #28
                            Ambei das Debug Log
                            Angehängte Dateien

                            Kommentar


                              #29
                              Code:
                              2013-11-03 16:49:50,745 DEBUG    DLMS         dlms: meter returned capability for higher baudrate 4800 -- __init__.py:_update_values:51
                              2013-11-03 16:49:50,750 DEBUG    DLMS         dlms: trying to switch baudrate -- __init__.py:_update_values:54
                              2013-11-03 16:49:50,757 DEBUG    DLMS         dlms: socket closed - creating new one -- __init__.py:_update_values:66
                              2013-11-03 16:49:50,769 DEBUG    DLMS         dlms: Switching took: 0.01s -- __init__.py:_update_values:68
                              2013-11-03 16:49:50,774 DEBUG    DLMS         dlms: switch done -- __init__.py:_update_values:69
                              2013-11-03 16:49:52,782 DEBUG    DLMS         dlms: Reading took: 3.55s -- __init__.py:_update_values:86
                              2013-11-03 16:49:52,788 WARNING  DLMS         dlms: checksum/protocol error: response=0x6 checksum=0 -- __init__.py:_update_values:95
                              Verrät, dass der Wechsel auf 4800 Baud erkannt wird und umgeschaltet wird. Der Checksummen-Fehler - mal schaun.

                              Code:
                              2013-11-03 16:50:09,348 DEBUG    DLMS         dlms: update -- __init__.py:_update_values:28
                              
                              [...]
                              
                              2013-11-03 16:50:29,033 DEBUG    DLMS         dlms: update -- __init__.py:_update_values:28
                              2013-11-03 16:50:29,460 WARNING  DLMS         dlms: device reports readiness to read but returned no data (device disconnected?) -- __init__.py:_update_values:43
                              2013-11-03 16:50:29,539 WARNING  DLMS         dlms: device reports readiness to read but returned no data (device disconnected?) -- __init__.py:_update_values:43
                              2013-11-03 16:50:29,547 DEBUG    Scheduler    DLMS next time: 2013-11-03 16:50:49+01:00 -- scheduler.py:_next_time:289
                              2013-11-03 16:50:29,570 WARNING  DLMS         dlms: device reports readiness to read but returned no data (device disconnected?) -- __init__.py:_update_values:85
                              2013-11-03 16:50:29,581 DEBUG    DLMS         dlms: Reading took: 20.23s -- __init__.py:_update_values:86
                              2013-11-03 16:50:29,595 WARNING  DLMS         dlms: checksum/protocol error: response=0x0 0x0 0x0 0x0 0x0 0x0 checksum=0 -- __init__.py:_update_values:95
                              Ab der Umschaltung ist also erst mal was kaputt. An was für einer Schnittstelle hängt der Leser? USB-Seriell-Wandler? Chipsatz bekannt?

                              Vorschlag:

                              Füge unter Zeile 48:
                              Code:
                                          baud_capable = 300 * (1 << (response[4] - 0x30))
                              noch
                              Code:
                                          baud_capable = 300
                              ein. Dann bleibt es trotz der (korrekt erkannten - in deiner Auslesung die "4" in der zweiten Zeile!) höheren Datenrate auf 300 Baud.

                              Grüße
                              Robert

                              Kommentar


                                #30
                                Lesekopf
                                volkszaehler.org - wiki - USB-IR-Schreib-Lesekopf

                                Chip ist ein CP2104-GM

                                Anbei nochmal das Debug-Log mit 300 Baud

                                er liest ausjedenfall schon was, trotzdem bricht er das Lesen scheinbar an unterschiedlichen Punkten ab. Kenn mich leider mit Python noch nicht so gut aus das ich das selber lösen kann, bin aber am lernen.

                                Vielen Dank schonmal für deinen Support. Echt klasse. Hab auch schon ne kleine Spende da gelassen.
                                Angehängte Dateien

                                Kommentar

                                Lädt...
                                X