Ankündigung

Einklappen
Keine Ankündigung bisher.

Plugin Problem: Luxtronik

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

    Plugin Problem: Luxtronik

    Hallo!

    Sorry, dass ich wieder mit dem Thema anfange. Aber es bringt mich zum Verzweifeln.

    Das Plugin verliert immer wieder die Verbindung zur Wärmepumpe.
    Code:
    2015-10-07 15:37:40,974 DEBUG    Scheduler    Luxtronic2 next time: 2015-10-07 15:38:40+02:00 -- scheduler.py:_next_time:289
    2015-10-07 15:37:44,471 ERROR    Luxtronic2   Method Luxtronic2 exception: error receiving answer: timeout -- scheduler.py:_task:348
    Traceback (most recent call last):
      File "/usr/smarthome/plugins/luxtronic2/__init__.py", line 107, in _request
        answer = self._sock.recv(length)
    socket.timeout: timed out
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/usr/smarthome/lib/scheduler.py", line 344, in _task
        obj()
      File "/usr/smarthome/plugins/luxtronic2/__init__.py", line 254, in _refresh
        self.refresh_parameters()
      File "/usr/smarthome/plugins/luxtronic2/__init__.py", line 153, in refresh_parameters
        answer = self._request(request, 8)
      File "/usr/smarthome/plugins/luxtronic2/__init__.py", line 110, in _request
        raise luxex("error receiving answer: timeout")
    plugins.luxtronic2.luxex: error receiving answer: timeout
    Danach kommt immer dieser Fehler
    Code:
    2015-10-07 15:39:40,476 WARNING  Luxtronic2   Luxtronic2: failed to retrieve parameters -- __init__.py:refresh_parameters:173
    2015-10-07 15:39:40,500 WARNING  Luxtronic2   Luxtronic2: failed to retrieve calculated -- __init__.py:refresh_calculated:225
    2015-10-07 15:39:40,531 DEBUG    Luxtronic2   cycle takes 0.05545401573181152 seconds -- __init__.py:_refresh:276
    2015-10-07 15:39:40,984 DEBUG    Scheduler    Luxtronic2 next time: 2015-10-07 15:40:40+02:00 -- scheduler.py:_next_time:289
    Nach einem Neustart von sh klappt die Abfrage wieder ganz normal.
    Im Plugin war der Abfrageintervall auf 5min.(cycle=300)
    Mit diesem Wert kam der Fehler ca. 10mal am Tag. Nach Änderung auf cycle=60 nur mehr 2-3mal.

    Die __init__.py
    Code:
    #!/usr/bin/env python3
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #
    # Copyright 2012-2013 KNX-User-Forum e.V.       https://knx-user-forum.de/
    #
    #  This file is part of SmartHome.py.    http://mknx.github.io/smarthome/
    #
    #  SmartHome.py is free software: you can redistribute it and/or modify
    #  it under the terms of the GNU General Public License as published by
    #  the Free Software Foundation, either version 3 of the License, or
    #  (at your option) any later version.
    #
    #  SmartHome.py 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 SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
    #
    
    import sys
    import logging
    import socket
    import threading
    import struct
    import time
    import datetime
    
    logger = logging.getLogger('')
    
    
    class luxex(Exception):
        pass
    
    
    class LuxBase():
    
        def __init__(self, host, port=8888):
            self.host = host
            self.port = int(port)
            self._sock = False
            self._lock = threading.Lock()
            self.is_connected = False
            self._connection_attempts = 0
            self._connection_errorlog = 60
            self._params = []
            self._attrs = []
            self._calc = []
    
        def get_attribute(self, identifier):
            return self._attrs[identifier] if identifier < len(self._attrs) else None
    
        def get_parameter(self, identifier):
            return self._params[identifier] if identifier < len(self._params) else None
    
        def get_calculated(self, identifier):
            return self._calc[identifier] if identifier < len(self._calc) else None
    
        def get_attribute_count(self):
            return len(self._attrs)
    
        def get_parameter_count(self):
            return len(self._params)
    
        def get_calculated_count(self):
            return len(self._calc)
    
        def connect(self):
            self._lock.acquire()
            try:
                self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self._sock.settimeout(4)
                self._sock.connect((self.host, self.port))
            except Exception as e:
                self._connection_attempts -= 1
                if self._connection_attempts <= 0:
                    logger.error(
                        'Luxtronic2: could not connect to {0}:{1}: {2}'.format(self.host, self.port, e))
                    self._connection_attempts = self._connection_errorlog
                return
            finally:
                self._lock.release()
            logger.info(
                'Luxtronic2: connected to {0}:{1}'.format(self.host, self.port))
            self.is_connected = True
            self._connection_attempts = 0
    
        def close(self):
            self.is_connected = False
            try:
                self._sock.close()
                self._sock = False
            except:
                pass
    
        def _request(self, request, length):
            if not self.is_connected:
                raise luxex("no connection to luxtronic.")
            try:
                self._sock.send(request)
            except Exception as e:
                self._lock.release()
                self.close()
                raise luxex("error sending request: {0}".format(e))
            try:
                answer = self._sock.recv(length)
            except socket.timeout:
                self._lock.release()
                raise luxex("error receiving answer: timeout")
            except Exception as e:
                self._lock.release()
                self.close()
                raise luxex("error receiving answer: {0}".format(e))
            return answer
    
        def _request_more(self, length):
            try:
                return self._sock.recv(length)
            except socket.timeout:
                self._lock.release()
                raise luxex("error receiving payload: timeout")
            except Exception as e:
                self._lock.release()
                self.close()
                raise luxex("error receifing payload: {0}".format(e))
    
        def set_param(self, param, value):
            param = int(param)
    #       old = self._params[param] if param < len(self._params) else 0
            payload = struct.pack('!iii', 3002, int(param), int(value))
            self._lock.acquire()
            answer = self._request(payload, 8)
            self._lock.release()
            if len(answer) != 8:
                self.close()
                raise luxex("error receiving answer: no data")
            answer = struct.unpack('!ii', answer)
            fields = ['cmd', 'param']
            answer = dict(list(zip(fields, answer)))
            if answer['cmd'] == 3002 and answer['param'] == param:
                logger.debug(
                    "Luxtronic2: value {0} for parameter {1} stored".format(value, param))
                return True
            else:
                logger.warning(
                    "Luxtronic2: value {0} for parameter {1} not stored".format(value, param))
                return False
    
        def refresh_parameters(self):
            request = struct.pack('!ii', 3003, 0)
            self._lock.acquire()
            answer = self._request(request, 8)
            if len(answer) != 8:
                self._lock.release()
                self.close()
                raise luxex("error receiving answer: no data")
            answer = struct.unpack('!ii', answer)
            fields = ['cmd', 'len']
            answer = dict(list(zip(fields, answer)))
            if answer['cmd'] == 3003:
                params = []
                for i in range(0, answer['len']):
                    param = self._request_more(2)
                    params.append(struct.unpack('!i', param)[0])
                self._lock.release()
                if len(params) > 0:
                    self._params = params
                    return True
                return False
            else:
                self._lock.release()
                logger.warning("Luxtronic2: failed to retrieve parameters")
                return False
    
        def refresh_attributes(self):
            request = struct.pack('!ii', 3005, 0)
            self._lock.acquire()
            answer = self._request(request, 8)
            if len(answer) != 8:
                self._lock.release()
                self.close()
                raise luxex("error receiving answer: no data")
            answer = struct.unpack('!ii', answer)
            fields = ['cmd', 'len']
            answer = dict(list(zip(fields, answer)))
            if answer['cmd'] == 3005:
                attrs = []
                for i in range(0, answer['len']):
                    attr = self._request_more(1)
                    attrs.append(struct.unpack('!b', attr)[0])
                self._lock.release()
                if len(attrs) > 0:
                    self._attrs = attrs
                    return True
                return False
            else:
                self._lock.release()
                logger.warning("Luxtronic2: failed to retrieve attributes")
                return False
    
        def refresh_calculated(self):
            request = struct.pack('!ii', 3004, 0)
            self._lock.acquire()
            answer = self._request(request, 12)
            if len(answer) != 12:
                self._lock.release()
                self.close()
                raise luxex("error receiving answer: no data")
            answer = struct.unpack('!iii', answer)
            fields = ['cmd', 'state', 'len']
            answer = dict(list(zip(fields, answer)))
            if answer['cmd'] == 3004:
                calcs = []
                for i in range(0, answer['len']):
                    calc = self._request_more(4)
                    calcs.append(struct.unpack('!i', calc)[0])
                self._lock.release()
                if len(calcs) > 0:
                    self._calc = calcs
                    return answer['state']
                return 0
            else:
                self._lock.release()
                logger.warning("Luxtronic2: failed to retrieve calculated")
                return 0
    
    
    class Luxtronic2(LuxBase):
        _parameter = {}
        _attribute = {}
        _calculated = {}
        _decoded = {}
        alive = True
    
        def __init__(self, smarthome, host, port=8888, cycle=60):
            LuxBase.__init__(self, host, port)
            self._sh = smarthome
            self._cycle = int(cycle)
            self.connect()
    
        def run(self):
            self.alive = True
            self._sh.scheduler.add('Luxtronic2', self._refresh, cycle=self._cycle)
    
        def stop(self):
            self.alive = False
    
        def _refresh(self):
            if not self.is_connected:
                return
            start = time.time()
            if len(self._parameter) > 0:
                self.refresh_parameters()
                for p in self._parameter:
                    val = self.get_parameter(p)
                    if val:
                        self._parameter[p](val, 'Luxtronic2')
            if len(self._attribute) > 0:
                self.refresh_attributes()
                for a in self._attribute:
                    val = self.get_attribute(a)
                    if val:
                        self._attribute[a](val, 'Luxtronic2')
            if len(self._calculated) > 0 or len(self._decoded) > 0:
                self.refresh_calculated()
                for c in self._calculated:
                    val = self.get_calculated(c)
                    if val:
                        self._calculated[c](val, 'Luxtronic2')
                for d in self._decoded:
                    val = self.get_calculated(d)
                    if val:
                        self._decoded[d](self._decode(d, val), 'Luxtronic2')
            cycletime = time.time() - start
            logger.debug("cycle takes {0} seconds".format(cycletime))
    
        def _decode(self, identifier, value):
            if identifier == 95:
                return (datetime.datetime.fromtimestamp(value).strftime('%m.%d.%y %H:%M'))
            if identifier == 96:
                return (datetime.datetime.fromtimestamp(value).strftime('%m.%d.%y %H:%M'))
            if identifier == 97:
                return (datetime.datetime.fromtimestamp(value).strftime('%m.%d.%y %H:%M'))
            if identifier == 98:
                return (datetime.datetime.fromtimestamp(value).strftime('%m.%d.%y %H:%M'))
            if identifier == 99:
                return (datetime.datetime.fromtimestamp(value).strftime('%m.%d.%y %H:%M'))
            if identifier == 118:
                if value == 0:
                    return 'Test'
                if value == 1:
                    return 'Waermepumpe steht'
                if value == 2:
                    return 'Test2'
                if value == 3:
                    return 'Test3'
                return '???'
            if identifier == 120:
                if value == 0:
                    return 'Heizbetrieb'
                if value == 1:
                    return 'Keine Anforderung'
                if value == 2:
                    return 'Netz- Einschaltverzoegerung'
                if value == 3:
                    return 'SSP Zeit'
                if value == 4:
                    return 'Sperrzeit'
                if value == 5:
                    return 'Brauchwasser'
                if value == 6:
                    return 'Estrich Programm'
                if value == 7:
                    return 'Abtauen'
                if value == 8:
                    return 'Pumpenvorlauf'
                if value == 9:
                    return 'Thermische Desinfektion'
                if value == 10:
                    return 'Kuehlbetrieb'
                if value == 12:
                    return 'Schwimmbad'
                if value == 13:
                    return 'Heizen Ext.'
                if value == 14:
                    return 'Brauchwasser Ext.'
                if value == 16:
                    return 'Durchflussueberwachung'
                if value == 17:
                    return 'ZWE Betrieb'
                return '???'
            if identifier == 10:
                return float(value) / 10
            if identifier == 11:
                return float(value) / 10
            if identifier == 12:
                return float(value) / 10
            if identifier == 15:
                return float(value) / 10
            if identifier == 19:
                return float(value) / 10
            if identifier == 20:
                return float(value) / 10
            if identifier == 151:
                return float(value) / 10
            if identifier == 152:
                return float(value) / 10
            return value
    
        def parse_item(self, item):
            if 'lux2' in item.conf:
                d = item.conf['lux2']
                d = int(d)
                self._decoded[d] = item
            if 'lux2_a' in item.conf:
                a = item.conf['lux2_a']
                a = int(a)
                self._attribute[a] = item
            if 'lux2_c' in item.conf:
                c = item.conf['lux2_c']
                c = int(c)
                self._calculated[c] = item
            if 'lux2_p' in item.conf:
                p = item.conf['lux2_p']
                p = int(p)
                self._parameter[p] = item
                return self.update_item
    
        def update_item(self, item, caller=None, source=None, dest=None):
            if caller != 'Luxtronic2':
                self.set_param(item.conf['lux2_p'], item())
    
    
    def main():
        try:
            lux = LuxBase('192.168.1.11')
            lux.connect()
            if not lux.is_connected:
                return 1
            start = time.time()
            lux.refresh_parameters()
            lux.refresh_attributes()
            lux.refresh_calculated()
            cycletime = time.time() - start
            print("{0} Parameters:".format(lux.get_parameter_count()))
            for i in range(0, lux.get_parameter_count()):
                print("  {0} = {1}".format(i + 1, lux.get_parameter(i)))
            print("{0} Attributes:".format(lux.get_attribute_count()))
            for i in range(0, lux.get_attribute_count()):
                print("  {0} = {1}".format(i + 1, lux.get_attribute(i)))
            print("{0} Calculated:".format(lux.get_calculated_count()))
            for i in range(0, lux.get_calculated_count()):
                print("  {0} = {1}".format(i + 1, lux.get_calculated(i)))
            print("cycle takes {0} seconds".format(cycletime))
    
        except Exception as e:
            print("[EXCEPTION] error main: {0}".format(e))
            return 1
        finally:
            if lux:
                lux.close()
    
    if __name__ == "__main__":
        sys.exit(main())
    Kann mir jemand von euch Profis einen Tipp geben, woran das liegt, oder wie man den Fehler behebt?

    LG Max
    Zuletzt geändert von Max2612; 08.10.2015, 09:27.

    #2
    Hallo!

    Ich habe die letzten 3 Tage von meinen Windows- Rechner die Wärmepumpe im Sekundentakt "gepingt". Während der 3 Tage lief das Plugin problemlos - keine Ausfälle.
    Nach dem Abstellen der pings - kamen die Ausfälle wieder.

    Kann man daraus etwas gewinnen? Tipps?

    LG Max

    Kommentar


      #3
      Es sind IMHO einige Fehler im Code vorhanden die die try ... except ... finally Konstrukte betreffen. Das führt dazu, das bei einem Fehler ggf. die Verbindung als geöffnet eingetragen wird, auch wenn das gar nicht der Fall ist. Leider habe ich keine Luxtronic hier um das zu prüfen. Aber das return hinter dem finally solltest Du ergänzen und ggf. das self.is_connected = False in den try Block verschieben.

      Beispiel:
      Code:
          def connect(self):
              self._lock.acquire()
              try:
                  self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                  self._sock.settimeout(4)
                  self._sock.connect((self.host, self.port))
              except Exception as e:
                  self._connection_attempts -= 1
                  if self._connection_attempts <= 0:
                      logger.error(
                          'Luxtronic2: could not connect to {0}:{1}: {2}'.format(self.host, self.port, e))
                      self._connection_attempts = self._connection_errorlog
                  self._lock.release()
                  return
      
              self._lock.release()
              logger.info(
                  'Luxtronic2: connected to {0}:{1}'.format(self.host, self.port))
              self.is_connected = True
              self._connection_attempts = 0
      
          def close(self):
              try:
                  self._sock.close()
                  self._sock = False
                  self.is_connected = False
              except:
                  pass
      Gruß,
      Bernd
      Zuletzt geändert von bmx; 15.10.2015, 16:38.

      Kommentar


        #4
        Hallo!

        Danke Bernd, für die Tipps. Ich werde das morgen testen und berichten.

        LG Max

        Kommentar


          #5
          Hallo,

          ich habe die __init__.py wie gesagt geändert. Jedoch wird zwar das Plugin geladen, aber keine Verbindung aufgebaut.
          Code:
          2015-10-15 09:45:06,619 DEBUG    Main         SQLite: database integrity ok -- __init__.py:__init__:78
          2015-10-15 09:45:06,651 DEBUG    Main         SQLite pack next time: 2015-10-16 03:02:00+02:00 -- scheduler.py:_next_time:289
          2015-10-15 09:45:06,657 DEBUG    Main         Plugin: uzsu -- plugin.py:__init__:43
          2015-10-15 09:45:06,715 INFO     Main         Init UZSU -- __init__.py:__init__:36
          2015-10-15 09:45:06,719 DEBUG    Main         Plugin: smarttv -- plugin.py:__init__:43
          2015-10-15 09:45:07,038 DEBUG    Main         Plugin: luxtronic2 -- plugin.py:__init__:43
          Sonst kommt folgende Meldung.
          Code:
          2015-10-14 20:30:21,696 DEBUG    Main         Plugin: luxtronic2 -- plugin.py:__init__:43
          2015-10-14 20:30:21,809 INFO     Main         Luxtronic2: connected to 192.168.1.11:8888 -- __init__.py:connect:85
          Für Hilfe wär ich sehr dankbar.
          LG Max

          Kommentar


            #6
            Hast Du das Plugin mal Standalone ohne Smarthome.py laufen lassen?

            Kommentar


              #7
              Ja, aber ohne Erfolg.

              Code:
              sudo python /usr/smarthome/plugins/luxtronic2/__init__.py
              Es kommt zwar keine Fehlermeldung aber auch sonst nichts. Es werden keine Werte angezeigt.

              Kommentar


                #8
                Ist die IP Deiner Luxtronic2 denn auch 192.168.1.11 wie im Sourcecode oben angegeben?

                Und man liest zur Luxtronic des öfteren, das die abschmiert und dann nicht erreichbar ist.
                Hast Du mal mit Ping versucht, ob die auch sichtbar ist?
                GGf. mal reset durchgeführt?

                Kommentar


                  #9
                  IP stimmt, ist fix vergeben.
                  Parallel zum testen läuft auch der Webserver. Der ist auch erreichbar, wenn sh.py die Verbindung verliert.

                  Ich hatte schon die Netzwerkverbindung in Verdacht. Deshalb hatte ich 3 Tage lang im Sekundentakt die Luxtronic gepingt. Alle Pingversuche waren OK. Und während der 3 Tage lief auch das Plugin fehlerfrei.

                  Eventuell ein Firmware- Up/Downgrade der Luxtronic versuchen?!?!

                  Kommentar


                    #10
                    Welche FW-Version hast du denn drauf ?? Bei der V 1.76 hat sich nämlich der Port auf 8889 geändert
                    greetz Benni

                    Kommentar


                      #11
                      Zitat von MrDuFF Beitrag anzeigen
                      Welche FW-Version hast du denn drauf ?? Bei der V 1.76 hat sich nämlich der Port auf 8889 geändert

                      Ich habe V1.73.
                      Das Problem ist aber gelöst, denke ich zumindest.
                      Vor ca. 4 Wochen habe ich das CAT-5 Kabel getauscht. Seitdem läufts perfekt.

                      Aber danke für den Tipp!!

                      Kommentar


                        #12
                        Mit der Firmware 1.73 funktionierte mein Luxtronik2-Plugin einwandfrei. Nach dem Update auf die Firmware 1.76 erhielt ich nur noch "0"-Werte. Ich änderte dann den Port von 8888 auf 8889. Das Plugin stürzte ab:

                        Code:
                        2015-11-21 19:49:12 ERROR    Luxtronic2   Method Luxtronic2 exception: error receiving answer: no data
                        Traceback (most recent call last):
                          File "/usr/local/smarthome/lib/scheduler.py", line 344, in _task
                            obj()
                          File "/usr/local/smarthome/plugins/luxtronic2/__init__.py", line 465, in _refresh
                            self.refresh_parameters()
                          File "/usr/local/smarthome/plugins/luxtronic2/__init__.py", line 345, in refresh_parameters
                            raise luxex("error receiving answer: no data")
                        plugins.luxtronic2.luxex: error receiving answer: no data
                        Mein plugin.conf sieht wie folgt aus:

                        Code:
                        [luxtronic2]
                            class_name = Luxtronic2
                            class_path = plugins.luxtronic2
                            host = 192.168.10.212
                            port = 8889
                        Wie kann ich mit der Firmware 1.76 wieder die Daten auslesen ?

                        Gruss

                        Matthias

                        Kommentar


                          #13
                          Hallo!

                          In der __init__.py kommt mehrmals der Eintrag "port=8888" vor.
                          Vielleicht musst du diese Einträge ebenfalls durch 8889 ersetzen.

                          Lg Max

                          Kommentar


                            #14
                            Hallo,

                            der Eintrag "port" in plugin.conf überschreibt die Default-Werte im __init__.py. Das Problem liegt in der Routine "_request" beim Befehl "self._sock.recv":

                            Code:
                                def _request(self, request, length):
                                    if not self.is_connected:
                                        raise luxex("no connection to luxtronic.")
                                    try:
                                        self._sock.send(request)
                                    except Exception as e:
                                        self._lock.release()
                                        self.close()
                                        raise luxex("error sending request: {0}".format(e))
                                    try:
                                        answer = self._sock.recv(length)
                                    except socket.timeout:
                                        self._lock.release()
                                        raise luxex("error receiving answer: timeout")
                                    except Exception as e:
                                        self._lock.release()
                                        self.close()
                                        raise luxex("error receiving answer: {0}".format(e))
                                    return answer
                            In der Routine "refresh_attributes" wird die obige Funktion mit dem Befehl 3005 aufgerufen. In 9 von 10 Fällen kommt mit der Firmware 1.76 die Antwort mit 8 Bytes nicht in einem Block an, sondern es treffen zuerst nur 4 Bytes ein. Ich habe nun testweise die Routine "_request" so erweitert, dass ein 2.Block empfangen wird, falls der erste Block zu kurz war. Nun läuft die Kommunikation wieder.

                            Gruss

                            Matthias

                            Kommentar


                              #15
                              Was muss ich da genau umprogrammieren? Bin leider nicht so Python bewandert.
                              Danke.

                              Edit: Hat sich erlegt. Seit Update der WP auf 1.77 geht wieder alles wie gewohnt.
                              Zuletzt geändert von Polecat; 29.12.2015, 21:48.
                              Gruß
                              Christian

                              Kommentar

                              Lädt...
                              X