Ankündigung

Einklappen
Keine Ankündigung bisher.

Plugin: Viessmann Heizung auslesen (Vitodens200 / 300 mit Vitotronic 200 (HO1))

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

    Plugin: Viessmann Heizung auslesen (Vitodens200 / 300 mit Vitotronic 200 (HO1))

    Hallo zusammen,
    in einem anderen Thread kam die Frage nach meinem viessmann Plugin. Daher möchte ich es hier gern vorstellen. Ich bin selbst mit den vcontrold und dem vclient nicht zurechtgekommen, daher habe ich mich dann selbst ans Werk gemacht und habe ein eigenes Plugin geschrieben. Es ist natürlich auf meine Heizung und Konfiguration zugeschnitten aber ich denke es kann auch anderen helfen eine Viessmann Heizung auszulesen.

    Zunächst an der Stelle vielen Dank an die Seite https://github.com/openv/openv über die ich die meisten Informationen bezogen habe.
    Leider nicht alle, es musst auch einiges "ertüftelt" werden.

    Ich habe eine Vitodens 343F (Gas-Brennwertkessel mit witterungsgeführter Kessel- und Heizkreisregelung für 2 Heizkreise). Hiervon ist einer mit Mischer und einer ohne.

    Der folgende Screeshot zeigt, was ich alles auslese. Dies habe ich gemacht, da ich viele Probleme im Vorfeld mit der Heizung hatte und nun immer up to date bleiben wollte, ob es der Heizung gut geht oder nicht.

    screenshot.jpg

    Als Auslesegerät verwende ich den USB Optolink von der folgenden Seite, der hier auch kostengünstig bestellt werden kann.
    https://github.com/openv/openv/wiki/Bauanleitung-USB

    Mit diesem habe ich dann mal mit einem Windows System einen ersten Test gemacht. Hierzu habe ich das Programm vControl verwendet.
    https://github.com/openv/openv/wiki/v-control

    Schaut man sich hier den Log an, sieht man schon in etwa, wie die Heizung kommuniziert.
    Code:
    22-11-2016 13:42:22 DATA Init
    22-11-2016 13:42:22 COMM Init auf COM3
    22-11-2016 13:42:22 DATA TX: 16 0 0
    22-11-2016 13:42:23 COMM Comm Port OK
    22-11-2016 13:42:23 COMM Verbunden: COM3
    22-11-2016 13:42:24 DATA Lese: Version
    22-11-2016 13:42:24 DATA TX: 41 5 0 1 0 F8 2 0
    22-11-2016 13:42:24 DATA RX: 6 41 7 1 1
    22-11-2016 13:42:24 DATA RX: 0 F8 2 20 CB EE
    22-11-2016 13:42:24 SYS  Version: VScotHO1
    22-11-2016 13:42:25 DATA  Lese Daten
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 8 8A 4 9C
    22-11-2016 13:42:25 DATA RX: 6 41
    22-11-2016 13:42:25 DATA RX: 9 1 1 8 8A 4 80
    22-11-2016 13:42:25 DATA RX: 5 1 0 27
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 55 25 2 82
    22-11-2016 13:42:25 DATA RX: 6
    22-11-2016 13:42:25 DATA RX: 41 7 1 1 55 25
    22-11-2016 13:42:25 DATA RX: 2 70 0 F5
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 55 27 2 84
    22-11-2016 13:42:25 DATA RX: 6 41 7 1
    22-11-2016 13:42:25 DATA RX: 1 55 27 2 61 0
    22-11-2016 13:42:25 DATA RX: E8
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 55 5A 2 B7
    22-11-2016 13:42:25 DATA RX: 6 41 7 1
    22-11-2016 13:42:25 DATA RX: 1 55 5A 2 32 0
    22-11-2016 13:42:25 DATA RX: EC
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 8 10 2 20
    22-11-2016 13:42:25 DATA RX: 6
    22-11-2016 13:42:25 DATA RX: 41 7 1 1 8 10
    22-11-2016 13:42:25 DATA RX: 2 8D 2 B2
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 8 8 2 18
    22-11-2016 13:42:25 DATA RX: 6 41 7
    22-11-2016 13:42:25 DATA RX: 1 1 8 8 2 51 2
    22-11-2016 13:42:25 DATA RX: 6E
    22-11-2016 13:42:25 DATA TX: 41 5 0 1 8 96 2 A6
    22-11-2016 13:42:25 DATA RX: 6
    22-11-2016 13:42:25 DATA RX: 41 7 1 1 8 96
    22-11-2016 13:42:25 DATA RX: 2 C8 0 71
    Der folgende Block gibt Auskunft über das Protokoll und die Heizung selbst:

    Code:
    22-11-2016 13:42:24 DATA Lese: Version
    22-11-2016 13:42:24 DATA TX: 41 5 0 1 0 F8 2 0
    22-11-2016 13:42:24 DATA RX: 6 41 7 1 1
    22-11-2016 13:42:24 DATA RX: 0 F8 2 20 CB EE
    22-11-2016 13:42:24 SYS  Version: VScotHO1
    Ein Blick in die Geräteliste https://github.com/openv/openv/wiki/Ger%C3%A4te zeigt, dass es sich um die folgende Heizung handelt:

    20 CB = VScotHO1, 300,KW, Vitodens200 / 300 mit Vitotronic 200 (HO1), Gas-Brennwertkessel mit witterungsgeführter Kessel- und Heizkreisregelung für 2 Heizkreise mit Mischer und 1 Heizkreis ohne Mischer

    Um die Nutzdaten genauer zu verstehen lohnt sich ein Blick in den Telegrammaufbau, der ebenfalls auf der Seite openv beschrieben ist:
    https://github.com/openv/openv/wiki/Protokoll-300

    Das Schwierige ist es später herauszufinden, wie die Datenpunkte genau heißen, die abgefragt werden sollen. Auch hier gibt es Listen auf der nun viel zitierten Seite. Allerdings habe ich hier nicht alles finden können und musste mir das über die verschiedensten Seiten zusammensuchen.
    https://github.com/openv/openv/wiki/Adressen

    Nun aber zum eigentlichen Plugin. Es funktioniert genau für meine Heizung und läuft schon sehr lange absolut reibungslos. Das USB Interface habe ich mit einem Link versehen, so dass ich es immer unter dem Namen "ttyUSB_HEIZUNG" ansprechen kann. Hier muss dann natürlich euer Device entsprechend eingetragen sein.

    Wichtig zu verstehen ist, dass dieses Plugin aktuell nur Daten liest. Es ist etwas vorbereitet für das Schreiben, allerdings habe ich hier nicht weitergemacht. Ich wollte meine Zirkulationspumpe manuell starten. Das ist aber wohl bei dem Modell nicht möglich. Schade, ansonsten hätte ich es morgens einfach eingeschaltet, wenn ich auch warmes Wasser brauche und nicht einfach nach der integrierten Zeitschaltuhr.

    Das Plugin ermittelt die zugehörigen items selbst. Diese haben ein paar zusätzliche Parameter. Somit ist es dem Plugin egal, wie die Items in euren Config Files benannt werden. Die zusätzlichen Parameter sind ebenfalls kommentiert. Wichtig ist hier noch der Parameter "pluginreduction". Hiermit kann man die Abfrage untersetzen. So wird dieser Wert nicht bei jeder Abfrage abgefragt.

    Beispiel für die item:
    Code:
    [ZAEHLER]
        [[HEIZUNG]]
            [[[AUSSENTEMPERATUR]]]
                # Information Kessel Außentemperatur read 2-Byte -60..60 0x5525
                # DATA TX: 41 5 0 1 55 25 2 82
                # DATA RX: 41 7 1 1 55 25 2 EF 0 74 --> 00EF = 239 --> 23.9°C (Faktor 0.1)
                # --> Senden   41 5 0 1 55 25 2 82
                #              -- - - - ----- - --
                #               | | | |   |   |  +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]5+0+1+55+25+2 = [dez]5+0+1+(5x16)+5+(2x16)+5+2 = 130dez = 82hex
                #               | | | |   |   +----- XX Anzahl der Bytes, die in der Antwort erwartet werden
                #               | | | |   +--------- XX XX 2 byte Adresse der Daten oder Prozedur
                #               | | | +------------- XX 01 = ReadData, 02 = WriteData, 07 = Function Call
                #               | | +--------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
                #               | +----------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
                #               +------------------- Telegramm-Start-Byte
                #
                # --> Empfangen 6 41 7 1 1 55 25 2 EF 0 74
                #               - -- - - - ----- - ---- --
                #               |  | | | |   |   |   |   +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]7+1+1+55+25+2+EF+0 = [dez]7+1+1+(5x16)+5+(2x16)+5+2+(14*16)+(15*16)+0 = [dez]7+1+1+(80)+5+(32)+5+2+(224)+(15)+0 = 372dez = 1.74hex)
                #               |  | | | |   |   |   +------ Wert
                #               |  | | | |   |   +---------- XX Anzahl der Bytes, die in der Antwort erwartet werden
                #               |  | | | |   +-------------- XX XX 2 byte Adresse der Daten oder Prozedur
                #               |  | | | +------------------ XX 01 = ReadData, 02 = WriteData, 07 = Function Call
                #               |  | | +-------------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
                #               |  | +---------------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
                #               |  +------------------------ Telegramm-Start-Byte
                #               +--------------------------- OK (Antwort auf 0x16 0x00 0x00 und auf korrekt empfangene Telegramme)
                #
                # --> Antwort: 0x00EF = 239 = 23.9°
                name = Aussentemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                knx_dpt = 9
                knx_send = 5/2/0                  # Temperatur auf den Bus schreiben für die Plus-Taster
               pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = aussentemperatur    # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = signed             # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000155250282# Ende direkt hinter dem Byte String
    Die vollständige Liste meiner Items hänge ich auch mal an. Da kann man ggf. die Codes entnehmen.
    Code:
    # -----------------------------------------------------------------------
    # Items für die Heizung
    # -----------------------------------------------------------------------
    [ZAEHLER]
        [[HEIZUNG]]
            [[[READ]]]
                name = Alle Werte sofort auslesen
                type = bool
                value = False
                autotimer = 1 = False
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                pluginfct = readtrigger           # Funktion, dieses items
            [[[SET_ZIRKULATION]]]
                name = Zirkulationspumpe ein/ausschalten
                type = bool
                value = False
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                pluginfct = setcirculationpump    # Funktion, dieses items
            [[[LAST_READ]]]
                name = Letzter Lesevorgang
                type = str
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                pluginfct = lastreadtime          # Funktion, dieses items
            [[[ALARMMESSAGE]]]
                name = Heizung Alarmmeldung
                type = bool
                visu_acl = rw
                value = False
            [[[AUSSENTEMPERATUR]]]
                # Information Kessel Außentemperatur read 2-Byte -60..60 0x5525
                # DATA TX: 41 5 0 1 55 25 2 82
                # DATA RX: 41 7 1 1 55 25 2 EF 0 74 --> 00EF = 239 --> 23.9°C (Faktor 0.1)
                # --> Senden   41 5 0 1 55 25 2 82
                #              -- - - - ----- - --
                #               | | | |   |   |  +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]5+0+1+55+25+2 = [dez]5+0+1+(5x16)+5+(2x16)+5+2 = 130dez = 82hex
                #               | | | |   |   +----- XX Anzahl der Bytes, die in der Antwort erwartet werden
                #               | | | |   +--------- XX XX 2 byte Adresse der Daten oder Prozedur
                #               | | | +------------- XX 01 = ReadData, 02 = WriteData, 07 = Function Call
                #               | | +--------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
                #               | +----------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
                #               +------------------- Telegramm-Start-Byte
                #
                # --> Empfangen 6 41 7 1 1 55 25 2 EF 0 74
                #               - -- - - - ----- - ---- --
                #               |  | | | |   |   |   |   +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]7+1+1+55+25+2+EF+0 = [dez]7+1+1+(5x16)+5+(2x16)+5+2+(14*16)+(15*16)+0 = [dez]7+1+1+(80)+5+(32)+5+2+(224)+(15)+0 = 372dez = 1.74hex)
                #               |  | | | |   |   |   +------ Wert
                #               |  | | | |   |   +---------- XX Anzahl der Bytes, die in der Antwort erwartet werden
                #               |  | | | |   +-------------- XX XX 2 byte Adresse der Daten oder Prozedur
                #               |  | | | +------------------ XX 01 = ReadData, 02 = WriteData, 07 = Function Call
                #               |  | | +-------------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
                #               |  | +---------------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
                #               |  +------------------------ Telegramm-Start-Byte
                #               +--------------------------- OK (Antwort auf 0x16 0x00 0x00 und auf korrekt empfangene Telegramme)
                #
                # --> Antwort: 0x00EF = 239 = 23.9°
                name = Aussentemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                knx_dpt = 9
                knx_send = 5/2/0                  # Temperatur auf den Bus schreiben für die Plus-Taster
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = aussentemperatur    # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = signed             # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000155250282# Ende direkt hinter dem Byte String
            [[[AUSSENTEMPERATUR_GEDAEMPFT]]]
                # Information Kessel Außentemperatur gedaempft read 2-Byte -60..60 0x557
                # DATA TX: 41 5 0 1 55 27 2 84
                # DATA RX: 41 7 1 1 55 27 2 F5 0 7C --> 00F5 = 245 --> 24.5°C (Faktor 0.1)
                name = Aussentemperatur gedaempft [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = aussentempgedaempft # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = signed             # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000155270284# Ende direkt hinter dem Byte String
            [[[BRENNERSTATUS]]]
                # Brenner eingesachaltet
                name = Brennerstatus
                type = bool
                cache = True
                eval = True if (sh.ZAEHLER.HEIZUNG.BRENNER_LEISTUNG() > 0.0) else False
                eval_trigger = ZAEHLER.HEIZUNG.BRENNER_LEISTUNG
                [[[[ALTER]]]]
                    # Brenner eingesachaltet
                    name = Dauer des aktuellen Brennerstatus
                    type = num
                    # Berechnung minütlich
                    crontab = * * * * = 1  # crontab Minütlich - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = (sh.ZAEHLER.HEIZUNG.BRENNERSTATUS.age() / 60)
                [[[[PREV_ALTER]]]]
                    # Brenner eingesachaltet
                    name = Dauer des Brennerstatus zuvor
                    type = num
                    # Berechnung minütlich
                    crontab = * * * * = 1  # crontab Minütlich - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = (sh.ZAEHLER.HEIZUNG.BRENNERSTATUS.prev_age() / 60)
            [[[BRENNERSTARTS]]]
                # Information Kessel Brennerstarts read 4-Byte 0..64000 0x088A
                # DATA TX: 41 5 0 1 8 8A 4 9C
                # DATA RX: 41 9 1 1 8 8A 4 48 F0 0 0 D9 --> 0000F048 = 61512 --> 61512 (Faktor 1)
                name = Brennerstarts
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = brennerstarts       # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001088A049C# Ende direkt hinter dem Byte String
                [[[[LETZTE_STUNDE]]]]
                    name = Brennertstarts innerhalb der letzten Stunde
                    type = num
                    cache = True
                    # Berechnung stündlich
                    crontab = 0 * * * = 1  # crontab Stündlich - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round((sh.ZAEHLER.HEIZUNG.BRENNERSTARTS() - sh.ZAEHLER.HEIZUNG.BRENNERSTARTS.db('min', '1h')), 1)
                [[[[LETZTER_TAG]]]]
                    name = Brennertstarts innerhalb des letzten Tages
                    type = num
                    cache = True
                    database@mysqldb = init
                    # Berechnung täglich
                    crontab = 0 0 * * = 1     # crontab Täglich 0 Uhr - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round((sh.ZAEHLER.HEIZUNG.BRENNERSTARTS() - sh.ZAEHLER.HEIZUNG.BRENNERSTARTS.db('min', '1d')), 1)
            [[[ANLAGEN_LEISTUNG]]]
                # Brenner Leistung
                # DATA TX: 41 5 0 1 A3 8F 2 3A
                # DATA RX: 41 7 1 1 A3 8F 2 0 0 3D  --> 0000 = 0 --> Aus (Faktor 0.1)
                name = Anlagenleistung
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = anlagenleistung     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001A38F023A# Ende direkt hinter dem Byte String
            [[[BRENNER_LEISTUNG]]]
                # Brenner Leistung
                # DATA TX: 41 5 0 1 A3 05 2 B0
                # DATA RX: 41 7 1 1 A3 05 2 0 0 B3  --> 0000 = 0 --> Aus (Faktor 0.1)
                name = Brennerleistung
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = brennerleistung     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001A30502B0# Ende direkt hinter dem Byte String
            [[[BETRIEBSSTUNDEN]]]
                # Information Kessel Brenner Betriebsstunden read 4-Byte 0..64000 0x08A7
                # DATA TX: 41 5 0 1 8 A7 4 B9
                # DATA RX: 41 9 1 1 8 A7 4 CE 47 DD 0 B0 --> 00DD47CE = 14501838 --> 4028,28h (Faktor 1/3600)
                name = Betriebsstunden [h]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = betriebsstunden     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 3600               # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000108A704B9# Ende direkt hinter dem Byte String
                [[[[LETZTE_STUNDE]]]]
                    name = Betriebsstunden innerhalb der letzten Stunde
                    type = num
                    cache = True
                    # Berechnung stündlich
                    crontab = 0 * * * = 1  # crontab Stündlich - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round((sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN() - sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN.db('min', '1h')), 1)
                [[[[LETZTER_TAG]]]]
                    name = Betriebsstunden innerhalb des letzten Tages
                    type = num
                    cache = True
                    database@mysqldb = init
                    # Berechnung täglich
                    crontab = 0 0 * * = 1     # crontab Täglich 0 Uhr - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round((sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN() - sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN.db('min', '1d')), 1)
            [[[BETRIEB_ZU_STARTS]]]
                name = Brennertstarts / Betriebsstunden gesamt
                type = num
                cache = True
                # Berechnung bei Änderung
                eval = round((sh.ZAEHLER.HEIZUNG.BRENNERSTARTS() / sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN()), 1)
                eval_trigger = ZAEHLER.HEIZUNG.BRENNERSTARTS | ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN
                [[[[LETZTE_STUNDE]]]]
                    name = Brennertstarts / Betriebsstunden innerhalb der letzten Stunde
                    type = num
                    cache = True
                    # Berechnung stündlich
                    crontab = 0 * * * = 1  # crontab Stündlich - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round(((sh.ZAEHLER.HEIZUNG.BRENNERSTARTS() - sh.ZAEHLER.HEIZUNG.BRENNERSTARTS.db('min', '1h')) / (sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN() - sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN.db('min', '1h'))), 1)
                [[[[LETZTER_TAG]]]]
                    name = Brennertstarts / Betriebsstunden innerhalb des letzten Tages
                    type = num
                    cache = True
                    database@mysqldb = init
                    # Berechnung täglich
                    crontab = 0 0 * * = 1     # crontab Täglich 0 Uhr - Aufbau: mm(0..59) hh day(0..28) wday(0=Mo, 1=Di, 2=Mi, 3=Do, 4=Fr, 5=Sa, 6=So)
                    enforce_updates = True    # Updates True, da der Crontab immer mit 1 ausführt und die Berechnung durchgeführt werden soll.
                    eval = round(((sh.ZAEHLER.HEIZUNG.BRENNERSTARTS() - sh.ZAEHLER.HEIZUNG.BRENNERSTARTS.db('min', '1d')) / (sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN() - sh.ZAEHLER.HEIZUNG.BETRIEBSSTUNDEN.db('min', '1d'))), 1)
            [[[KESSELSOLLTEMPERATUR]]]
                # Diagnose Kessel Kesselsolltemperatur read 2-Byte 0..127 0x555A
                # DATA TX: 41 5 0 1 55 5A 2 B7
                # DATA RX: 41 7 1 1 55 5A 2 32 0 EC --> 0032 = 50 --> 5.0°C (Faktor: 0.1)
                name = Kesselsolltemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = kesselsolltemp      # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001555A02B7# Ende direkt hinter dem Byte String
            [[[KESSELTEMPERATUR]]]
                # Information Kessel Kesseltemperatur read 2-Byte 0..150 0x0810
                # DATA TX: 41 5 0 1 8 10 2 20
                # DATA RX: 41 7 1 1 8 10 2 7C 1 A0 --> 017C = 380 --> 38.0°C (Faktor: 0.1)
                name = Kesseltemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = kesseltemp          # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000108100220# Ende direkt hinter dem Byte String
            [[[ABGASTEMPERATUR]]]
                # Abgastemperatur read 2-Byte 0..500 0x0816
                # DATA TX: 41 5 0 1 8 16 2 26
                # DATA RX: 41 7 1 1 8 16 2 7C 1 A6 --> 017C = 380 --> 38.0°C (Faktor: 0.1)
                name = Abgastemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = abgastemp           # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000108160226# Ende direkt hinter dem Byte String
            [[[WARMWASSERSOLLTEMPERATUR]]]
                # Bedienung HK1 Betriebsdaten HK1 Warmwassersolltemperatur write 1-Byte 10 60 (90) 0x6300
                # DATA TX: 41 5 0 1 63 0 1 6A
                # DATA RX: 41 7 1 1 63 0 1 36 A2 --> 36 = 54 --> 54°C (Faktor: 1)
                name = Warmwassertemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = warmwassersolltemp  # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 410500016300016A# Ende direkt hinter dem Byte String
            [[[WARMWASSERISTTEMPERATUR]]]
                # Information Warmwasser Temperatur Speicher-/ Lade-/ Komfortsensor read 2-Byte 0 150 0x0812
                # DATA TX: 41 5 0 1 8 12 2 22
                # DATA RX: 41 7 1 1 8 12 2 1F 2 46 --> 021F = 543 --> 54.3°C (Faktor: 0.1)
                name = Warmwassertemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = warmwasseristtemp   # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000108120222# Ende direkt hinter dem Byte String
            [[[WARMWASSERAUSTRITTSTEMPERATUR]]]
                # Information Warmwasser Austrittstemperatur read 2-Byte 0 150 0x0814
                # DATA TX: 41 5 0 1 8 14 2 24
                # DATA RX: 41 7 1 1 8 14 2 1F 2 48 --> 021F = 543 --> 54.3°C (Faktor: 0.1)
                name = Warmwasseraustrittstemperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = warmwasserausttemp  # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 4105000108140224# Ende direkt hinter dem Byte String
            [[[BETRIEBSART]]]
                [[[[HK1]]]]
                    # Information Heizkreis HK1 Aktuelle Betriebsart read 1-Byte 0 3 0x2500
                    # VALUE: 0 - Abschaltbetr. (Dauernd)   0x00
                    # VALUE: 1 - Red. Betrieb  (Schaltuhr) 0x01
                    # VALUE: 2 - Normalbetrieb (Schaltuhr) 0x02
                    # VALUE: 3 - Normalbetrieb (Dauernd)   0x03
                    # DATA TX: 41 5 0 1 25 0 1 2C
                    # DATA RX: 41 7 1 1 25 0 1 2 31 --> 02 = 2 --> Normalbetrieb (Schaltuhr)
                    name = Betriebasart 0..3
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk1betriebsart      # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 410500012500012C# Ende direkt hinter dem Byte String
                [[[[HK2]]]]
                    # Information Heizkreis HK2 Aktuelle Betriebsart read 1-Byte 0 3 0x2500
                    # VALUE: 0 - Abschaltbetr. (Dauernd)   0x00
                    # VALUE: 1 - Red. Betrieb  (Schaltuhr) 0x01
                    # VALUE: 2 - Normalbetrieb (Schaltuhr) 0x02
                    # VALUE: 3 - Normalbetrieb (Dauernd)   0x03
                    # DATA TX: 41 5 0 1 35 0 1 3C
                    # DATA RX: 41 7 1 1 35 0 1 2 41 --> 02 = 2 --> Normalbetrieb (Schaltuhr)
                    name = Betriebasart 0..3
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk2betriebsart      # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 410500013500013C# Ende direkt hinter dem Byte String
            [[[HEIZART]]]
                [[[[HK1]]]]
                    # Information Heizkreis HK1 Aktuelle Heizart read 1-Byte 0 3 0x2323
                    # VALUE: 0 - Abschaltbetrieb         0x00
                    # VALUE: 1 - Nur Warmwasser          0x01
                    # VALUE: 2 - Heizen und Warmwasser   0x02
                    # VALUE: 3 - Normalbetrieb (Dauernd) 0x03
                    # DATA TX: 41 5 0 1 23 23 1 4D
                    # DATA RX: 41 7 1 1 23 23 1 2 52 --> 02 = 2 --> Heizen und Warmwasser
                    name = Betriebasart 0..3
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk1heizsart         # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 410500012323014D# Ende direkt hinter dem Byte String
                [[[[HK2]]]]
                    # Information Heizkreis HK2 Aktuelle Heizart read 1-Byte 0 3 0x3323
                    # VALUE: 0 - Abschaltbetrieb         0x00
                    # VALUE: 1 - Nur Warmwasser          0x01
                    # VALUE: 2 - Heizen und Warmwasser   0x02
                    # VALUE: 3 - Normalbetrieb (Dauernd) 0x03
                    # DATA TX: 41 5 0 1 33 23 1 5D
                    # DATA RX: 41 7 1 1 33 23 1 2 62 --> 02 = 2 --> Heizen und Warmwasser
                    name = Betriebasart 0..3
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk2heizsart         # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 410500013323015D# Ende direkt hinter dem Byte String
            [[[VORLAUFSOLLTEMPERATUR]]]
                [[[[HK1]]]]
                    # Diagnose Heizkreis HK1 Vorlaufsolltemperatur read 2-Byte 0 127 0x2544
                    # DATA TX: 41 5 0 1 25 44 2 71
                    # DATA RX: 41 7 1 1 25 44 2 0 0 74 --> 0000 = 0 --> 0.0°C (Faktor: 0.1)
                    name = Vorlaufsolltemperatur HK1 [°C]
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk1vorlaufsolltemp  # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 4105000125440271# Ende direkt hinter dem Byte String
                [[[[HK2]]]]
                    # Diagnose Heizkreis HK2 Vorlaufsolltemperatur read 2-Byte 0 127 0x3544
                    # DATA TX: 41 5 0 1 35 44 2 81
                    # DATA RX: 41 7 1 1 35 44 2 0 0 84 --> 0000 = 0 --> 0.0°C (Faktor: 0.1)
                    name = Vorlaufsolltemperatur HK1 [°C]
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk2vorlaufsolltemp  # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 4105000135440281# Ende direkt hinter dem Byte String
            [[[VORLAUFISTTEMPERATUR]]]
                [[[[HK1]]]]
                    # Diagnose Heizkreis HK1 Vorlaufisttemperatur read 2-Byte 0 127 0x2544
                    # DATA TX: 41 5 0 1 29 0 2 31
                    # DATA RX: 41 7 1 1 29 0 2 0 034 --> 0000 = 0 --> 0.0°C (Faktor: 0.1)
                    name = Vorlaufisttemperatur HK1 [°C]
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk1vorlaufisttemp   # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 4105000129000231# Ende direkt hinter dem Byte String
                [[[[HK2]]]]
                    # Diagnose Heizkreis HK2 Vorlaufisttemperatur read 2-Byte 0 127 0x3544
                    # DATA TX: 41 5 0 1 39 0 2 41
                    # DATA RX: 41 7 1 1 39 0 2 0 0 44 --> 0000 = 0 --> 0.0°C (Faktor: 0.1)
                    name = Vorlaufisttemperatur HK1 [°C]
                    type = num
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk2vorlaufisttemp   # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                    pluginvcode = 4105000139000241# Ende direkt hinter dem Byte String
            [[[PUMPE]]]
                [[[[HK1]]]]
                    # Diagnose Heizkreis HK1 Umwälzpumpe read 1-Byte 0 1 0x2906
                    # DATA TX: 41 5 0 1 29 06 1 36
                    # DATA RX: 41 7 1 1 29 06 1 1 3A --> 1 --> TRUE
                    name = Umwälzpumpe HK1
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk1umwaelzpumpe     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 4105000129060136# Ende direkt hinter dem Byte String
                [[[[HK2]]]]
                    # Diagnose Heizkreis HK2 Umwälzpumpe read 1-Byte 0 1 0x2906
                    # DATA TX: 41 5 0 1 39 06 1 46
                    # DATA RX: 41 7 1 1 39 06 1 1 4A --> 1 --> TRUE
                    name = Umwälzpumpe HK1
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = hk2umwaelzpumpe     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 4105000139060146# Ende direkt hinter dem Byte String
                [[[[WARMWASSER]]]]
                    # Information Warmwasser Speicherladepumpe read 1-Bit 0 1 0x6513
                    # DATA TX: 41 5 0 1 65 13 1 7F
                    # DATA RX: 41 7 1 1 65 13 1 1 83 --> 1 --> TRUE
                    name = Warmwasserpumpe
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = warmwasserpumpe     # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 410500016513017F# Ende direkt hinter dem Byte String
                [[[[ZIRKULATION]]]]
                    # Zirkulationspumpe read 1-Byte TRUE/FALSE 0x6515
                    # DATA TX: 41 5 0 1 65 15 1 81
                    # DATA RX: 41 7 1 1 65 15 1 0 83  --> 0 --> FALSE
                    name = Zirkulations Pumpe
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = ausgang28_zirkpumpe # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 4105000165150181# Ende direkt hinter dem Byte String
                [[[[SOLAR]]]]
                    # Solarpumpe read 1-Byte TRUE/FALSE 0x6552
                    # DATA TX: 41 5 0 1 65 52 1 BE
                    # DATA RX: 41 7 1 1 65 52 1 1 C2 --> 1 --> TRUE
                    name = Solar Pumpe
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = solarpump           # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 41050001655201BE# Ende direkt hinter dem Byte String
                [[[[INTERN]]]]
                    # Interne Pumpe
                    # DATA TX: 41 5 0 1 76 60 1 DD
                    # DATA RX: 41 7 1 1 76 60 1 1 E1 --> 1 --> TRUE
                    name = Interne Pumpe
                    type = bool
                    cache = True
                    database@mysqldb = init
                    pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                    plugintagid = intern              # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                    plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                    pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                    pluginvcode = 41050001766001DD# Ende direkt hinter dem Byte String
            [[[SOLARKOLLEKTORTEMPERATUR]]]
                # Solar Kollektortemperatur read 2-Byte -50..240 0x6564
                # DATA TX: 41 5 0 1 65 64 2 D1
                # DATA RX: 41 7 1 1 65 64 2 2 0 D6 --> 0002 = 2 --> 0.2°C (Faktor 0.1)
                name = Solarkollektor Temperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = solarkollektortemp  # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = signed             # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001656402D1# Ende direkt hinter dem Byte String
            [[[SOLARSPEICHERTEMPERATUR]]]
                # Solar Speichertemperatur read 2-Byte 0..130 0x6566
                # DATA TX: 41 5 0 1 65 66 2 D3
                # DATA RX: 41 7 1 1 65 66 2 2 0 D8 --> 0002 = 2 --> 0.2°C (Faktor 0.1)
                name = Solarspeicher Temperatur [°C]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = solarspeichertemp   # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001656602D3# Ende direkt hinter dem Byte String
            [[[SOLARBETRIEBSSTUNDEN]]]
                # Solar Betriebsstunden read 2-Byte 0..64000 0x6568
                # DATA TX: 41 5 0 1 65 68 2 D5
                # DATA RX: 41 9 1 1 65 68 2 F 0 E7 --> 000F = 15 --> 15 (Faktor 1)
                name = Solar Betriebsstunden [h]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = solarbetriebsstunden# Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001656802D5# Ende direkt hinter dem Byte String
            [[[SOLARWAERMEMENGE]]]
                # Solar Wärmemenge read 2-Byte 0..64000 0x6560
                # DATA TX: 41 5 0 1 65 60 2 CD
                # DATA RX: 41 9 1 1 65 60 2 F 0 DF --> 000F = 15 --> 15kWh (Faktor 1)
                name = Solar Wärmemenge [kWh]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = solarwaermemenge    # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001656002CD# Ende direkt hinter dem Byte String
            [[[SOLARAUSBEUTE]]]
                # Solar Ausbeute heute read 4-Byte 0..1150000 0xCF30
                # DATA TX: 41 5 0 1 CF 30 4 09
                # DATA RX: 41 9 1 1 CF 30 4 F 0 0 0 1B --> 0000.000F = 15 --> 15Wh (Faktor 1)
                name = Solar Ausbeute heute [Wh]
                type = num
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = solarausbeute       # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginsigned = unsigned           # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
                pluginreduction = 1               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginfactor = 1                  # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
                pluginvcode = 41050001CF300409# Ende direkt hinter dem Byte String
            [[[FROSTGEFAHR]]]
                # Frostgefahr read 1-Byte TRUE/FALSE 0x2510
                # DATA TX: 41 5 0 1 25 10 1 3C
                # DATA RX: 41 7 1 1 25 10 1 0 3F --> 0 --> FALSE
                name = Frostgefahr
                type = bool
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = frostgefahr         # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginvcode = 410500012510013C# Ende direkt hinter dem Byte String
            [[[SAMMELSTOERUNG]]]
                # Sammelstoerung read 1-Byte TRUE/FALSE 0x0A82
                # DATA TX: 41 5 0 1 0A 82 1 93
                # DATA RX: 41 7 1 1 0A 82 1 0 96 --> 0 --> FALSE
                name = Sammelstoerung
                type = bool
                cache = True
                database@mysqldb = init
                pluginusage = viessmann           # Wird vom angegebenen Plugin verwendet
                plugintagid = sammelstoerung      # Im Plugin wird das Item im Dictionary unter dem defineirten Namen geführt. So ist es unabhängig vom Item Namen und Pfad
                plugintype = bool                 # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
                pluginreduction = 3               # Untersetzung: 1=bei jedem Aufruf den Wert auslesen, 2= bei jedem 2. Aufruf, 3=bei jedem 3. Aufruf ...
                pluginvcode = 410500010A820193# Ende direkt hinter dem Byte String    
            [[[ICON_STATUS]]]
                # 0 = Störung      Icon = control_x.svg        (rot)
                # 1 = Solarpumpe   Icon = sani_solar.svg       (weiss)
                # 2 = Brenner      Icon = sani_boiler_temp.svg (weiss)
                # 3 = Brenner aus  Icon = sani_boiler_temp.svg (weiss)
                name = Iconstatus für die Heizung
                type = num
                cache = True
                eval = 0 if (sh.ZAEHLER.HEIZUNG.SAMMELSTOERUNG() == True) else 1 if (sh.ZAEHLER.HEIZUNG.PUMPE.SOLAR() == True) else 2 if (sh.ZAEHLER.HEIZUNG.BRENNER_LEISTUNG() > 0.0) else 3
                eval_trigger = ZAEHLER.HEIZUNG.SAMMELSTOERUNG | ZAEHLER.HEIZUNG.PUMPE.SOLAR | ZAEHLER.HEIZUNG.BRENNER_LEISTUNG
    Plugin code
    \plugins\viessmann\__init__.py
    Code:
    #!/usr/bin/env python3
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #
    # Viessmann Heizung, Auswertung
    # Vitodens200 / 300 mit Vitotronic 200 (HO1)
    # Gas-Brennwertkessel mit witterungsgeführter Kessel- und Heizkreisregelung für 2 Heizkreise mit Mischer und 1 Heizkreis ohne Mischer
    # 20CB = VScotHO1
    #
    # Version | Datum      | Author | Beschreibung
    # --------|------------|--------|---------------------------------------------------------
    #    1.00 | 24.11.2016 | LC     | Initialversion
    #    1.01 | 29.12.2017 | LC     | Logger besser angepasst self.logger = logging.getLogger(__name__), initialisiert in def ___init___
    #
    #
    # Beispiel
    #
    # Senden        41 5 0 1 55 25 2 82
    # Read Request  -- - - - ----- - --
    #                | | | |   |   |  +------- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]5+0+1+55+25+2 = [dez]5+0+1+(5x16)+5+(2x16)+5+2 = 130dez = 82hex
    #                | | | |   |   +---------- XX Anzahl der Bytes, die in der Antwort erwartet werden
    #                | | | |   +-------------- XX XX 2 byte Adresse der Daten oder Prozedur
    #                | | | +------------------ XX 01 = ReadData, 02 = WriteData, 07 = Function Call
    #                | | +-------------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
    #                | +---------------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
    #                +------------------------ Telegramm-Start-Byte
    #
    # Empfangen   :  6 ----------------------- OK (Antwort auf 0x16 0x00 0x00 und auf korrekt empfangene Telegramme)                        
    #                5 ----------------------- Schnittstelle ist aktiv und wartet auf eine Initialisierung
    #               15 ----------------------- Schnittstelle meldet einen Fehler zurück
    #
    #               41 7 1 1 55 25 2 EF 0 74
    #               -- - - - ----- - ---- --
    #                | | | |   |   |   |   +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]7+1+1+55+25+2+EF+0 = [dez]7+1+1+(5x16)+5+(2x16)+5+2+(14*16)+(15*16)+0 = [dez]7+1+1+(80)+5+(32)+5+2+(224)+(15)+0 = 372dez = 1.74hex)
    #                | | | |   |   |   +------ Wert
    #                | | | |   |   +---------- XX Anzahl der Bytes, die in der Antwort erwartet werden
    #                | | | |   +-------------- XX XX 2 byte Adresse der Daten oder Prozedur
    #                | | | +------------------ XX 01 = ReadData, 02 = WriteData, 07 = Function Call
    #                | | +-------------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
    #                | +---------------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
    #                +------------------------ Telegramm-Start-Byte
    #              
    #
    # Protokollbeschreibung
    # http://openv.wikispaces.com/Protokoll+300
    
    
    import logging
    import serial
    import time
    import binascii
    
    # Plugin Klasse
    class viessmann():
    
        # Def: __init__
        # Beschreibung von: https://github.com/mknx/smarthome/wiki/Write-a-plugin-5-minutes
        # The function init is called once when smarthome.py initializes before the
        # items are loaded. Here you place the code that is needed to initialize you
        # plugin. For example you can open a serial port if you need to communicate
        # to an external device, open files, initialize other variables you need and
        # so on. The function receives the parameter “smarthome” which gives access to
        # the smarthome.py functions. You should save the value in a variable for later
        # use like in the example above. Other parameter values are received from the
        # file plugin.conf. You can default values for the case that the parameter is
        # not defined in the plugin.conf file. It is a good practice to log your plugin
        # name to the smarthome.log file.
        def __init__(self, smarthome, serialport="/dev/ttyUSB_HEIZUNG", update_cycle="900"):
            # Logger festlegen
            self.logger = logging.getLogger(__name__)
            # Eintrag in den Log
            self.logger.info('Plugin viessmann - Initialisierung gestartet')
            # Grundwerte festlegen
            self._sh = smarthome
            self._update_cycle = int(update_cycle)
            self._serial = serial.Serial(
                    port=serialport,
                    timeout=1,
                    baudrate=4800,
                    bytesize=serial.EIGHTBITS,
                    parity=serial.PARITY_EVEN,
                    stopbits=serial.STOPBITS_TWO)
            self._items = dict()
            self._lastbyte = b''
            self._lastbytetime = time.time()
            # Untersetzung: Aktueller Zähler, 0=Init, 1=bei jedem Zyklus, 2=jeden 2. Zyklus, 3=jeden 3. Zyklus
            # Der Zähler ist nur bei Systemstart = 0
            # Anschließend zählt dieser immer von 1..maxreduction
            self._actreduction = 0
            # Untersetzung: Maximaler Wert, der über die Konfig projektiert wurde. Wird dynamisch ermittelt
            # Begrenzt auch den actreduction Zähler, dieser wird läuft immer von 1..maxreduction
            self._maxreduction = 0
    
    
        # Def: run
        # Beschreibung von: https://github.com/mknx/smarthome/wiki/Write-a-plugin-5-minutes
        # The function run is called once when smarthome.py starts.
        # Run is executed after all items are loaded.
        # You need to set the variable self.alive=True here.
        def run(self):
            # Eigenen Scheduler Eintrag erzeugen und diese Logik alle 2sek
            # selbständig aufrufen, um keine Umdrehung des Zählers zu verpassen.
            self._sh.scheduler.add(
                'viessmann', self._readCyclicData, cycle=self._update_cycle)
            # Alive Variable für das System auf True setzen
            self.alive = True
    
        # Def: stop
        # Beschreibung von: https://github.com/mknx/smarthome/wiki/Write-a-plugin-5-minutes
        # This is called when smarthome.py shuts down.
        # This is the right place to close files and data connections.
        # You need to set the variable self.alive=False here.
        def stop(self):
            # Alive Variable für das System auf False setzen
            self.alive = False
            # Serielle Schnittstelle schließen
            self._serial.close()
            # Scheduler selbständig entfernen
            self._sh.scheduler.remove('viessmann')
    
        # Def: parse_item
        # Beschreibung von: https://github.com/mknx/smarthome/wiki/Write-a-plugin-5-minutes
        # The function parse_item is called for each item during startup when
        # smarthome.py reads the item.conf file. You can access item parameters
        # and start according action here. For example you have the following
        # item defined in …/items/smarthome.conf
        #
        # Items für dieses Plugin verfügen über mehrere Attribute
        # + pluginusage     = viessmann
        # + plugintagid     = z.B. brennerstarts
        # + pluginreduction = 1
        # + ...
        # Ist es ein Item für dieses Plugin so trägt es auch ein weiteres Attribut namens "plugintagid"
        # Hierüber können mehrere zu einem Plugin gehörende Items klar definiert und verwendet werden
        # ohne, dass der eigentliche Item Name/Pfad eine bestimmte Nomenklatur aufweisen müssen.
        def parse_item(self, item):
            # Prüfung ob ein Item existiert mit dem attribut "pluginusage"
            if (('pluginusage' in item.conf) and ('plugintagid' in item.conf) and ('pluginvcode' in item.conf) and ('pluginreduction' in item.conf)):
                # Prüfung ob dieses Attribut den Namen "viessmann" trägt
                if ((item.conf['pluginusage'] == 'viessmann') and ('pluginvcode' in item.conf)):
                    # String aus der Config wird in einen bytestring gewandelt
                    # Beispiel: "4105000108A704B9" --> b'\x41\x05\x00\x01\x08\xA7\x04\xB9'
                    vcodebytestring = binascii.a2b_hex(item.conf['pluginvcode'])
                    if (self._checkChecksum(vcodebytestring) == True):
                        # Speichern des Items unter seinem "plugintagid" Namen im
                        # Plugin eigenen Item-Dictionary
                        self._items[item.conf['plugintagid']] = item
                        # Maximale Untersetzung ermitteln anhand der Daten aus der Config
                        # Dient dazu den aktuellen Zähler immer wieder von 1 zu starten.
                        self._maxreduction = max(self._maxreduction, int(item.conf['pluginreduction']))
                        # Eintrag in den Log
                        self.logger.info('Plugin viessmann - Item added: {}'.format(item.conf['plugintagid']))
                    else:
                        # Checksumme für das Item (Sendecode) ist nicht korrekt
                        messagetext = 'Plugin viessmann - Falsche Checksumme für den vcode (Sendecode {}}, Item not added: {}, '.format(vcodebytestring, item.conf['plugintagid'])
                        self.logger.error(messagetext)
            elif (('pluginusage' in item.conf) and ('pluginfct' in item.conf) ):
                if ((item.conf['pluginusage'] == 'viessmann') and (item.conf['pluginfct'] == 'lastreadtime')):
                    self._itemlastread = item
                elif ((item.conf['pluginusage'] == 'viessmann') and (item.conf['pluginfct'] == 'readtrigger')):
                    return self.update_item
                elif ((item.conf['pluginusage'] == 'viessmann') and (item.conf['pluginfct'] == 'setcirculationpump')):
                    return self.update_item
            return None
    
        # Def: parse_logic
        # Nur der Vollständigkeit halber hier mit aufgelistet
        def parse_logic(self, logic):
            if 'xxx' in logic.conf:
                # self.function(logic['name'])
                pass
    
        # Def: update_item
        # Nur der Vollständigkeit halber hier mit aufgelistet
        def update_item(self, item, caller=None, source=None, dest=None):
            #if caller != 'plugin':
            self.logger.info("Plugin viessmann - Item update item: {0}".format(item.id()))
            if (('pluginusage' in item.conf) and ('pluginfct' in item.conf)):
                # Prüfung ob dieses Attribut den Namen "viessmann" trägt und es sich um das
                # Item zum erneuten Lesen handelt
                if ((item() == True) and (item.conf['pluginusage'] == 'viessmann') and (item.conf['pluginfct'] == 'readtrigger')):
                    # Damit alle Werte gelesen werden muss die Untersetzung
                    # auf den Inititialisierungswert gesetzt werden.
                    self._actreduction = 0
                    # Lese Funktion manuell ausführen
                    self._readCyclicData()
                    # Rücksetzen des Trigger Items
                    item('off')
                if ((item.conf['pluginusage'] == 'viessmann') and (item.conf['pluginfct'] == 'setcirculationpump')):
                    # Zirkulationspumpe schalten
                    self._writeCircPump(item())
                    # Lese Funktion manuell ausführen
                    self._readCyclicData()
    
    
        def _checkChecksum(self, bytestring):
            # Returnvalue vorbelegen (Checksumme falsch)
            returnvalue = False
            # Checksummen Variable für die Berechnung
            checksum = 0
            # Es wurde das Startbyte gefunden
            startbytefound = False
            # Sofern die Länge vom bytestring größer als
            # die Mindestlänge 2 ist, kann die Aktion
            # ausgeführt werden.
            bytestringwork = bytestring
            if ((len(bytestringwork)) > 1):
                # Bytestring Byte für Byte auswerten. Wenn nur
                # noch ein Byte übrig ist, dann stoppen. Dies
                # ist dann das Checksummen Byte
                while ((len(bytestringwork)) > 1):
                    # Erstes Byte zwischenspeichern
                    leftbyte = bytestringwork[:1]
                    # Sofern das Startbyte bereits gefunden wurde
                    # muss das aktuelle Byte mit in die Berechnung
                    # einbezogen werden.
                    if (startbytefound == True):
                        # Checksumme berechnen. Sie bildet sich aus der
                        # Aufsummierung der einzelenen Bytes
                        checksum = checksum + ord(leftbyte)
                    # Suche nach dem Startbyte b'\x41'
                    elif (leftbyte == b'\x41'):
                        # Es handelt sich um das Startbyte.
                        # Ab dem nächsten Byte handelt es sich um
                        # Nutzdaten, die mit in die Checksumme einfließen.
                        startbytefound = True
                    # Bytestring um das erste Byte verkürzen
                    bytestringwork = bytestringwork[1:]
                # Checksumme prüfen
                if ((checksum % 256) == ord(bytestringwork)):
                    # Berechnete Checksumme stimmt mit der angegebenen Checksumme überein
                    returnvalue = True
                else:
                    # Checksummen passen nicht zusammen
                    messagetext = 'Plugin viessmann - Checksumme für Bytestring ist fehlerhaft. Bytestring : {}, CRC byte : {}, CRC Berechnet : {}, CRC im String : {}'.format(bytestring, bytestringwork, checksum, ord(bytestringwork))
                    self.logger.error(messagetext)
            else:
                messagetext = 'Plugin viessmann - Checksumme kann nicht berechnet werden. Bytestring zu kurz. Bytestring : {}'.format(bytestring)
                self.logger.error(messagetext)
            # Rückgabewert der Funktion zurückgeben
            # True  = Checksumme ist okay
            # False = Checksumme falsch
            return returnvalue    
    
    
        def _readByte(self):
            self.logger.debug('Plugin viessmann - Read byte')
            # Standard Rückgabewert definieren (leeres Byte)
            returnvalue = b''
            # Es wird per Schleife x-mal gelesen
            j = 0
            while (j < 5):
                readbyte = self._serial.read(1)
                self.logger.debug('Plugin viessmann --------- Read byte: {}'.format(readbyte))
                # Prüfung, ob ein Byte empfangen wurde
                if (readbyte != b''):
                    # Ein Byte wurde empfangen. Dieses wird gespeichert
                    self._lastbyte = readbyte
                    # Zeit des Empfangs ebenfalls speichern
                    self._lastbytetime = time.time()
                    # Schleife abbrechen
                    break
                # Schleifenzähler inkrementieren
                j = j + 1
            # Wert zurückgeben. Wenn kein Byte empfangen wurde, wird b'' zurückgeliefert.
            return returnvalue
    
    
        def _writeBytes(self, bytestring):
            self.logger.debug('Plugin viessmann - Write bytestring: {}'.format(bytestring))
            # Sendepuffer leeren (Sicherheitsfunktion)
            self._serial.flushInput()
            # Übergebenen Bytestring senden
            self._serial.write(bytestring)
            # Sendepuffer leeren
            self._serial.flushInput()
    
    
        def _initCommunication(self):
            self.logger.info('Plugin viessmann - Init Communication')
            # Standard Rückgabewert definieren (Initialisierung fehlgeschlagen)
            returnvalue = False
            # Merker: Wurde der Initialisierungsstring b'\x16\x00\x00' gesendet.
            # Wird hierauf dann mit b'\x06' geantwortet ist die Komunikation aufgebaut.
            initstringsent = False
    
            # Schnittstelle zurücksetzen
            self._writeBytes(b'\x04')
            # Lesen eines Bytes
            self._readByte()
    
            # Initialisierung ca. 10mal probieren
            i = 0
            while (i < 10):
                if ((initstringsent == True) and (self._lastbyte == b'\x06')):
                    # Schnittstelle hat auf den Initialisierungsstring mit OK
                    # geantwortet. Die Abfrage von Werten kann beginnen. Diese
                    # Funktion meldet hierzu True zurück.
                    returnvalue = True
                    # Schleife abbrechen, da Initialisierung erfolgreich
                    break
                if ((self._lastbyte == b'\x06') or (self._lastbyte == b'\x05')):
                    # Schnittstelle ist zurückgesetzt und wartet auf Daten
                    # b'\x05' = Warten auf Initialisierungsstring
                    # b'\x06' = Schnittstelle initialisiert
                    # In beiden Fällen wird zur Sicherheit der Initialisierungsstring gesendet.
                    self._writeBytes(b'\x16\x00\x00')
                    # Merker setzen: Initialisierungstring wurde zuletzt gesendet
                    initstringsent = True
                elif (self._lastbyte == b'\x15'):
                    # Die Schnittstelle hat einen Fehler zurückgemeldet.
                    messagetext = 'Plugin viessmann - Die Schnittstelle hat einen Fehler zurückgemeldet (\x15), Schleifenindex {}'.format(i)
                    self.logger.error(messagetext)
                    # Schnittstelle zurücksetzen
                    self._writeBytes(b'\x04')
                    # Merker rücksetzen: Initialisierungstring wurde nicht zuletzt gesendet
                    initstringsent = False
                else:
                    # Letzter Wert ist undefiniert
                    # Schnittstelle zurücksetzen
                    self._writeBytes(b'\x04')
                    # Merker rücksetzen: Initialisierungstring wurde nicht zuletzt gesendet
                    initstringsent = False
                # Lesen eines Bytes
                self._readByte()
                # Counter hochzählen
                i = i + 1
            # Zurückgeben, ob die Initialisierung erfolgreich war
            # True  = Initialisierung erfolgreich. Letztes Byte war \x06 und nicht zu lange her
            # False = Initialisierung fehlgeschlagen.
            return returnvalue
    
    
        def _setItem(self, item, databytes):
            # Standard Rückgabewert definieren (Schreiben des Items nicht erfolgreich)
            returnvalue = False
            if (len(databytes) > 0):
                # In Abhängigkeit vom Datentyp die Datenbytes auswerten und das Item schreiben
                if (item.conf['plugintype'] == 'num'):
                    # Faktor für das Item ermitteln
                    factor = float(item.conf['pluginfactor'])
                    # Initialisierung der internen Variablen
                    longvalue = 0
                    floatvalue = 0.0
                    byteindex = 0
                    databytes_work = databytes
                    while (len(databytes_work) > 0):
                        # Erstes Byte zwischenspeichern
                        leftbyte = databytes_work[:1]
                        # Wert des Bytes ermitteln
                        value = int(ord(leftbyte))
                        # Gewichten / Multiplizieren
                        if (byteindex > 0):
                            value = int(value * pow(256, byteindex))
                        # Aufaddieren der einzelnen Bytes
                        longvalue = longvalue + value
                        # Byteindex hochzählen, damit das Byte mit
                        # netsprechend mit 1, 256, 65536, 16777216, usw.
                        # multipliziert werden kann
                        byteindex = byteindex + 1
                        # Bytestring um das erste Byte verkürzen
                        databytes_work = databytes_work[1:]
                    # Signed/Unsigned berücksichtigen
                    if (item.conf['pluginsigned'] == 'signed'):
                        if ((byteindex == 1) and (longvalue > 127)):
                            longvalue = (256 - longvalue) * (-1)
                        elif ((byteindex == 2) and (longvalue > 32767)):
                            longvalue = (65536 - longvalue) * (-1)
                        #elif ((byteindex == 4) and (longvalue > *2147483647‬)):
                        #    longvalue = (*4294967295‬ - longvalue) * (-1)
                    # Faktor berücksichtigen
                    floatvalue = round(float(longvalue / factor), 2)
                    # Item schreiben
                    item(floatvalue)
                    # Debug Meldung über das erfolgreiche Schreiben
                    self.logger.debug('Plugin viessmann - Analogwert geschrieben : {}, Float-Wert : {}, Long-Wert : {}, Rohbytes : {}'.format(item.conf['plugintagid'], floatvalue, longvalue, databytes))
                    # Item korrek geschrieben
                    returnvalue = True
                elif (item.conf['plugintype'] == 'bool'):
                    # Datenbyte Binärzustand prüfen
                    if ((databytes == b'\x01') or (databytes == b'\x03')):
                        # Item aktivieren
                        item('on')
                        # Debug Meldung über das erfolgreiche Schreiben
                        self.logger.debug('Plugin viessmann - Binärwert geschrieben : {}, Wert : {}, Rohbytes : {}'.format(item.conf['plugintagid'], 'True', databytes))
                        # Item korrek geschrieben
                        returnvalue = True
                    elif ((databytes == b'\x00') or (databytes == b'\x02')):
                        # Item deaktivieren
                        item('off')
                        # Debug Meldung über das erfolgreiche Schreiben
                        self.logger.debug('Plugin viessmann - Binärwert geschrieben : {}, Wert : {}, Rohbytes : {}'.format(item.conf['plugintagid'], 'False', databytes))
                        # Item korrek geschrieben
                        returnvalue = True
                    else:
                        messagetext = 'Plugin viessmann - Item kann nicht geschrieben werden, da Binärwert nicht klar. Read value : {}, Datenbytes : {}'.format(item.conf['plugintagid'], databytes)
                        self.logger.error(messagetext)
                else:
                    messagetext = 'Plugin viessmann - Item kann nicht geschrieben werden, da Datentyp undefiniert. Read value : {}, Datentyp : {}'.format(item.conf['plugintagid'], item.conf['plugintype'])
                    self.logger.error(messagetext)
            else:
                messagetext = 'Plugin viessmann - Item kann nicht geschrieben werden, übergebener Bytestring leer ist. Read value : {}'.format(item.conf['plugintagid'])
                self.logger.error(messagetext)
            # Rückgabewert ausgeben
            # True  : Schreiben des Items war erfolgreich
            # False : Schreiben des Items fehlerhaft
            return returnvalue
    
    
        def _readValue(self, item):
    #        self.logger.debug('Plugin viessmann - Read value : {}, Type : {}'.format(item.conf['plugintagid'], item.conf['plugintype']))
    
            # Standard Rückgabewert definieren (Lesen nicht erfolgreich)
            returnvalue = False
    
            # Übergebenen Sende Bytestring auswerten
            vcodestring = item.conf['pluginvcode']
            # String aus der Config wird in einen bytestring gewandelt
            # Beispiel: "4105000108A704B9" --> b'\x41\x05\x00\x01\x08\xA7\x04\xB9'
            vcodebytestring =  binascii.a2b_hex(vcodestring)
            # Vorletztes Byte ermitteln
            # Anfrage 41 05 00 01 55 25 02 82
            #                           --
            #                           LÄNGE
            vcodelengthbyte = vcodebytestring[-2:]
            vcodelengthbyte = vcodelengthbyte[:1]
            # Aus dem Byte den ASCII-Zahlenwert ermitteln. Diese Länge wird für den Dateninhalt der Antwort genutzt
            # Es müssen noch die Zusatzdaten mit addiert werden (diese sind immer +9)
            # Antwort 06 41 07 01 01 55 25 02 EF 00 74
            #         --                      ----- --
            #         OK                      DATEN CS
            vcode_responselen = ord(vcodelengthbyte)
            vcode_responsetotallen = vcode_responselen + 9
    
            # ByteString für den Rückgabewert
            bytestring = b''
            # Schnittstelle ist bereit. Kommando kann gesendet werden.
            vcodestring = item.conf['pluginvcode']
            # String aus der Config wird in einen bytestring gewandelt
            # Beispiel: "4105000108A704B9" --> b'\x41\x05\x00\x01\x08\xA7\x04\xB9'
            vcodebytestring = binascii.a2b_hex(vcodestring)
            self._writeBytes(vcodebytestring)
            # Rückgabebytestring einlesen
            k = 0
            while k < vcode_responsetotallen:
                # Byte von Schnittstelle lesen
                self.logger.debug('Plugin viessmann --------- Read byte for : {}, index k : {}, kmax : {}, responselen : {}, lengthbyte : {}'.format(vcodebytestring, k, vcode_responsetotallen, vcode_responselen, vcodelengthbyte))
                readbyte = self._readByte()
                # Erstes und letztes Byte muss b'\x06' sein (Bedeutung: OK der Daten)
                # Wenn dies nicht der Fall ist, wird abgebrochen
                #if ((k == 0) or (k == (vcode_responsetotallen - 1))):
                if (k == 0):
                    if not (self._lastbyte == b'\x06'):
                        # Bytestring zurücksetzen
                        bytestring = b''
                        break
                else:
                    # Schreiben des Readbyte Strings bestehend aus
                    # allen gelesenen Bytes bis auf b'\x06' am
                    # Anfang und am Ende (Bsp.: 41 07 01 01 55 25 02 EF 00 74)
                    bytestring = bytestring + self._lastbyte
                # Schleifenzähler inkrementieren
                k = k + 1
    
            self.logger.debug('Plugin viessmann --------- Bytestring : {}'.format(bytestring))
    
            # Prüfung ob alle Daten gelesen wurden. Hierzu kann geprüft werden
            # ob die Länge der Bytestring der erwarteten Länge minus die beiden
            # b'\x06' Bytes am Anfang und Ende entspricht.
            if (len(bytestring) == (vcode_responsetotallen - 1)):
                # Entspricht das erste Byte b'\x41'
                bytestring_extract = bytestring[:1]
                if (bytestring_extract == b'\x41'):
                    # Bytestring startet mit dem Identifier für den Telegramstart
                    # Mit dem String kann weiter gearbeitet werden
                    # Prüfung ob die Checksumme in Ordnung ist
                    if (self._checkChecksum(bytestring) == True):
                        # Gelesene Bytes aus dem Array auswerten
                        # Datenbytes extrahieren
                        bytestring_extract = bytestring[(((-1) * vcode_responselen) - 1):]
                        bytestring_extract = bytestring_extract[:vcode_responselen]
    #                    messagetext = 'Plugin viessmann - Datenbytes extrahiert. Read value : {}, Databytes : {}'.format(item.conf['plugintagid'], bytestring_extract)
    #                    self.logger.debug(messagetext)
                        # Wert aus den datenbytes ermitteln und mit Faktor in das Item schreiben
                        self._setItem(item, bytestring_extract)
                        # Wert wurde erfolgreich gelesen und das Item geschrieben
                        returnvalue = True
                    else:
                        messagetext = 'Plugin viessmann - Checksumme fehlerhaft. Read value : {}, Readed bytestring : {}'.format(item.conf['plugintagid'], bytestring)
                        self.logger.error(messagetext)
                else:
                    messagetext = 'Plugin viessmann - Gelesener Bytestring startet nicht mit \x41. Read value : {}, Readed bytestring : {}'.format(item.conf['plugintagid'], bytestring)
                    self.logger.error(messagetext)
            else:
                messagetext = 'Plugin viessmann - Gelesener Bytestring hat nicht die korrekte Länge. Read value : {}, Erwartete Länge : {}, Readed bytestring : {}'.format(item.conf['plugintagid'], (vcode_responsetotallen - 1), bytestring)
                self.logger.error(messagetext)
    
            # Rückgabe, ob der Wert erfolgreich gelesen und das Item geschrieben wurde
            return returnvalue
    
        # Schreiben der Zirkulationspumpe
        # Dummy: Wert wird nicht geschrieben. Dient nur dazu zu prüfen, ob die Funktion aufgerufen wird, wenn das Item sich ändert
        #        Soll der Wert tatsächlich geschrieben werden, so muss die vollständige Implementierung entsprechend umbenannt werden.
        #        Siehe direkt hierunter
        #        _writeCircPump() --> _writeCircPump_DUMMY() und _writeCircPump_DEACTIVE() --> _writeCircPump()
        def _writeCircPump(self, seton):
            self.logger.info('Plugin viessmann - WRITE TEST (Wert wird nicht geschrieben) - Write circulation pump : {}'.format(seton))
    
            def _writeCircPump_DEACTIVE(self, seton):
            self.logger.info('Plugin viessmann - WRITE - Write circulation pump : {}'.format(seton))
    
            # Standard Rückgabewert definieren (Lesen nicht erfolgreich)
            returnvalue = False
    
            if (seton == True):
                # Code on
                vcodestring = "410600026515010386"
            else:
                vcodestring = "410600026515010285"
    
            # String aus der Config wird in einen bytestring gewandelt
            # Beispiel: "410600026515010184" --> b'\x41\x06\x00\x02\x65\x15\x01\x01\x84'
            vcodebytestring =  binascii.a2b_hex(vcodestring)
            # Vorvorletztes Byte ermitteln
            # Anfrage 41 06 00 02 65 15 01 03 86
            #                           --
            #                           LÄNGE
            vcodelengthbyte = vcodebytestring[-3:]
            vcodelengthbyte = vcodelengthbyte[:1]
            # Aus dem Byte den ASCII-Zahlenwert ermitteln. Diese Länge wird für den Dateninhalt der Antwort genutzt
            # Es müssen noch die Zusatzdaten mit addiert werden (diese sind immer +9)
            # Antwort 06 41 06 01 02 65 15 01 01 85
            #         --                   -- -- --
            #         OK                      DA CS
            vcode_responselen = ord(vcodelengthbyte)
            vcode_responsetotallen1 = vcode_responselen + 9
            vcode_responsetotallen = 10
    
            # ByteString für den Rückgabewert
            bytestring = b''
            # Schnittstelle ist bereit. Kommando kann gesendet werden.
            vcodestring = vcodestring
            # String aus der Config wird in einen bytestring gewandelt
            # Beispiel: "410600026515010184" --> b'\x41\x06\x00\x02\x65\x15\x01\x01\x84'
            vcodebytestring = binascii.a2b_hex(vcodestring)
            self.logger.error('Plugin viessmann - WRITE - --------- Write zirkulation, sende bytestring : {}, erwartete antwortlänge : {}'.format(vcodebytestring, vcode_responsetotallen1))
            self._writeBytes(vcodebytestring)
            # Rückgabebytestring einlesen
            k = 0
            while k < vcode_responsetotallen:
                # Byte von Schnittstelle lesen
                self.logger.debug('Plugin viessmann - WRITE - --------- Write zirkulation, Read byte for : {}, index k : {}, kmax : {}, responselen : {}, lengthbyte : {}'.format(vcodebytestring, k, vcode_responsetotallen, vcode_responselen, vcodelengthbyte))
                readbyte = self._readByte()
                self.logger.debug('Plugin viessmann - WRITE - --------- Write zirkulation, Readed byte : {}'.format(readbyte))
                # Erstes und letztes Byte muss b'\x06' sein (Bedeutung: OK der Daten)
                # Wenn dies nicht der Fall ist, wird abgebrochen
                #if ((k == 0) or (k == (vcode_responsetotallen - 1))):
                if (k == 0):
                    if not (self._lastbyte == b'\x06'):
                        # Bytestring zurücksetzen
                        bytestring = b''
                        break
                else:
                    # Schreiben des Readbyte Strings bestehend aus
                    # allen gelesenen Bytes bis auf b'\x06' am
                    # Anfang und am Ende (Bsp.: 41 06 01 02 65 15 01 01 85)
                    bytestring = bytestring + self._lastbyte
                # Schleifenzähler inkrementieren
                k = k + 1
            self.logger.debug('Plugin viessmann - WRITE - --------- Write zirkulation, Empfangs bytestring : {}'.format(bytestring))
    
            # Prüfung ob alle Daten gelesen wurden. Hierzu kann geprüft werden
            # ob die Länge der Bytestring der erwarteten Länge minus die beiden
            # b'\x06' Bytes am Anfang und Ende entspricht.
            if (len(bytestring) == (vcode_responsetotallen - 1)):
                # Entspricht das erste Byte b'\x41'
                bytestring_extract = bytestring[:1]
                if (bytestring_extract == b'\x41'):
                    # Bytestring startet mit dem Identifier für den Telegramstart
                    # Mit dem String kann weiter gearbeitet werden
                    # Prüfung ob die Checksumme in Ordnung ist
                    if (self._checkChecksum(bytestring) == True):
                        # Gelesene Bytes aus dem Array auswerten
                        # Datenbytes extrahieren
                        bytestring_extract = bytestring[(((-1) * vcode_responselen) - 1):]
                        bytestring_extract = bytestring_extract[:vcode_responselen]
                        messagetext = 'Plugin viessmann - WRITE - Datenbytes extrahiert. Read Databytes : {}'.format(bytestring_extract)
                        self.logger.info(messagetext)
                        # Wert wurde erfolgreich gelesen und das Item geschrieben
                        returnvalue = True
                    else:
                        messagetext = 'Plugin viessmann - WRITE - Checksumme fehlerhaft. Readed bytestring : {}'.format(bytestring)
                        self.logger.error(messagetext)
                else:
                    messagetext = 'Plugin viessmann - WRITE - Gelesener Bytestring startet nicht mit \x41. Readed bytestring : {}'.format(bytestring)
                    self.logger.error(messagetext)
            else:
                messagetext = 'Plugin viessmann - WRITE - Gelesener Bytestring hat nicht die korrekte Länge. Erwartete Länge : {}, Readed bytestring : {}'.format((vcode_responsetotallen - 1), bytestring)
                self.logger.error(messagetext)
    
            # Rückgabe, ob der Wert erfolgreich gelesen und das Item geschrieben wurde
            return returnvalue
    
        # Def: _readCyclicData(zyklische Funktion)
        # Zu Beginn werden alle Werte ausgelesen, anschließend mit entsprechenden Untersetzungen
        # die wichtigen und die weniger wichtigen Daten
        def _readCyclicData(self):
            # Alle Einträge aus dem Distionary durchlaufen und für jedes Item prüfen,
            # ob dieses in dem Zyklus (Untersetzung) ausgelesen werden soll.
            for name in sorted(self._items.keys(), reverse=True):
                item = self._items[name]
                # Prüfung ob dieses Item in dem Zyklus ausgwertet werden soll
                if ((self._actreduction % int(item.conf['pluginreduction'])) == 0):
                    self.logger.debug('Plugin viessmann')
                    self.logger.debug('Plugin viessmann - Reduction act: {} - Read item: {}'.format(self._actreduction, item.conf['plugintagid']))
                    # Lesen
                    readsuccess = False
                    # 1. Versuch den Wert von der Heizung zu lesen
                    if ((time.time() - 500) > self._lastbytetime):
                        # Der letzte Lesevorgang ist länger als 500ms her.
                        # Daher wird die Schnittstelle zur Sicherheit initialisiert.
                        self._initCommunication()
                    # Lesen der Daten (1. versuch)
                    readsuccess = self._readValue(item)
                    if (readsuccess == False):
                        # Erster Leseversuch hat nicht funktioniert
                        # Initialisierung der Schnittstelle vornehmen
                        inited = self._initCommunication()
                        # Prüfung, ob die Initialisierung erfolgreich war
                        if (inited == True):
                            # 2. Versuch den Wert von der Heizung zu lesen
                            readsuccess = self._readValue(item)
                    if (readsuccess == False):
                        # Wert konnte nicht gelesen oder das Item nicht geschrieben werden
                        messagetext = 'Plugin viessmann - Wert konnte nicht gelesen und Item geschrieben werden. Read value : {}, Schnittstelle initialisiert : {}'.format(item.conf['plugintagid'], readsuccess)
                        self.logger.error(messagetext)
                    else:
                        # Korrekt gelesen und Item geschrieben
                        messagetext = 'Plugin viessmann - Wert korrekt gelesen und Item geschrieben. Read value : {}'.format(item.conf['plugintagid'])
                        self.logger.info(messagetext)
    
    
            # Untersetzungscounter um 1 hochzählen
            self._actreduction = self._actreduction + 1
            # Sofern der Untersetzungscounter größer ist als der Maximalwert,
            # wird dieser auf 1 zurückgesetzt
            if self._actreduction > self._maxreduction:
                self._actreduction = 1
    
            # Letzte Zeit vom Lesen setzen
            timestring = '' + time.strftime('%d.%m.%Y %H:%M:%S')
            self._itemlastread(timestring)
    Zum Schluss muss das Plugin dann noch in smarthome eingebettet und aufgerufen werden. Das passiert natürlich über die plugin.yaml

    plugin.yaml
    Code:
    viessmann:
        class_name: viessmann
        class_path: plugins.viessmann
        serialport: /dev/ttyUSB_HEIZUNG
        update_cycle: 300
    Das ist schon alles einige Zeit her. Ich denke einiges ist beschrieben. Die Fragen kommen sicherlich beim Probieren. Vielleicht kann ich dann helfen. Ansonsten einfach mal ausprobieren, wer es mag.

    Gruß
    loeserman
    Zuletzt geändert von loeserman; 14.01.2019, 21:32. Grund: Links überarbeitet

    #2

    Zuletzt geändert von loeserman; 29.12.2017, 21:32. Grund: Post wieder gelöscht, da Änderungen direkt im ersten Eintrag mit eingebracht.

    Kommentar


      #3
      Spitzenleistung, da werde ich mich morgen mal dransetzen!

      Zur Info für dich und alle, die das hier lesen: da wikispaces teurer wurde, ist das Projekt umgezogen nach https://github.com/openv/openv/ - allerdings sind nicht alle Daten vollständig übernommen; insbesondere angehängte Dateien fehlen teilweise (leider). Diese sind bis Ende des kommenden Jahres noch bei wikispaces (und danach ggf. bei web.archive.org) zu finden.

      Wenn ich morgen mein Plugin (hoffentlich) erstmal fertig habe, schaue ich mir deins mal intensiver an und melde mich dann nochmal

      Kommentar


        #4
        Da hast du dir ja richtig Arbeit gemacht... Respekt.

        Ausprobiert habe ich es jetzt noch nicht, zum Einen, weil während des Testens meine "normale" Datensammlung (Aufzeichnung der Temperaturkurven) aussetzt, zum Anderen, weil ich erst die Kontrollsequenzen rausfinden müsste.

        Mein erster Gedanke beim Lesen war "Hey cool, der hat sich den Protokollparser gespart." - der zweite war "Verdammt, der hat sich den Protokollparser gespart..."

        Ich denke mal, dass für die meisten Interessierten das das Argument sein könnte, das Plugin zu nutzen oder nicht zu nutzen. So richtig portabel ist das mit den Binärcodes nicht - auf der anderen Seite ist eine Konfiguration mit getrennter Befehlskonfiguration wie in vcontrold sicher schön, für so ein Plugin aber wahrscheinlich zu aufwändig.

        Was hältst du denn von der (grundsätzlichen) Idee, den P300-Protokollparser ins Plugin einzubauen und in der Konfig stattdessen den Datenpunkt (also die Adresse, Anzahl der Bytes und Datentyp) anzugeben? Mir ist schon klar, dass du das nicht brauchst, deine Konfiguration läuft ja Wenn der Parser entsprechend steht, wird sicher auch die Schreib-Funktionalität wesentlich einfacher umzusetzen sein...



        Kommentar


          #5
          Machen kann man einiges, allerdings weiß ich noch nicht so ganz genau, was Du Dir wünschen würdest?

          Betrachten wir das Beispiel:
          Code:
          # --> Senden   41 5 0 1 55 25 2 82
          #              -- - - - ----- - --
          #               | | | |   |   |  +-- Prüfsumme (Summe über alley Bytes ohne die 41; [hex]5+0+1+55+25+2 = [dez]5+0+1+(5x16)+5+(2x16)+5+2 = 130dez = 82hex
          #               | | | |   |   +----- XX Anzahl der Bytes, die in der Antwort erwartet werden
          #               | | | |   +--------- XX XX 2 byte Adresse der Daten oder Prozedur
          #               | | | +------------- XX 01 = ReadData, 02 = WriteData, 07 = Function Call
          #               | | +--------------- XX 00 = Anfrage, 01 = Antwort, 03 = Fehler
          #               | +----------------- Länge der Nutzdaten (Anzahl der Bytes zwischen dem Telegramm-Start-Byte (0x41) und der Prüfsumme)
          #               +------------------- Telegramm-Start-Byte
          Stand jetzt würde dann in der Config folgendes angegeben werden müssen:
          Code:
          plugintype = num                  # Datentyp dieses Items (num=Numerische Werte, bool=Binärwert)
          pluginsigned = signed             # Ist der Analogdatentyp mit Vorzeichen behaftet (Möglichkeiten "signed", "unsigned")
          pluginfactor = 10                 # Übertragener Integerwert muss durch den gegebenen Faktor geteilt werden bevor der Wert im Item gespeichert wird
          pluginvcode = 4105000155250282# Ende direkt hinter dem Byte String
          Was wäre nun Dein Wunsch? Du möchtest nur die Adresse "55 25", die Anzahl der Bytes für die Antwort "2" und den Datentyp "num" angeben? Der Rest ist ja eh nahezu statisch, da eigentlich meistens eine Anfrage auf ReadData gemacht wird. Und das Telegram Start Byte ist immer gleich. Die Länge der Nutzdaten könnte man errechnen und die Prüfsumme auch. Allerdings der Faktor und auch ob es signed oder unsigned ist, steht nicht dabei und muss immer manuell angegeben werden.

          Habe mir das bei vcontrold nie genauer angesehen, wie die das lösen. Stehe aber Verbesserungsvorschlägen offen gegenüber. Könnt mir ja etwas helfen.

          Das mit der Datensammlung sehe ich persönlich nicht so eng. Die Werte werden ja alle neu geholt und sind nicht inkrementell. Daher stört es mich nicht, wenn bei mir in der Aufzeichnung mal eine Lücke ist. Das einzige was mich binär interessiert sind die Brennerstarts und die Betriebsstunden, aber selbst die werden ja als Zahl geholt und müssen nicht von mir anhand eines Bools inkrementiert werden.

          Kommentar


            #6
            Da hast du an mir vorbeiverstanden (oder ich vorbeigeschrieben) - die Aufzeichnung war nur die Ausrede, mein Produktivsystem nicht vom Lesekopf abzuklemmen [COLOR=#B1B1B1 !important][/COLOR][COLOR=#999999 !important]​[/COLOR]

            Im vcontrold ist das Ganze noch komplizierter und über mehrere Konfigurationen verteilt, dafür aber ultimativ flexibel. Das bräuchte man hier sicher nicht, und eine Beschränkung auf das P300-Protokoll sollte schon reichen.

            Die Angabe der Adresse ist klar, die Datenlänge (1,2, 3, 4 Bytes) auch; der Datentyp kommt im Original von Viessmann und kann z.B. Temperatur (2 Bytes, 1/10 Auflösung), Temperatur (1 Byte, 1/2 Auflösung), Temperatur (1 Byte, 1/2 Auflösung, unsigned), Status (1 Byte char oder enum), Betriebsart (1 Byte enum), Zähler (2 Byte unsigned, Stunden), Zähler (2 Byte unsigned, Sekunden) usw. sein.

            Die Kunst bei den Heizungen ist ja, zu wissen bzw. herauszubekommen, welche Adresse welchen Nutzwert in welchem Format liefert... [COLOR=#B1B1B1 !important][/COLOR][COLOR=#999999 !important]​[/COLOR]

            Ich will auch nicht kritisieren, was du gemacht hast - die Leseroutine mit Byteparsing finde ich klasse! - , aber ich denke an eine potenzielle Umsteigerklientel, die vcontrold zugunsten des direkten Zugriffs per Plugin aufgeben könnten. Da wäre es wahrscheinlich attraktiv, die Parameter direkt als Adressen anzugeben (zumal diese Daten teilweise auch von Viessmann zur Verfügung gestellt werden).

            Und die tatsächlich zu sendende Bytefolge zu berechnen kann die Maschine besser und schneller, auch wenn sie es jedesmal wieder tun muss [COLOR=#B1B1B1 !important][/COLOR][COLOR=#999999 !important]​[/COLOR]

            Ich kann mich ja mal an einen generischen Parser ransetzen, der aus den angegeben Daten Bytestring-Datagramme bastelt und andersherum... kann aber nicht versprechen, wie schnell das geht. Immerhin hab ich noch ein halbes Wochenende [COLOR=#B1B1B1 !important][/COLOR][COLOR=#999999 !important]​[/COLOR]

            Achso - und wünschen würde ich mir erstmal nix, mein Setup läuft ja [COLOR=#B1B1B1 !important][/COLOR][COLOR=#999999 !important]​[/COLOR] Mir fallen nur immer viele Ideen ein, was man noch umsetzen könnte. Nicht immer ist der erwartete Mehrwert mehr als die reingesteckte Arbeit... hihi

            Kommentar


              #7
              Danke für die Antwort und keine Sorge, ich sehe das auch nicht als Kritik. Wie gesagt freue ich mich über Feedback und wenn es vielen hilft. Habe leider immer nur abends etwas Zeit und daher komme ich bei solchen Weiterentwicklungen immer nur schwer voran. Aber das kennst Du sicherlich. Können ja gemeinsam versuchen diesen Parser zu integrieren. Wenn Du da etwas vorbereitest, können wir ihn ja versuchen entsprechend einzubetten.

              Nicht immer ist der erwartete Mehrwert mehr als die reingesteckte Arbeit...
              Das ist ja so üblich bei einem Hobby ;-) Daran soll es nicht scheitern.

              Kommentar


                #8
                Hallo,

                gibt es hier ein Update oder anders gefragt, wer nutzt das Plugin?

                Michael

                Kommentar


                  #9
                  Hallo,

                  ich bin gerade dabei, das bestehende Plugin in der o.g. Version in meiner Visu einzubauen.
                  Brauch aber noch ein paar Tage bis ich so weit bin. Muss erst noch den Lesekopf bauen.

                  Dann kann ich mal berichten.

                  Grüße Martin

                  Kommentar


                    #10
                    Hallo loeserman,
                    leider funktionieren die Links auf "wikispaces" nicht mehr. Ist es Dir möglich, die Dateien in ein anderes Ziel zu verlinken?
                    Danke
                    Hans

                    Kommentar


                      #11
                      Hallo,

                      das meiste der o.g. Links und viele Informationen zur Anbindung der Viessmann Heizung ist unter https://github.com/openv/openv/wiki zu finden.

                      Grüße

                      Kommentar


                        #12
                        Hallo zusammen,
                        danke für den Hinweis mit den Links. Ich habe diese mal überarbeitet im ersten Post. bamus007 hatte es aber schon richtig erkannt. Die Seite war einfach nur umgezogen. Hatte damals noch einen PDF von Viessmann gefunden. Mal sehen, ob ich hierzu auch noch einen Link finde.

                        Bei mir läuft das Plugin noch reibungslos. Daher gern einfach probieren. Wenn es dann zu Problemen kommt, dann gucken wir mal. Ist zwar schon etwas her aber irgendwie bekommen wir das schon hin.

                        Gruß
                        loeserman

                        Kommentar


                          #13
                          Hatte über die folgende Seite damals noch Datenpunktlisten gefunden für den Einsatz der VitoGate.
                          https://connectivity.viessmann.com/d.../vitogate.html

                          Allerdings scheinen sie das nicht mehr frei rauszugeben. Habe nur noch diese gefunden:
                          https://www.google.com/url?sa=t&rct=...01GtFR9ke3glWO

                          Kommentar


                            #14
                            Hallo,

                            gibt es hier ein Update oder anders gefragt, wer nutzt das Plugin?
                            Ich möchte es auch verwenden, um endlich meine Viessmann Heizung mit anzubinden.

                            Beste Grüße

                            Kommentar


                              #15
                              Hi sisamiwe,
                              nein es gibt kein Update. Nutze es immer noch so. Läuft nun schon ewig ohne Probleme.

                              Gruß
                              Christoph

                              Kommentar

                              Lädt...
                              X