Ankündigung

Einklappen
Keine Ankündigung bisher.

ComfoAir Steuerung über RS232

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

    Hallo Moritz

    Ich sehe aus deinem Post/Log ein Problem mit der Kommunikation bzw. der Daten die empfangen werden. Hier muss ich die Auswertung im Plugin noch "robuster" machen. Gewisse Probleme könnte man relativ einfach im Plugin abfangen.

    Ich schaue mal wie ich das Auswerten noch verbessern kann.

    Wiso aber senden nicht mehr geht ist mir gerade ein absolutes Rätsel!? Ich suche mal ob ich was finde. Aber du sagst, dass du auch vorher nicht mehr senden konntest, bevor ich den Workaround gestern für Torsten eingestellt habe?

    EDIT: Zur behebung oder besser gesagt zur besseren erkennung des Endzeichens auch wenn sich noch falsche Zeichen eingeschlichen haben (Bei dir dort zusehen, wo der String bis auf 0 dezimiert wird) ändere mal folgende Kleinigket...

    In der Zeile 281:

    Code:
    $reciv = substr($reciv,0,-4)
    zu
    Code:
    $reciv = substr($reciv,0,-[COLOR=Red]2[/COLOR])
    Das sollte die Dezimierung auf 0 verhindern und zu mindest ein Teil der Checksummenfehler beheben.

    Weitere kleine Verbesserungen werden folen, bis die Kommunikation wieder zufriedenstellen funktioniert. Da bin ich aber auf eure Mithilfe angewiesen, da ich keine KWL zum testen habe Am Ende gibts daraus eine neue Version für alle
    Gruss Patrik alias swiss

    Kommentar


      Soo....

      Hier mal eine Modifizierte Version die mit folgenden Fehlern nun besser umgehen können sollte:

      - Falsche Zeichen am ende des Datenstrings werden nun herausgefiltert
      - Mehrere Telegramme in einem Datenstring -> Hier nur das erste Datenpaket auswerten
      - Falsche Startzeichen werden nun entfernt

      Code:
      # Plugin zur Ansteuerung einer Zender ComfoAir
      # Version 1.6.1 05.04.2013 BETA
      # Copyright: swiss (https://knx-user-forum.de/members/swiss.html)
      # Aufbau moeglichst so, dass man unterhalb der Einstellungen nichts veraendern muss!
      # - Neu mit der Moeglichkeit zur Anbindung über einen Moxa NPort von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
      # - Neustrukturierung des Codes von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
       
      
      ####################
      ###Einstellungen:###
      ####################
      
      #BITTE ab sofort die Einstellungen unter conf.d vornemen. Damit bleiben die Einstellungen auch bei einem Update erhalten.
       
      
      ######################
      ##ENDE Einstellungen##
      ######################
      
      
      #Ab hier nichts mehr aendern.
      #Hauptverarbeitung
      
      
      #Erzeuge Variablen fuer die Zuordnung der Steuerfunktionen zu den Gruppenadressen:
      my $ga_stufeabwesend = ''; #1bit Trigger fuer Stufe "Abwesend". 1=Aktivieren
      my $ga_stufe1 = ''; #1bit Trigger fuer Stufe1. 1=Aktivieren
      my $ga_stufe2 = ''; #1bit Trigger fuer Stufe2. 1=Aktivieren
      my $ga_stufe3 = ''; #1bit Trigger fuer Stufe3. 1=Aktivieren
      my $ga_komforttemp = ''; #GA DPT 9.001 zum setzen der Komforttemperatur
      my $ga_reset_filter = ''; #1bit Trigger fuer das Zuruecksetzen des Betriebsstundenzaehlers des Filters. 1=Reset
      my $ga_reset_error = ''; #1bit Trigger fuer das zuruecksetzen der KWL nach einem Fehler. 1=Reset
       
      
      #Hier werden die Gruppenadressen fuer die Rueckmeldungen vergeben: (Nich vergeben = inaktiv)
      my $ga_status_ventilator_zul = ''; #GA DPT5.001 fuer Status Ventilator Zuluft %
      my $ga_status_ventilator_abl = ''; #GA DPT5.001 fuer Status Ventilator Abluft %
      my $ga_status_bypass_prozent = ''; #GA DPT5.001 fuer Status Bypassklappe %
      my $ga_betriebsstunden_filter = ''; #GA DPT16.000 fuer die Rueckmeldung der Betribsstunden des Filters
      my $ga_zustand_badschalter = ''; #GA DPT1.001 fuer die Rueckmeldung des Zustandes des Badezimmerschalters
      my $ga_fehler_filter = ''; #GA DPT 1.001 fuer den Zustand des Filters. 0=OK, 1=Filter Voll
      my $ga_fehlercode = ''; #GA DPT 16.000 fuer die Ausgabe des Fehlercodes als Text
      my $ga_aktstufe = ''; #Wert für aktuelle Stufe
       
      #Hier werden die Gruppenadressen für die Temperaturen vergeben: (Nicht vergeben=inaktiv)
      my $ga_aul_temp = ''; #GA DPT 9.001 für die Aussenlufttemperatur
      my $ga_zul_temp = ''; #GA DPT 9.001 für die Zulufttemperatur
      my $ga_abl_temp = ''; #GA DPT 9.001 für die Ablufttemperatur
      my $ga_fol_temp = ''; #GA DPT 9.001 für die Fortlufttemperatur
       
      #Zuordnung der Namen fuer die RRD's:
      my $Name_rrd_AUL = 'KWL_Aussenluft'; #Name RRD Aussenluft
      my $Name_rrd_ZUL = 'KWL_Zuluft'; #Name RRD Zuluft
      my $Name_rrd_ABL = 'KWL_Abluft'; #Name RRD Abluft
      my $Name_rrd_FOL = 'KWL_Fortluft'; #Name RRD Fortluft
       
      #Pfad zur seriellen Schnittstelle oder dem USB-Seriell-Wandler:
      my $schnittstelle = '/dev/ttyUSB-2-4';
       
      
      #Angaben für die Kommunikation über den UDP-Port einer MOXA [diese Einstellungen reichen aus, d.h. auf dem Wiregate muss unter "Seriell/LAN/Socketverbindungen" KEINE Socketverbindung erstellt werden
      my $socknum = ""; # Eindeutige Nummer des Sockets
      my $send_ip = ""; # SendeIP (UDP)
      my $send_port = ""; # Sendeport (UDP)    
      my $recv_ip = ""; # EmpfangsIP (UDP)
      my $recv_port = ""; # Empfangsport (UDP)
       
      
      # Kommunikationsart
      my $Kom_Art = "S"; # "S" = seriell; "M" = Moxa
       
      
      # Dauer einer Abfrage
      my $Zaehler = "250"; #Mit dieser Variable Zaehler wird beeinflusst, wie lange das Plugin auf den Abschluss einer Rückmeldung der KWL wartet; empfohlener Wert für seriell: 250; für Moxa: 25
       
      # Debug level 0 = nur die wichtigsten Infos, 1 = Alle Zustaende, 2 = Rohdaten (nur für Fehlersuche)
      my $debug=0;
       
       
      #Weitere Variablen die benötigt werden -> NICHT verändern!
      my $seriel;
      my $sin; #Serial Input = Empangener Datenstrom
      my $cin; #Counter Input =  Länge des Datenpackets
      my $laenge; #Länge des empfangenen Datenstrings nachdem kürzen
       
      my $checksum = 0; #Checksumme
      my @hex; #Hilfsarray für die Checksummenberechnung
      my $x07warschon; #Hilfsvariable für die Checksummenberechnung
       
      &readConf(); #conf.d einlesen
       
      my $return_value2;
      my $daten;
      my $reciv;
      my $reciv_all;
      my $ack = pack("H*","07F3");
      my $rcv_checksum;
       
      # Zyklischer Aufruf nach restart, empfang GA oder nach einstellung rrd (typisch 300sek).
      $plugin_info{$plugname.'_cycle'}  = 300; 
      
      use Device::SerialPort;
      
       
      #Einrichten der Seriellen Schnittstelle fuer die Kommunikation mit dem ComfoAir falls Schnittstelle auf "S" steht
      if ($Kom_Art eq "S"){
                   $seriel = Device::SerialPort->new($schnittstelle) || die "Kann $schnittstelle nicht öffnen! ($!)\n";
                   $seriel->baudrate(9600);
                   $seriel->parity("none");
                   $seriel->databits(8);
                   $seriel->stopbits(1);
                   if($debug>=1){plugin_log($plugname,'Schnittstelle: ' . $schnittstelle . ' erfolgreich geöffnet')};
      }elsif ($Kom_Art eq "M"){
          if (!$socket[$socknum]) { # socket erstellen
                  $socket[$socknum] = IO::Socket::INET->new(LocalPort => $recv_port,
                                            Proto => "udp",
                                            LocalAddr => $recv_ip,
                                            PeerPort  => $send_port,
                                            PeerAddr  => $send_ip,
                                            ReuseAddr => 1
                                             )
              or return ("open of $recv_ip : $recv_port failed: $!");
              $socksel->add($socket[$socknum]); # add socket to select
              $plugin_socket_subscribe{$socket[$socknum]} = $plugname; # Plugin an Socket "anmelden"
              return "opened Socket $socknum";
                                   if($debug>=1){plugin_log($plugname,'Socket: ' . $socknum . ' erfolgreich geöffnet')};
          }
      }
      
       
      if ($msg{'apci'} eq "A_GroupValue_Write"){ #Wenn ein Telegramm vom KNX empfangen wird, ab hier auswerten
          if ($msg{'dst'} eq $ga_stufeabwesend && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00990101";
              plugin_log($plugname,'Stufe abwesend setzen');
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_stufe1 && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00990102";
              plugin_log($plugname,'Stufe 1 setzen');
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_stufe2 && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00990103";
              plugin_log($plugname,'Stufe 2 setzen');
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_stufe3 && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00990104";
              plugin_log($plugname,'Stufe 3 setzen');
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_komforttemp) {
              my $komforttemp = knx_read($msg{'dst'},0,9.001);
              plugin_log($plugname,'Komforttemp auf: ' . $komforttemp . '°C setzen');
              my $temphex = ($komforttemp + 20)*2; #Rechne die Temperatur fuer die ComfoAir um
              $temphex = sprintf "%x" , $temphex; # Mache aus Integer HEX
              $daten = "00D301" . $temphex;
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_reset_filter && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00DB0400000001";
              plugin_log($plugname,'Filter zurücksetzen');
              $return_value2 = command_senden($daten);
          }elsif ($msg{'dst'} eq $ga_reset_error && knx_read($msg{'dst'},0,1) == 1) {
              $daten = "00DB0401000000";
              plugin_log($plugname,'Fehler zurücksetzen');
              $return_value2 = command_senden($daten);
          }
          if($debug>=2){plugin_log($plugname,'ENDE Aufruf durch GA');}
          return;
                   
      } else { # zyklischer Aufruf
          # Plugin an Gruppenadresse "anmelden", hierdurch wird das Plugin im folgenden bei jedem eintreffen eines Telegramms auf die GA aufgerufen und der obere Teil dieser if-Schleife durchlaufen
          $plugin_subscribe{$ga_stufeabwesend}{$plugname} = 1;
          $plugin_subscribe{$ga_stufe1}{$plugname} = 1;
          $plugin_subscribe{$ga_stufe2}{$plugname} = 1;
          $plugin_subscribe{$ga_stufe3}{$plugname} = 1;
          $plugin_subscribe{$ga_komforttemp}{$plugname} = 1;
          $plugin_subscribe{$ga_reset_filter}{$plugname} = 1;
          $plugin_subscribe{$ga_reset_error}{$plugname} = 1;
          
                   
          $daten = "00D100";
          plugin_log($plugname,'Temperatur abrufen');
          $return_value2 = command_senden($daten);
          
          if($ga_status_ventilator_zul && $ga_status_ventilator_abl){ #Nur wenn beide GA's vergeben sind, dann die Zust?nde der Ventilatoren abfragen
              $daten = "000B00";
              if($debug>=1){plugin_log($plugname,'Ventilator Status abrufen');}
              $return_value2 = command_senden($daten);
          }
                   
          if($ga_status_bypass_prozent){ #Nur wenn die GA vergeben ist, dann Zustand Bypassklappe abfragen
              $daten = "000D00";
              if($debug>=1){plugin_log($plugname,'Bypass Zustand abrufen');}
              $return_value2 = command_senden($daten);
          }
                   
          if($ga_betriebsstunden_filter){ #Nur wenn die GA vergeben ist, die Betriebsstunden abfragen
              $daten = "00DD00";
              if($debug>=1){plugin_log($plugname,'Betriebsstunden abrufen');}
              $return_value2 = command_senden($daten);
          }
                   
          if($ga_zustand_badschalter){ #Nur wenn die GA vergeben ist, die Binaereingaenge abfragen
              $daten = "000300";
              if($debug>=1){plugin_log($plugname,'Binäreingänge abrufen');}
              $return_value2 = command_senden($daten);
          }
                   
          if($ga_aktstufe){ #Nur wenn die GA vergeben ist, die Ventilationsstufe abfragen
              $daten = "00CD00";
              if($debug>=1){plugin_log($plugname,'Ventilationsstufe abrufen');}
              $return_value2 = command_senden($daten);
          }
                   
          #Hier werden die Stoermeldungen abgefragt
          $daten = "00D900";
          if($debug>=1){plugin_log($plugname,'Störungen abrufen');}
          $return_value2 = command_senden($daten);
      
      
          if($debug>=2){plugin_log($plugname,'ENDE Zyklische Abfrage');}
          return;
      }
       
       
      # Ab hier wird das Datenpaket inklusive Checksumme zusammengestellt und an die ComfoAir uebertragen
      sub command_senden{
          my $data = $_[0];
          if($debug>=2){plugin_log($plugname,'data: ' . $data);} 
          $checksum = checksum_berechnen($data);
          if($debug>=2){plugin_log($plugname,'Checksumme aus der Subroutine: '.$checksum);}
          my $command = pack("H*","07F0" . $data . $checksum . "070F");
          my $commandhex = $command;
          
          $commandhex =~ s/(.)/sprintf("0x%x ",ord($1))/eg;
          if($debug>=2){plugin_log($plugname,'transmit: ' . $commandhex);} #Zeigt im Pluginlog das fertige Datenpaket, dass uebertragen wird
                   if ($Kom_Art eq "S"){    
                                   $seriel->write($command); #Befehl an die ComfoAir senden
                   } elsif ($Kom_Art eq "M"){    
                                   $plugin_info{$plugname.'_debug'} = $command;
                                   syswrite($socket[$socknum], $command);
                   }
          $reciv = '';
              
          $|=1;
          my $exit=0;
          while($exit < $Zaehler) 
          {
              if ($Kom_Art eq "S"){
                   ($cin, $sin) = $seriel->read(45);
              }elsif ($Kom_Art eq "M"){
                                       $sin ='';
                                       if ($fh) { # Antwort auslesen
                                                      recv($fh,$sin,80,0);
                                       }
                                       $cin = length($sin);
              }
              if($cin > 0){
                  $sin = unpack "H*", $sin;
                  $reciv .= $sin;
                  $exit=0;
              }else{
                  $exit++
              }
              
              if($debug>=2){plugin_log($plugname,'reciv-direkt:     ' . $sin);}
           
                  if($reciv =~ /070f/i){           
                      last;
                  }
          }#Ende While   
      
          if ($Kom_Art eq "S"){
              $seriel->write($ack); #ACK senden
              if($debug>=2){plugin_log($plugname,'ACK senden');}
          } elsif ($Kom_Art eq "M"){
              syswrite($socket[$socknum], $ack); #ACK senden
              if($debug>=2){plugin_log($plugname,'ACK senden');}
          }
      
          if($reciv eq ""){
              plugin_log($plugname,'FEHLER: Keine Daten empfangen!');
              return;
          }
          
          while ((length($reciv) > 3) && (substr($reciv,(length($reciv)-4),4) ne '070f')) #solange das Ende nicht 0f lautet
          {
                          if($debug>=2){plugin_log($plugname,'String vor Kuerzung Ende: '.$reciv);}
                          $reciv = substr($reciv,0,-2); #String um die letzten zwei Zeichen kürzen
                          if($debug>=2){plugin_log($plugname,'String nach Kuerzung Ende: '.$reciv);}
          }
       
              #Hier wird der empfangene String um Start- und Endbyte gekürzt
              $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
              $reciv = substr($reciv,0,($laenge-4));
              if($debug>=2){plugin_log($plugname,'String ohne 07f3: '.$reciv);}
              
              #Test einer Methode falls aussversehen mehrere Datenpakete auf einmal im Datenstring enthalten sind...
              if($reciv =~ /07f307f0/i){
                  my @dataarray=split(/07f307f0/,$reciv);
                  $reciv = @dataarray[0];
              }
              
              if($debug>=2){plugin_log($plugname,'Erste 4 Byte des Datenpakets: '.substr($reciv,0,4));}
      
                                                  
              while (substr($reciv,(0,4) ne '07f0'){
                  $reciv = substr($reciv,2); #falls noch ein falsche Zeichen am Anfang des Strings enthalten sind, werden diese hier entfernt.
                  if($debug>=2){plugin_log($plugname,'reciv gekuerzt: '.$reciv);}
              }
      
                                   
              #Nun wird die Checksumme gelesen und aus dem Datenstring entfernt
              $checksum = 0;
              $checksum = substr($reciv,-2,2);
              if($debug>=2){plugin_log($plugname,'Checksumme gelesen: '.$checksum);}
              $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
              $reciv = substr($reciv,0,($laenge-2));
              if($debug>=2){plugin_log($plugname,'Datenpaket ohne Checksumme: '.$reciv);}
      
              #Hier wird die Subroutine für die Berechnung der Checksumme aufgerufen und das Ergebnis in $rcv_checksum zurück gegeben
              $rcv_checksum = checksum_berechnen($reciv);
      
                                  
              if($rcv_checksum eq $checksum){ #Hier wird geprüft ob die Checksumme korrekt ist
                  if($debug>=2){plugin_log($plugname,'Checksumme OK ');}
                  if($reciv =~ /00D209/i){ #Wenn die Temperaturen empfangen wurden und die Laenge passt
      
                      my $t1 = substr($reciv,8,2);
                      my $t2 = substr($reciv,10,2);
                      my $t3 = substr($reciv,12,2);
                      my $t4 = substr($reciv,14,2);
                                                                                 
                      #Hier werden die Temperaturen "decodiert" damit sie einen Sinn ergeben
                      $t1 =  (hex($t1)/2)-20;
                      $t2 =  (hex($t2)/2)-20;
                      $t3 =  (hex($t3)/2)-20;
                      $t4 =  (hex($t4)/2)-20;
      
                      #Wenn die GA's vergeben wurde, die Temperaturen auf die GA's senden
                      if($ga_aul_temp ne ''){knx_write($ga_aul_temp,$t1,9.001);}
                      if($ga_zul_temp ne ''){knx_write($ga_zul_temp,$t2,9.001);}
                      if($ga_abl_temp ne ''){knx_write($ga_abl_temp,$t3,9.001);}
                      if($ga_fol_temp ne ''){knx_write($ga_fol_temp,$t4,9.001);}
                      
                      #Ab hier werden die RRD's mit den aktuellen Temperaturen aktualisiert:
                      update_rrd($Name_rrd_AUL,"",$t1);
                      update_rrd($Name_rrd_ZUL,"",$t2);
                      update_rrd($Name_rrd_ABL,"",$t3);
                      update_rrd($Name_rrd_FOL,"",$t4);
                      
                      plugin_log($plugname,'AUL: ' . $t1 . '°C, ZUL:' . $t2 . '°C, ABL: ' . $t3 . '°C, FOL: ' . $t4 . '°C');
      
                  }elsif($reciv =~ /000C06/i){ #Wenn der Status fuer die Ventilatoren empfangen wurden
                      my $vent_zul = substr($reciv,6,2);
                      my $vent_abl = substr($reciv,8,2);
                      plugin_log($plugname,'ZUL: ' . hex($vent_zul) . '% ABL: ' . hex($vent_abl) . '%');
                      knx_write($ga_status_ventilator_zul,hex($vent_zul),5.001);
                      knx_write($ga_status_ventilator_abl,hex($vent_abl),5.001);    
                      
                  }elsif($reciv =~ /00CE0E/i){ #Wenn Status Ventilatorenstufe empfangen wurden
                      my $akt_stufe = substr($reciv,22,2);
                      plugin_log($plugname,'AKT_STUFE: ' . hex($akt_stufe));
                      knx_write($ga_aktstufe,hex($akt_stufe),5.005);                                                                                
      
                  }elsif($reciv =~ /000E04/i){ #Wenn der Status fuer die Bypassklappe empfangen wurden
                      my $bypass_prozent = substr($reciv,6,2);
                      plugin_log($plugname,'Bypass: ' . hex($bypass_prozent) . '%');                
                      knx_write($ga_status_bypass_prozent,hex($bypass_prozent),5.001);
      
                  }elsif($reciv =~ /00DE14/i){ #Wenn die Rueckmeldung der Betriebsstunden empfangen wurden
                      my $betriebsstunden_filter = substr($reciv,36,4);
                      plugin_log($plugname,'Betriebsstunden: ' . hex($betriebsstunden_filter) . 'h');                 
                      knx_write($ga_betriebsstunden_filter,hex($betriebsstunden_filter) . 'h',16.000);
                      
                  }elsif($reciv =~ /000402/i){ #Wenn die Rueckmeldung der Binaereingaenge empfangen wurden
                      my $zustand_badschalter = substr($reciv,8,1);
                      plugin_log($plugname,'Zustand Badezimmerschalter: ' . $zustand_badschalter);                 
                      knx_write($ga_zustand_badschalter,$zustand_badschalter,1.001);
                      
                  }elsif($reciv =~ /00DA11/i){ #Wenn die Rueckmeldung der Stoermeldungen empfangen wurden
                      my $fehlerAlo = substr($reciv,6,2);
                      my $fehlerAhi = substr($reciv,30,2);
                      my $fehlerE = substr($reciv,8,2);
                      my $fehlerFilter = substr($reciv,22,2);
                      my $fehlerEA = substr($reciv,24,2);
                      
                      my $numAlo = 'A';
                      my $numAhi = 'A';
                      my $numE = 'A';
                      my $numEA = 'A';
                      
                      $numAlo .= unpack("B*",pack("H*",$fehlerAlo));
                      $numAhi .= unpack("B*",pack("H*",$fehlerAhi));
                      $numE .= unpack("B*",pack("H*",$fehlerE));
                      $numEA .= unpack("B*",pack("H*",$fehlerEA));
                      
                      
                      $fehlerAlo = reverse($numAlo); #Wandle den Wert in Binaer und drehe die Reihenfolge um. z.B 0x02 = 00000010 = 010000000
                      $fehlerAlo = index($fehlerAlo,'1')+1; # Zaehle an welcher Stelle die 1 auftaucht (von links gelesen) z.B. 01000000 = INDEX 2 = Fehler2
                      
                      if($fehlerAhi ne '00'){
                          $fehlerAhi = index(reverse($numAhi),'1')+9;
                      }else{
                          $fehlerAhi = '';
                      }
                      
                      $fehlerE = index(reverse($numE),'1')+1;
                      $fehlerEA = index(reverse($numEA),'1')+1;
                                                                                  
                      if($fehlerAhi == 16){$fehlerAhi = 0;}
                                                                                  
                      if($ga_fehlercode){ #Wenn die GA fuer das uebertragen den Fehlercodes eingertagen wurde, ab hier auswerten
                          if($fehlerAlo > 0){
                              plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAlo);
                              knx_write($ga_fehlercode,'A' . $fehlerAlo,16.001);
                          }elsif($fehlerAhi ne ''){
                              plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAhi);
                              knx_write($ga_fehlercode,'A' . $fehlerAhi,16.001);                        
                          }elsif($fehlerE > 0){
                              plugin_log($plugname,'Aktueller Fehlercode: E' . $fehlerE);
                              knx_write($ga_fehlercode,'E' . $fehlerE,16.001);
                          }elsif($fehlerEA > 0){
                              plugin_log($plugname,'Aktueller Fehlercode: EA' . $fehlerEA);
                              knx_write($ga_fehlercode,'EA' . $fehlerEA,16.001);
                          }else{
                              plugin_log($plugname,'Aktueller Fehlercode: keiner' );
                              knx_write($ga_fehlercode,'keiner' . $fehlerEA,16.001);
                          }    
                      }
                      
                      if(hex($fehlerFilter) > 0){
                          plugin_log($plugname,'Aktueller Fehler: Filter Voll');
                          knx_write($ga_fehler_filter,1,1);
                      }else{
                          knx_write($ga_fehler_filter,0,1);
                      }               
                  }
              }else{
                  if($debug>=1){plugin_log($plugname,'Checksumme fehlerhaft! Gelesen: '.$checksum.' Berechnet: '.$rcv_checksum);}
              }
      } #ENDE Sub command_senden
      
      sub checksum_berechnen {    
          my $chk_datasum = $_[0];
          $rcv_checksum =0;
          my $i;
           $chk_datasum = $chk_datasum . "AD"; #+173 fuer die Checksummenberechnung
              if($debug>=2){plugin_log($plugname,'String für die Berechnung der Checksumme: '.$chk_datasum);}
          $x07warschon = 0;
          $laenge = length($chk_datasum);
              for($i = 0; $i< $laenge; $i++) {
                  my $wertstring = substr($chk_datasum,$i,2);
                  if($debug>=3){plugin_log($plugname,'Zahl: '.$wertstring);}
                  my $wertbetrag = hex($wertstring);
                  if ($wertbetrag == 7) {
                      if ($x07warschon == 1) {
                          $x07warschon = 0;
                          $i++;
                          next;
                      } else {
                          $x07warschon = 1;
                      }
                  }
              $rcv_checksum += $wertbetrag;
                  if($debug>=3){plugin_log($plugname,'Summe: '.$rcv_checksum);}
              $i++;
          }
                  if($debug>=3){plugin_log($plugname,'Summe def: '.$rcv_checksum);}
      
          if($debug>=2){plugin_log($plugname,'Checksumme vor der Umwandlung: '.$rcv_checksum);}
          $rcv_checksum = sprintf "%x\n" , $rcv_checksum; #Mache aus Integer wieder HEX
          if($debug>=2){plugin_log($plugname,'Checksumme vor der Kürzung: '.$rcv_checksum);}
          $rcv_checksum = substr($rcv_checksum,-3,2); #Verwende nur die letzten beiden Stellen
          if($debug>=2){plugin_log($plugname,'Checksumme nach der Kürzung: '.$rcv_checksum);}
          return $rcv_checksum;
      } #Ende checksum_berechnen
      
       
      
      sub readConf
      {
          my $confFile = '/etc/wiregate/plugin/generic/conf.d/'.basename($plugname,'.pl').'.conf';
          if (! -f $confFile)
          {
              plugin_log($plugname, " no conf file [$confFile] found!"); 
          }
          else
          {
              if($debug>=1){plugin_log($plugname, " reading conf file [$confFile].");}
              open(CONF, $confFile);
              my @lines = <CONF>;
              close($confFile);
              my $result = eval("@lines");
              ($result) and plugin_log($plugname, "conf file [$confFile] returned result[$result]");
              if ($@) 
              {
                  if($debug>=1){plugin_log($plugname, " conf file [$confFile] returned:");}
                  my @parts = split(/\n/, $@);
                  if($debug>=2){plugin_log($plugname, " --> $_") foreach (@parts);}
              }
          }
      } # readConf
      Was noch offen ist und was mir etwas zu denken gibt...

      - Zwischen durch kommen gar keine Daten im Plugin an
      - Am Senden habe ich noch nichts verändert (was auch? Ich weiss nicht mal wo es da klemmt )
      Gruss Patrik alias swiss

      Kommentar


        Hallo Patrik,

        ich bin dir selbstverständig äußerst dankbar für die Weiterentwicklung, na klar bekommst du Feedback von mir nur her damit, ich werde hier alles testen können.

        Die Zeile habe ich geändert, log zeigt beim ersten Plugin Aufruf Werte, dann bei jeden weiteren nur noch die Checksummen Fehler:


        Code:
        2013-04-05 22:19:43.432,Comfoair,conf file [/etc/wiregate/plugin/generic/conf.d/Comfoair.conf] returned result[1]
        2013-04-05 22:19:43.432,Comfoair,Temperatur abrufen
        2013-04-05 22:19:43.459,Comfoair,AUL: 19°C, ZUL:19.5°C, ABL: 19.5°C, FOL: 19.5°C
        2013-04-05 22:19:43.459,Comfoair,Ventilator Status abrufen
        2013-04-05 22:19:43.461,Comfoair,Checksumme fehlerhaft! Gelesen: 26 Berechnet: 2a
        2013-04-05 22:19:43.462,Comfoair,Bypass Zustand abrufen
        2013-04-05 22:19:43.482,Comfoair,AUL: 19°C, ZUL:19.5°C, ABL: 19.5°C, FOL: 19.5°C
        2013-04-05 22:19:43.483,Comfoair,Betriebsstunden abrufen
        2013-04-05 22:19:43.534,Comfoair,Checksumme fehlerhaft! Gelesen: 24 Berechnet: 65
        2013-04-05 22:19:43.534,Comfoair,Binäreingänge abrufen
        2013-04-05 22:19:43.561,Comfoair,Zustand Badezimmerschalter: 0
        2013-04-05 22:19:43.563,Comfoair,Ventilationsstufe abrufen
        2013-04-05 22:19:43.605,Comfoair,AKT_STUFE: 2
        2013-04-05 22:19:43.607,Comfoair,Störungen abrufen
        2013-04-05 22:19:43.654,Comfoair,Aktueller Fehlercode: keiner
        2013-04-05 22:24:44.200,Comfoair,conf file [/etc/wiregate/plugin/generic/conf.d/Comfoair.conf] returned result[1]
        2013-04-05 22:24:44.201,Comfoair,Temperatur abrufen
        2013-04-05 22:24:44.204,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.204,Comfoair,Ventilator Status abrufen
        2013-04-05 22:24:44.207,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.208,Comfoair,Bypass Zustand abrufen
        2013-04-05 22:24:44.211,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.211,Comfoair,Betriebsstunden abrufen
        2013-04-05 22:24:44.214,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.214,Comfoair,Binäreingänge abrufen
        2013-04-05 22:24:44.218,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.218,Comfoair,Ventilationsstufe abrufen
        2013-04-05 22:24:44.221,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        2013-04-05 22:24:44.221,Comfoair,Störungen abrufen
        2013-04-05 22:24:44.224,Comfoair,Checksumme fehlerhaft! Gelesen:  Berechnet: ad
        Ist der ausführlich Log immer besser oder?

        Edit: da warst du schon schneller, das teste ich gleich!
        Schöne Grüße
        Moritz

        Kommentar


          Hallo Moritz

          Ja das ausfürliche Log hilft mir zu sehen was intern mit den Daten passiert. Dann kann ich auch besser herausfinden wo etwas nicht so tut wie es soll...

          Teste mal das neue Plugin das ich angehängt habe. Da sind ein paar Änderungen eingeflossen, die helfen sollte mehr verwertbare Datenpakete zu erhalten.
          Gruss Patrik alias swiss

          Kommentar


            So die 1.6.1 ist drauf, im erweiterten Log erscheint wiederholten:

            Code:
            2013-04-05 23:00:49.007,ConfoAir Test,,0s,syntax error at (eval 42) line 304, near "){"
            syntax error at (eval 42) line 439, near "}
             }"
            Can't use global @_ in "my" at (eval 42) line 442, near "= $_"
            syntax error at (eval 42) line 474, near ";
             }"
            syntax error at (eval 42) line 500, near "}
             }"
            
            2013-04-05 23:01:19.780,ConfoAir Test,,0s,syntax error at (eval 43) line 304, near "){"
            syntax error at (eval 43) line 439, near "}
             }"
            Can't use global @_ in "my" at (eval 43) line 442, near "= $_"
            syntax error at (eval 43) line 474, near ";
             }"
            syntax error at (eval 43) line 500, near "}
             }"
            
            2013-04-05 23:02:20.222,ConfoAir Test,,0s,syntax error at (eval 44) line 304, near "){"
            syntax error at (eval 44) line 439, near "}
             }"
            Can't use global @_ in "my" at (eval 44) line 442, near "= $_"
            syntax error at (eval 44) line 474, near ";
             }"
            syntax error at (eval 44) line 500, near "}
             }"
            Schöne Grüße
            Moritz

            Kommentar


              Oo...

              Ja da hat sich ein(oder mehrere?) Fehler eingeschlichen...

              Teste es mal so:

              Code:
              # Plugin zur Ansteuerung einer Zender ComfoAir
              # Version 1.6.1 05.04.2013 BETA
              # Copyright: swiss (https://knx-user-forum.de/members/swiss.html)
              # Aufbau moeglichst so, dass man unterhalb der Einstellungen nichts veraendern muss!
              # - Neu mit der Moeglichkeit zur Anbindung über einen Moxa NPort von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
              # - Neustrukturierung des Codes von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
               
              
              ####################
              ###Einstellungen:###
              ####################
              
              #BITTE ab sofort die Einstellungen unter conf.d vornemen. Damit bleiben die Einstellungen auch bei einem Update erhalten.
               
              
              ######################
              ##ENDE Einstellungen##
              ######################
              
              
              #Ab hier nichts mehr aendern.
              #Hauptverarbeitung
              
              
              #Erzeuge Variablen fuer die Zuordnung der Steuerfunktionen zu den Gruppenadressen:
              my $ga_stufeabwesend = ''; #1bit Trigger fuer Stufe "Abwesend". 1=Aktivieren
              my $ga_stufe1 = ''; #1bit Trigger fuer Stufe1. 1=Aktivieren
              my $ga_stufe2 = ''; #1bit Trigger fuer Stufe2. 1=Aktivieren
              my $ga_stufe3 = ''; #1bit Trigger fuer Stufe3. 1=Aktivieren
              my $ga_komforttemp = ''; #GA DPT 9.001 zum setzen der Komforttemperatur
              my $ga_reset_filter = ''; #1bit Trigger fuer das Zuruecksetzen des Betriebsstundenzaehlers des Filters. 1=Reset
              my $ga_reset_error = ''; #1bit Trigger fuer das zuruecksetzen der KWL nach einem Fehler. 1=Reset
               
              
              #Hier werden die Gruppenadressen fuer die Rueckmeldungen vergeben: (Nich vergeben = inaktiv)
              my $ga_status_ventilator_zul = ''; #GA DPT5.001 fuer Status Ventilator Zuluft %
              my $ga_status_ventilator_abl = ''; #GA DPT5.001 fuer Status Ventilator Abluft %
              my $ga_status_bypass_prozent = ''; #GA DPT5.001 fuer Status Bypassklappe %
              my $ga_betriebsstunden_filter = ''; #GA DPT16.000 fuer die Rueckmeldung der Betribsstunden des Filters
              my $ga_zustand_badschalter = ''; #GA DPT1.001 fuer die Rueckmeldung des Zustandes des Badezimmerschalters
              my $ga_fehler_filter = ''; #GA DPT 1.001 fuer den Zustand des Filters. 0=OK, 1=Filter Voll
              my $ga_fehlercode = ''; #GA DPT 16.000 fuer die Ausgabe des Fehlercodes als Text
              my $ga_aktstufe = ''; #Wert für aktuelle Stufe
               
              #Hier werden die Gruppenadressen für die Temperaturen vergeben: (Nicht vergeben=inaktiv)
              my $ga_aul_temp = ''; #GA DPT 9.001 für die Aussenlufttemperatur
              my $ga_zul_temp = ''; #GA DPT 9.001 für die Zulufttemperatur
              my $ga_abl_temp = ''; #GA DPT 9.001 für die Ablufttemperatur
              my $ga_fol_temp = ''; #GA DPT 9.001 für die Fortlufttemperatur
               
              #Zuordnung der Namen fuer die RRD's:
              my $Name_rrd_AUL = 'KWL_Aussenluft'; #Name RRD Aussenluft
              my $Name_rrd_ZUL = 'KWL_Zuluft'; #Name RRD Zuluft
              my $Name_rrd_ABL = 'KWL_Abluft'; #Name RRD Abluft
              my $Name_rrd_FOL = 'KWL_Fortluft'; #Name RRD Fortluft
               
              #Pfad zur seriellen Schnittstelle oder dem USB-Seriell-Wandler:
              my $schnittstelle = '/dev/ttyUSB-2-4';
               
              
              #Angaben für die Kommunikation über den UDP-Port einer MOXA [diese Einstellungen reichen aus, d.h. auf dem Wiregate muss unter "Seriell/LAN/Socketverbindungen" KEINE Socketverbindung erstellt werden
              my $socknum = ""; # Eindeutige Nummer des Sockets
              my $send_ip = ""; # SendeIP (UDP)
              my $send_port = ""; # Sendeport (UDP)    
              my $recv_ip = ""; # EmpfangsIP (UDP)
              my $recv_port = ""; # Empfangsport (UDP)
               
              
              # Kommunikationsart
              my $Kom_Art = "S"; # "S" = seriell; "M" = Moxa
               
              
              # Dauer einer Abfrage
              my $Zaehler = "250"; #Mit dieser Variable Zaehler wird beeinflusst, wie lange das Plugin auf den Abschluss einer Rückmeldung der KWL wartet; empfohlener Wert für seriell: 250; für Moxa: 25
               
              # Debug level 0 = nur die wichtigsten Infos, 1 = Alle Zustaende, 2 = Rohdaten (nur für Fehlersuche)
              my $debug=0;
               
               
              #Weitere Variablen die benoetigt werden -> NICHT veraendern!
              my $seriel;
              my $sin; #Serial Input = Empangener Datenstrom
              my $cin; #Counter Input =  Länge des Datenpackets
              my $laenge; #Länge des empfangenen Datenstrings nachdem kürzen
               
              my $checksum = 0; #Checksumme
              my @hex; #Hilfsarray für die Checksummenberechnung
              my $x07warschon; #Hilfsvariable für die Checksummenberechnung
               
              &readConf(); #conf.d einlesen
               
              my $return_value2;
              my $daten;
              my $reciv;
              my $reciv_all;
              my $ack = pack("H*","07F3");
              my $rcv_checksum;
               
              # Zyklischer Aufruf nach restart, empfang GA oder nach einstellung rrd (typisch 300sek).
              $plugin_info{$plugname.'_cycle'}  = 300; 
              
              use Device::SerialPort;
              
               
              #Einrichten der Seriellen Schnittstelle fuer die Kommunikation mit dem ComfoAir falls Schnittstelle auf "S" steht
              if ($Kom_Art eq "S"){
                           $seriel = Device::SerialPort->new($schnittstelle) || die "Kann $schnittstelle nicht öffnen! ($!)\n";
                           $seriel->baudrate(9600);
                           $seriel->parity("none");
                           $seriel->databits(8);
                           $seriel->stopbits(1);
                           if($debug>=1){plugin_log($plugname,'Schnittstelle: ' . $schnittstelle . ' erfolgreich geöffnet')};
              }elsif ($Kom_Art eq "M"){
                  if (!$socket[$socknum]) { # socket erstellen
                          $socket[$socknum] = IO::Socket::INET->new(LocalPort => $recv_port,
                                                    Proto => "udp",
                                                    LocalAddr => $recv_ip,
                                                    PeerPort  => $send_port,
                                                    PeerAddr  => $send_ip,
                                                    ReuseAddr => 1
                                                     )
                      or return ("open of $recv_ip : $recv_port failed: $!");
                      $socksel->add($socket[$socknum]); # add socket to select
                      $plugin_socket_subscribe{$socket[$socknum]} = $plugname; # Plugin an Socket "anmelden"
                      return "opened Socket $socknum";
                                           if($debug>=1){plugin_log($plugname,'Socket: ' . $socknum . ' erfolgreich geöffnet')};
                  }
              }
              
               
              if ($msg{'apci'} eq "A_GroupValue_Write"){ #Wenn ein Telegramm vom KNX empfangen wird, ab hier auswerten
                  if ($msg{'dst'} eq $ga_stufeabwesend && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00990101";
                      plugin_log($plugname,'Stufe abwesend setzen');
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_stufe1 && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00990102";
                      plugin_log($plugname,'Stufe 1 setzen');
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_stufe2 && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00990103";
                      plugin_log($plugname,'Stufe 2 setzen');
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_stufe3 && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00990104";
                      plugin_log($plugname,'Stufe 3 setzen');
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_komforttemp) {
                      my $komforttemp = knx_read($msg{'dst'},0,9.001);
                      plugin_log($plugname,'Komforttemp auf: ' . $komforttemp . '°C setzen');
                      my $temphex = ($komforttemp + 20)*2; #Rechne die Temperatur fuer die ComfoAir um
                      $temphex = sprintf "%x" , $temphex; # Mache aus Integer HEX
                      $daten = "00D301" . $temphex;
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_reset_filter && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00DB0400000001";
                      plugin_log($plugname,'Filter zurücksetzen');
                      $return_value2 = command_senden($daten);
                  }elsif ($msg{'dst'} eq $ga_reset_error && knx_read($msg{'dst'},0,1) == 1) {
                      $daten = "00DB0401000000";
                      plugin_log($plugname,'Fehler zurücksetzen');
                      $return_value2 = command_senden($daten);
                  }
                  if($debug>=2){plugin_log($plugname,'ENDE Aufruf durch GA');}
                  return;
                           
              } else { # zyklischer Aufruf
                  # Plugin an Gruppenadresse "anmelden", hierdurch wird das Plugin im folgenden bei jedem eintreffen eines Telegramms auf die GA aufgerufen und der obere Teil dieser if-Schleife durchlaufen
                  $plugin_subscribe{$ga_stufeabwesend}{$plugname} = 1;
                  $plugin_subscribe{$ga_stufe1}{$plugname} = 1;
                  $plugin_subscribe{$ga_stufe2}{$plugname} = 1;
                  $plugin_subscribe{$ga_stufe3}{$plugname} = 1;
                  $plugin_subscribe{$ga_komforttemp}{$plugname} = 1;
                  $plugin_subscribe{$ga_reset_filter}{$plugname} = 1;
                  $plugin_subscribe{$ga_reset_error}{$plugname} = 1;
                  
                           
                  $daten = "00D100";
                  plugin_log($plugname,'Temperatur abrufen');
                  $return_value2 = command_senden($daten);
                  
                  if($ga_status_ventilator_zul && $ga_status_ventilator_abl){ #Nur wenn beide GA's vergeben sind, dann die Zust?nde der Ventilatoren abfragen
                      $daten = "000B00";
                      if($debug>=1){plugin_log($plugname,'Ventilator Status abrufen');}
                      $return_value2 = command_senden($daten);
                  }
                           
                  if($ga_status_bypass_prozent){ #Nur wenn die GA vergeben ist, dann Zustand Bypassklappe abfragen
                      $daten = "000D00";
                      if($debug>=1){plugin_log($plugname,'Bypass Zustand abrufen');}
                      $return_value2 = command_senden($daten);
                  }
                           
                  if($ga_betriebsstunden_filter){ #Nur wenn die GA vergeben ist, die Betriebsstunden abfragen
                      $daten = "00DD00";
                      if($debug>=1){plugin_log($plugname,'Betriebsstunden abrufen');}
                      $return_value2 = command_senden($daten);
                  }
                           
                  if($ga_zustand_badschalter){ #Nur wenn die GA vergeben ist, die Binaereingaenge abfragen
                      $daten = "000300";
                      if($debug>=1){plugin_log($plugname,'Binäreingänge abrufen');}
                      $return_value2 = command_senden($daten);
                  }
                           
                  if($ga_aktstufe){ #Nur wenn die GA vergeben ist, die Ventilationsstufe abfragen
                      $daten = "00CD00";
                      if($debug>=1){plugin_log($plugname,'Ventilationsstufe abrufen');}
                      $return_value2 = command_senden($daten);
                  }
                           
                  #Hier werden die Stoermeldungen abgefragt
                  $daten = "00D900";
                  if($debug>=1){plugin_log($plugname,'Störungen abrufen');}
                  $return_value2 = command_senden($daten);
              
              
                  if($debug>=2){plugin_log($plugname,'ENDE Zyklische Abfrage');}
                  return;
              }
               
               
              # Ab hier wird das Datenpaket inklusive Checksumme zusammengestellt und an die ComfoAir uebertragen
              sub command_senden{
                  my $data = $_[0];
                  if($debug>=2){plugin_log($plugname,'data: ' . $data);} 
                  $checksum = checksum_berechnen($data);
                  if($debug>=2){plugin_log($plugname,'Checksumme aus der Subroutine: '.$checksum);}
                  my $command = pack("H*","07F0" . $data . $checksum . "070F");
                  my $commandhex = $command;
                  
                  $commandhex =~ s/(.)/sprintf("0x%x ",ord($1))/eg;
                  if($debug>=2){plugin_log($plugname,'transmit: ' . $commandhex);} #Zeigt im Pluginlog das fertige Datenpaket, dass uebertragen wird
                           if ($Kom_Art eq "S"){    
                                           $seriel->write($command); #Befehl an die ComfoAir senden
                           } elsif ($Kom_Art eq "M"){    
                                           $plugin_info{$plugname.'_debug'} = $command;
                                           syswrite($socket[$socknum], $command);
                           }
                  $reciv = '';
                      
                  $|=1;
                  my $exit=0;
                  while($exit < $Zaehler) 
                  {
                      if ($Kom_Art eq "S"){
                           ($cin, $sin) = $seriel->read(45);
                      }elsif ($Kom_Art eq "M"){
                                               $sin ='';
                                               if ($fh) { # Antwort auslesen
                                                              recv($fh,$sin,80,0);
                                               }
                                               $cin = length($sin);
                      }
                      if($cin > 0){
                          $sin = unpack "H*", $sin;
                          $reciv .= $sin;
                          $exit=0;
                      }else{
                          $exit++
                      }
                      
                      if($debug>=2){plugin_log($plugname,'reciv-direkt:     ' . $sin);}
                   
                          if($reciv =~ /070f/i){           
                              last;
                          }
                  }#Ende While   
              
                  if ($Kom_Art eq "S"){
                      $seriel->write($ack); #ACK senden
                      if($debug>=2){plugin_log($plugname,'ACK senden');}
                  } elsif ($Kom_Art eq "M"){
                      syswrite($socket[$socknum], $ack); #ACK senden
                      if($debug>=2){plugin_log($plugname,'ACK senden');}
                  }
              
                  if($reciv eq ""){
                      plugin_log($plugname,'FEHLER: Keine Daten empfangen!');
                      return;
                  }
                  
                  while ((length($reciv) > 3) && (substr($reciv,(length($reciv)-4),4) ne '070f')) #solange das Ende nicht 0f lautet
                  {
                                  if($debug>=2){plugin_log($plugname,'String vor Kuerzung Ende: '.$reciv);}
                                  $reciv = substr($reciv,0,-2); #String um die letzten zwei Zeichen kürzen
                                  if($debug>=2){plugin_log($plugname,'String nach Kuerzung Ende: '.$reciv);}
                  }
               
                      #Hier wird der empfangene String um Start- und Endbyte gekürzt
                      $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
                      $reciv = substr($reciv,0,($laenge-4));
                      if($debug>=2){plugin_log($plugname,'String ohne 07f3: '.$reciv);}
                      
                      #Test einer Methode falls aussversehen mehrere Datenpakete auf einmal im Datenstring enthalten sind...
                      if($reciv =~ /07f307f0/i){
                          my @dataarray=split(/07f307f0/,$reciv);
                          $reciv = @dataarray[0];
                      }
                      
                      if($debug>=2){plugin_log($plugname,'Erste 4 Byte des Datenpakets: '.substr($reciv,0,4));}
              
                                                          
                      while (substr($reciv,0,4)) ne '07f0'){
                          $reciv = substr($reciv,2); #falls noch ein falsche Zeichen am Anfang des Strings enthalten sind, werden diese hier entfernt.
                          if($debug>=2){plugin_log($plugname,'reciv gekuerzt: '.$reciv);}
                      }
              
                                           
                      #Nun wird die Checksumme gelesen und aus dem Datenstring entfernt
                      $checksum = 0;
                      $checksum = substr($reciv,-2,2);
                      if($debug>=2){plugin_log($plugname,'Checksumme gelesen: '.$checksum);}
                      $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
                      $reciv = substr($reciv,0,($laenge-2));
                      if($debug>=2){plugin_log($plugname,'Datenpaket ohne Checksumme: '.$reciv);}
              
                      #Hier wird die Subroutine für die Berechnung der Checksumme aufgerufen und das Ergebnis in $rcv_checksum zurück gegeben
                      $rcv_checksum = checksum_berechnen($reciv);
              
                                          
                      if($rcv_checksum eq $checksum){ #Hier wird geprüft ob die Checksumme korrekt ist
                          if($debug>=2){plugin_log($plugname,'Checksumme OK ');}
                          if($reciv =~ /00D209/i){ #Wenn die Temperaturen empfangen wurden und die Laenge passt
              
                              my $t1 = substr($reciv,8,2);
                              my $t2 = substr($reciv,10,2);
                              my $t3 = substr($reciv,12,2);
                              my $t4 = substr($reciv,14,2);
                                                                                         
                              #Hier werden die Temperaturen "decodiert" damit sie einen Sinn ergeben
                              $t1 =  (hex($t1)/2)-20;
                              $t2 =  (hex($t2)/2)-20;
                              $t3 =  (hex($t3)/2)-20;
                              $t4 =  (hex($t4)/2)-20;
              
                              #Wenn die GA's vergeben wurde, die Temperaturen auf die GA's senden
                              if($ga_aul_temp ne ''){knx_write($ga_aul_temp,$t1,9.001);}
                              if($ga_zul_temp ne ''){knx_write($ga_zul_temp,$t2,9.001);}
                              if($ga_abl_temp ne ''){knx_write($ga_abl_temp,$t3,9.001);}
                              if($ga_fol_temp ne ''){knx_write($ga_fol_temp,$t4,9.001);}
                              
                              #Ab hier werden die RRD's mit den aktuellen Temperaturen aktualisiert:
                              update_rrd($Name_rrd_AUL,"",$t1);
                              update_rrd($Name_rrd_ZUL,"",$t2);
                              update_rrd($Name_rrd_ABL,"",$t3);
                              update_rrd($Name_rrd_FOL,"",$t4);
                              
                              plugin_log($plugname,'AUL: ' . $t1 . '°C, ZUL:' . $t2 . '°C, ABL: ' . $t3 . '°C, FOL: ' . $t4 . '°C');
              
                          }elsif($reciv =~ /000C06/i){ #Wenn der Status fuer die Ventilatoren empfangen wurden
                              my $vent_zul = substr($reciv,6,2);
                              my $vent_abl = substr($reciv,8,2);
                              plugin_log($plugname,'ZUL: ' . hex($vent_zul) . '% ABL: ' . hex($vent_abl) . '%');
                              knx_write($ga_status_ventilator_zul,hex($vent_zul),5.001);
                              knx_write($ga_status_ventilator_abl,hex($vent_abl),5.001);    
                              
                          }elsif($reciv =~ /00CE0E/i){ #Wenn Status Ventilatorenstufe empfangen wurden
                              my $akt_stufe = substr($reciv,22,2);
                              plugin_log($plugname,'AKT_STUFE: ' . hex($akt_stufe));
                              knx_write($ga_aktstufe,hex($akt_stufe),5.005);                                                                                
              
                          }elsif($reciv =~ /000E04/i){ #Wenn der Status fuer die Bypassklappe empfangen wurden
                              my $bypass_prozent = substr($reciv,6,2);
                              plugin_log($plugname,'Bypass: ' . hex($bypass_prozent) . '%');                
                              knx_write($ga_status_bypass_prozent,hex($bypass_prozent),5.001);
              
                          }elsif($reciv =~ /00DE14/i){ #Wenn die Rueckmeldung der Betriebsstunden empfangen wurden
                              my $betriebsstunden_filter = substr($reciv,36,4);
                              plugin_log($plugname,'Betriebsstunden: ' . hex($betriebsstunden_filter) . 'h');                 
                              knx_write($ga_betriebsstunden_filter,hex($betriebsstunden_filter) . 'h',16.000);
                              
                          }elsif($reciv =~ /000402/i){ #Wenn die Rueckmeldung der Binaereingaenge empfangen wurden
                              my $zustand_badschalter = substr($reciv,8,1);
                              plugin_log($plugname,'Zustand Badezimmerschalter: ' . $zustand_badschalter);                 
                              knx_write($ga_zustand_badschalter,$zustand_badschalter,1.001);
                              
                          }elsif($reciv =~ /00DA11/i){ #Wenn die Rueckmeldung der Stoermeldungen empfangen wurden
                              my $fehlerAlo = substr($reciv,6,2);
                              my $fehlerAhi = substr($reciv,30,2);
                              my $fehlerE = substr($reciv,8,2);
                              my $fehlerFilter = substr($reciv,22,2);
                              my $fehlerEA = substr($reciv,24,2);
                              
                              my $numAlo = 'A';
                              my $numAhi = 'A';
                              my $numE = 'A';
                              my $numEA = 'A';
                              
                              $numAlo .= unpack("B*",pack("H*",$fehlerAlo));
                              $numAhi .= unpack("B*",pack("H*",$fehlerAhi));
                              $numE .= unpack("B*",pack("H*",$fehlerE));
                              $numEA .= unpack("B*",pack("H*",$fehlerEA));
                              
                              
                              $fehlerAlo = reverse($numAlo); #Wandle den Wert in Binaer und drehe die Reihenfolge um. z.B 0x02 = 00000010 = 010000000
                              $fehlerAlo = index($fehlerAlo,'1')+1; # Zaehle an welcher Stelle die 1 auftaucht (von links gelesen) z.B. 01000000 = INDEX 2 = Fehler2
                              
                              if($fehlerAhi ne '00'){
                                  $fehlerAhi = index(reverse($numAhi),'1')+9;
                              }else{
                                  $fehlerAhi = '';
                              }
                              
                              $fehlerE = index(reverse($numE),'1')+1;
                              $fehlerEA = index(reverse($numEA),'1')+1;
                                                                                          
                              if($fehlerAhi == 16){$fehlerAhi = 0;}
                                                                                          
                              if($ga_fehlercode){ #Wenn die GA fuer das uebertragen den Fehlercodes eingertagen wurde, ab hier auswerten
                                  if($fehlerAlo > 0){
                                      plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAlo);
                                      knx_write($ga_fehlercode,'A' . $fehlerAlo,16.001);
                                  }elsif($fehlerAhi ne ''){
                                      plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAhi);
                                      knx_write($ga_fehlercode,'A' . $fehlerAhi,16.001);                        
                                  }elsif($fehlerE > 0){
                                      plugin_log($plugname,'Aktueller Fehlercode: E' . $fehlerE);
                                      knx_write($ga_fehlercode,'E' . $fehlerE,16.001);
                                  }elsif($fehlerEA > 0){
                                      plugin_log($plugname,'Aktueller Fehlercode: EA' . $fehlerEA);
                                      knx_write($ga_fehlercode,'EA' . $fehlerEA,16.001);
                                  }else{
                                      plugin_log($plugname,'Aktueller Fehlercode: keiner' );
                                      knx_write($ga_fehlercode,'keiner',16.001);
                                  }    
                              }
                              
                              if(hex($fehlerFilter) > 0){
                                  plugin_log($plugname,'Aktueller Fehler: Filter Voll');
                                  knx_write($ga_fehler_filter,1,1);
                              }else{
                                  knx_write($ga_fehler_filter,0,1);
                              }               
                          }
                      }else{
                          if($debug>=1){plugin_log($plugname,'Checksumme fehlerhaft! Gelesen: '.$checksum.' Berechnet: '.$rcv_checksum);}
                      }
              } #ENDE Sub command_senden
              
              sub checksum_berechnen {    
                  my $chk_datasum = $_[0];
                  $rcv_checksum =0;
                  my $i;
                   $chk_datasum = $chk_datasum . "AD"; #+173 fuer die Checksummenberechnung
                      if($debug>=2){plugin_log($plugname,'String für die Berechnung der Checksumme: '.$chk_datasum);}
                  $x07warschon = 0;
                  $laenge = length($chk_datasum);
                      for($i = 0; $i< $laenge; $i++) {
                          my $wertstring = substr($chk_datasum,$i,2);
                          if($debug>=3){plugin_log($plugname,'Zahl: '.$wertstring);}
                          my $wertbetrag = hex($wertstring);
                          if ($wertbetrag == 7) {
                              if ($x07warschon == 1) {
                                  $x07warschon = 0;
                                  $i++;
                                  next;
                              } else {
                                  $x07warschon = 1;
                              }
                          }
                      $rcv_checksum += $wertbetrag;
                          if($debug>=3){plugin_log($plugname,'Summe: '.$rcv_checksum);}
                      $i++;
                  }
                          if($debug>=3){plugin_log($plugname,'Summe def: '.$rcv_checksum);}
              
                  if($debug>=2){plugin_log($plugname,'Checksumme vor der Umwandlung: '.$rcv_checksum);}
                  $rcv_checksum = sprintf "%x\n" , $rcv_checksum; #Mache aus Integer wieder HEX
                  if($debug>=2){plugin_log($plugname,'Checksumme vor der Kürzung: '.$rcv_checksum);}
                  $rcv_checksum = substr($rcv_checksum,-3,2); #Verwende nur die letzten beiden Stellen
                  if($debug>=2){plugin_log($plugname,'Checksumme nach der Kürzung: '.$rcv_checksum);}
                  return $rcv_checksum;
              } #Ende checksum_berechnen
              
               
              
              sub readConf
              {
                  my $confFile = '/etc/wiregate/plugin/generic/conf.d/'.basename($plugname,'.pl').'.conf';
                  if (! -f $confFile)
                  {
                      plugin_log($plugname, " no conf file [$confFile] found!"); 
                  }
                  else
                  {
                      if($debug>=1){plugin_log($plugname, " reading conf file [$confFile].");}
                      open(CONF, $confFile);
                      my @lines = <CONF>;
                      close($confFile);
                      my $result = eval("@lines");
                      ($result) and plugin_log($plugname, "conf file [$confFile] returned result[$result]");
                      if ($@) 
                      {
                          if($debug>=1){plugin_log($plugname, " conf file [$confFile] returned:");}
                          my @parts = split(/\n/, $@);
                          if($debug>=2){plugin_log($plugname, " --> $_") foreach (@parts);}
                      }
                  }
              } # readConf
              Gruss Patrik alias swiss

              Kommentar


                Bleibt noch gleich, wiederholt sich immer wieder:

                Code:
                2013-04-05 23:14:24.195,ConfoAir Test,,0s,syntax error at (eval 56) line 304, near "){"
                syntax error at (eval 56) line 439, near "}
                 }"
                Can't use global @_ in "my" at (eval 56) line 442, near "= $_"
                syntax error at (eval 56) line 474, near ";
                 }"
                syntax error at (eval 56) line 500, near "}
                 }"
                
                2013-04-05 23:14:53.955,ConfoAir Test2,,0s,syntax error at (eval 57) line 304, near ") ne"
                syntax error at (eval 57) line 439, near "}
                 }"
                Can't use global @_ in "my" at (eval 57) line 442, near "= $_"
                syntax error at (eval 57) line 474, near ";
                 }"
                syntax error at (eval 57) line 500, near "}
                Schöne Grüße
                Moritz

                Kommentar


                  In der Zeile 304 ist eine schliessende Klammer nach der 4 zuviel.

                  Müsste so aussehen:

                  Code:
                  while (substr($reciv,0,4) ne '07f0'){
                  Gruss Patrik alias swiss

                  Kommentar


                    Welches Kabel verlegen?

                    Hallo,

                    möchte meine zukünftige ComfoAir 350 (natürlich) auch mit dem wiregate steuern. ComfoAir steht auf dem Dachboden, wiregate im Keller. Habe schon mal zwei normale CAT5-Kabel verlegt, frage mich aber, ob das geeignet ist. Noch kann ich relativ einfach "verbessern".

                    Außerdem würde ich die Comfocontrol ease im EG in einer Ecke an die Wand schrauben und auch da entsprechende Leitungen vorsehen, um auch zeitweise (bis es rund läuft oder bei Fehlern) umklemmen zu können. Was ist denn für die ease zu verlegen? Und sollte ich von der ease direkt zur ComfoAir legen (Umklemmen auf Dachboden) oder besser von der ease in den Keller (nahe wiregate) und dann ggf. von dort weiter zur ComfoAir?

                    Gruß
                    stb

                    Kommentar


                      Hallo stb

                      Die CCEASE braucht im grunde genommen 4 Drähte. Wir verwenden hier meistens Installationskabel für Telefonleitungen. Wo du mitder Leitung der EASE besser hinfährst, hängtvon denörtlichenGegebenheiten ab. z.B. der Leitungslänge. Wenn die distanz zwischen EASE via WG auf den Dachboden nicht länger als ca. 30m ist, könnte das eine Lösung sein. Auch muss man sich fragen, wie gut die KWL auf dem Dachboden erreicht werden kann. Ist dies problemlos möglich, würde ich die Leitung direkt zur KWL ziehen. Einige haben dort zur einfacheren Umschaltung einen Schalter angebracht, der den Pluspol der EASE unterbricht. Somit kann sie schnell ein-/ausgeschaltet werden

                      Wie du die KWL mit dem WG verbindest ist die nächste Frage. Ein LAN Kabel ist da absolut die beste Lösung, da du dann sowohl einen USB-SeriellWandler als auch einen Moxa einsetzen kannst. Du bist also frei in der Wahl des "Übertragungsmediums"
                      Gruss Patrik alias swiss

                      Kommentar


                        Danke für die schnelle Antwort.

                        An die KWL komme ich dran, muss ja ohnehin wg. Filtertausch erreichbar sein. Dann gehe ich direkt vom EG zum Dach mit 2x2x0,8, die Schalter-Idee ist gut. Obwohl ich auch über den Keller nicht mehr als 20m zusammen bekäme...

                        Das CAT hat natürlich die meisten Adern, aber eben auch kleinere Adernquerschnitte (einfaches CAT5 hab ich hier). Schau'n wir mal.

                        Kommentar


                          So...

                          Nun funktioniert das Plugin etwas stabieler. Das Problem, dass immer zwischendurch keine Daten mehr empfangen werden besteht aber weiterhen. Nur sollte nun der Effekt daraus nicht mehr so störend auftreten.

                          Bitte bei Gelegenheit mal ausgibig testen:

                          Code:
                          # Plugin zur Ansteuerung einer Zender ComfoAir
                          # Version 1.6.2 06.04.2013 BETA
                          # Copyright: swiss (https://knx-user-forum.de/members/swiss.html)
                          # Aufbau moeglichst so, dass man unterhalb der Einstellungen nichts veraendern muss!
                          # - Neu mit der Moeglichkeit zur Anbindung über einen Moxa NPort von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
                          # - Neustrukturierung des Codes von Fechter65 (https://knx-user-forum.de/members/fechter65.html)
                          # - Besseres Fehlerhandling bei der Verarbeitung der Reuckmeldungen von swiss (https://knx-user-forum.de/members/swiss.html)
                           
                          
                          ####################
                          ###Einstellungen:###
                          ####################
                          
                          #BITTE ab sofort die Einstellungen unter conf.d vornemen. Damit bleiben die Einstellungen auch bei einem Update erhalten.
                           
                          
                          ######################
                          ##ENDE Einstellungen##
                          ######################
                          
                          
                          #Ab hier nichts mehr aendern.
                          #Hauptverarbeitung
                          
                          
                          #Erzeuge Variablen fuer die Zuordnung der Steuerfunktionen zu den Gruppenadressen:
                          my $ga_stufeabwesend = ''; #1bit Trigger fuer Stufe "Abwesend". 1=Aktivieren
                          my $ga_stufe1 = ''; #1bit Trigger fuer Stufe1. 1=Aktivieren
                          my $ga_stufe2 = ''; #1bit Trigger fuer Stufe2. 1=Aktivieren
                          my $ga_stufe3 = ''; #1bit Trigger fuer Stufe3. 1=Aktivieren
                          my $ga_komforttemp = ''; #GA DPT 9.001 zum setzen der Komforttemperatur
                          my $ga_reset_filter = ''; #1bit Trigger fuer das Zuruecksetzen des Betriebsstundenzaehlers des Filters. 1=Reset
                          my $ga_reset_error = ''; #1bit Trigger fuer das zuruecksetzen der KWL nach einem Fehler. 1=Reset
                           
                          
                          #Hier werden die Gruppenadressen fuer die Rueckmeldungen vergeben: (Nich vergeben = inaktiv)
                          my $ga_status_ventilator_zul = ''; #GA DPT5.001 fuer Status Ventilator Zuluft %
                          my $ga_status_ventilator_abl = ''; #GA DPT5.001 fuer Status Ventilator Abluft %
                          my $ga_status_bypass_prozent = ''; #GA DPT5.001 fuer Status Bypassklappe %
                          my $ga_betriebsstunden_filter = ''; #GA DPT16.000 fuer die Rueckmeldung der Betribsstunden des Filters
                          my $ga_zustand_badschalter = ''; #GA DPT1.001 fuer die Rueckmeldung des Zustandes des Badezimmerschalters
                          my $ga_fehler_filter = ''; #GA DPT 1.001 fuer den Zustand des Filters. 0=OK, 1=Filter Voll
                          my $ga_fehlercode = ''; #GA DPT 16.000 fuer die Ausgabe des Fehlercodes als Text
                          my $ga_aktstufe = ''; #Wert für aktuelle Stufe
                           
                          #Hier werden die Gruppenadressen für die Temperaturen vergeben: (Nicht vergeben=inaktiv)
                          my $ga_aul_temp = ''; #GA DPT 9.001 für die Aussenlufttemperatur
                          my $ga_zul_temp = ''; #GA DPT 9.001 für die Zulufttemperatur
                          my $ga_abl_temp = ''; #GA DPT 9.001 für die Ablufttemperatur
                          my $ga_fol_temp = ''; #GA DPT 9.001 für die Fortlufttemperatur
                           
                          #Zuordnung der Namen fuer die RRD's:
                          my $Name_rrd_AUL = 'KWL_Aussenluft'; #Name RRD Aussenluft
                          my $Name_rrd_ZUL = 'KWL_Zuluft'; #Name RRD Zuluft
                          my $Name_rrd_ABL = 'KWL_Abluft'; #Name RRD Abluft
                          my $Name_rrd_FOL = 'KWL_Fortluft'; #Name RRD Fortluft
                           
                          #Pfad zur seriellen Schnittstelle oder dem USB-Seriell-Wandler:
                          my $schnittstelle = '/dev/ttyUSB-2-4';
                           
                          
                          #Angaben für die Kommunikation über den UDP-Port einer MOXA [diese Einstellungen reichen aus, d.h. auf dem Wiregate muss unter "Seriell/LAN/Socketverbindungen" KEINE Socketverbindung erstellt werden
                          my $socknum = ""; # Eindeutige Nummer des Sockets
                          my $send_ip = ""; # SendeIP (UDP)
                          my $send_port = ""; # Sendeport (UDP)    
                          my $recv_ip = ""; # EmpfangsIP (UDP)
                          my $recv_port = ""; # Empfangsport (UDP)
                           
                          
                          # Kommunikationsart
                          my $Kom_Art = "S"; # "S" = seriell; "M" = Moxa
                           
                          
                          # Dauer einer Abfrage
                          my $Zaehler = "250"; #Mit dieser Variable Zaehler wird beeinflusst, wie lange das Plugin auf den Abschluss einer Rückmeldung der KWL wartet; empfohlener Wert für seriell: 250; für Moxa: 25
                           
                          # Debug level 0 = nur die wichtigsten Infos, 1 = Alle Zustaende, 2 = Rohdaten (nur für Fehlersuche)
                          my $debug=0;
                           
                           
                          #Weitere Variablen die benoetigt werden -> NICHT veraendern!
                          my $seriel;
                          my $sin; #Serial Input = Empangener Datenstrom
                          my $cin; #Counter Input =  Länge des Datenpackets
                          my $laenge; #Länge des empfangenen Datenstrings nachdem kürzen
                           
                          my $checksum = 0; #Checksumme
                          my @hex; #Hilfsarray für die Checksummenberechnung
                          my $x07warschon; #Hilfsvariable für die Checksummenberechnung
                           
                          &readConf(); #conf.d einlesen
                           
                          my $return_value2;
                          my $daten;
                          my $reciv;
                          my $reciv_all;
                          my $ack = pack("H*","07F3");
                          my $rcv_checksum;
                           
                          # Zyklischer Aufruf nach restart, empfang GA oder 1/2 der einstellung rrd (typisch 150sek).
                          $plugin_info{$plugname.'_cycle'}  = 150; 
                          
                          use Device::SerialPort;
                          use Time::Local;
                          
                           
                          #Einrichten der Seriellen Schnittstelle fuer die Kommunikation mit dem ComfoAir falls Schnittstelle auf "S" steht
                          if ($Kom_Art eq "S"){
                                       $seriel = Device::SerialPort->new($schnittstelle) || die "Kann $schnittstelle nicht öffnen! ($!)\n";
                                       $seriel->baudrate(9600);
                                       $seriel->parity("none");
                                       $seriel->databits(8);
                                       $seriel->stopbits(1);
                                       if($debug>=1){plugin_log($plugname,'Schnittstelle: ' . $schnittstelle . ' erfolgreich geöffnet')};
                          }elsif ($Kom_Art eq "M"){
                              if (!$socket[$socknum]) { # socket erstellen
                                      $socket[$socknum] = IO::Socket::INET->new(LocalPort => $recv_port,
                                                                Proto => "udp",
                                                                LocalAddr => $recv_ip,
                                                                PeerPort  => $send_port,
                                                                PeerAddr  => $send_ip,
                                                                ReuseAddr => 1
                                                                 )
                                  or return ("open of $recv_ip : $recv_port failed: $!");
                                  $socksel->add($socket[$socknum]); # add socket to select
                                  $plugin_socket_subscribe{$socket[$socknum]} = $plugname; # Plugin an Socket "anmelden"
                                  if($debug>=1){plugin_log($plugname,'Socket: ' . $socknum . ' erfolgreich geöffnet')};
                                  return "opened Socket $socknum";
                              }
                          }
                          
                           
                          if ($msg{'apci'} eq "A_GroupValue_Write"){ #Wenn ein Telegramm vom KNX empfangen wird, ab hier auswerten
                              if ($msg{'dst'} eq $ga_stufeabwesend && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00990101";
                                  plugin_log($plugname,'Stufe abwesend setzen');
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_stufe1 && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00990102";
                                  plugin_log($plugname,'Stufe 1 setzen');
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_stufe2 && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00990103";
                                  plugin_log($plugname,'Stufe 2 setzen');
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_stufe3 && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00990104";
                                  plugin_log($plugname,'Stufe 3 setzen');
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_komforttemp) {
                                  my $komforttemp = knx_read($msg{'dst'},0,9.001);
                                  plugin_log($plugname,'Komforttemp auf: ' . $komforttemp . '°C setzen');
                                  my $temphex = ($komforttemp + 20)*2; #Rechne die Temperatur fuer die ComfoAir um
                                  $temphex = sprintf "%x" , $temphex; # Mache aus Integer HEX
                                  $daten = "00D301" . $temphex;
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_reset_filter && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00DB0400000001";
                                  plugin_log($plugname,'Filter zurücksetzen');
                                  $return_value2 = command_senden($daten);
                              }elsif ($msg{'dst'} eq $ga_reset_error && knx_read($msg{'dst'},0,1) == 1) {
                                  $daten = "00DB0401000000";
                                  plugin_log($plugname,'Fehler zurücksetzen');
                                  $return_value2 = command_senden($daten);
                              }
                              if($debug>=2){plugin_log($plugname,'ENDE Aufruf durch GA');}
                              return;
                                       
                          } else { # zyklischer Aufruf
                              if(($plugin_info{$plugname.'_time'}+$plugin_info{$plugname.'_cycle'}) >= $plugin_info{$plugname.'_last'}){
                                  return;
                              }
                              $plugin_info{$plugname.'_time'} = time();
                              
                              # Plugin an Gruppenadresse "anmelden", hierdurch wird das Plugin im folgenden bei jedem eintreffen eines Telegramms auf die GA aufgerufen und der obere Teil dieser if-Schleife durchlaufen
                              $plugin_subscribe{$ga_stufeabwesend}{$plugname} = 1;
                              $plugin_subscribe{$ga_stufe1}{$plugname} = 1;
                              $plugin_subscribe{$ga_stufe2}{$plugname} = 1;
                              $plugin_subscribe{$ga_stufe3}{$plugname} = 1;
                              $plugin_subscribe{$ga_komforttemp}{$plugname} = 1;
                              $plugin_subscribe{$ga_reset_filter}{$plugname} = 1;
                              $plugin_subscribe{$ga_reset_error}{$plugname} = 1;
                              
                                       
                              $daten = "00D100";
                              plugin_log($plugname,'Temperatur abrufen');
                              $return_value2 = command_senden($daten);
                              
                              if($ga_status_ventilator_zul && $ga_status_ventilator_abl){ #Nur wenn beide GA's vergeben sind, dann die Zust?nde der Ventilatoren abfragen
                                  $daten = "000B00";
                                  if($debug>=1){plugin_log($plugname,'Ventilator Status abrufen');}
                                  $return_value2 = command_senden($daten);
                              }
                                       
                              if($ga_status_bypass_prozent){ #Nur wenn die GA vergeben ist, dann Zustand Bypassklappe abfragen
                                  $daten = "000D00";
                                  if($debug>=1){plugin_log($plugname,'Bypass Zustand abrufen');}
                                  $return_value2 = command_senden($daten);
                              }
                                       
                              if($ga_betriebsstunden_filter){ #Nur wenn die GA vergeben ist, die Betriebsstunden abfragen
                                  $daten = "00DD00";
                                  if($debug>=1){plugin_log($plugname,'Betriebsstunden abrufen');}
                                  $return_value2 = command_senden($daten);
                              }
                                       
                              if($ga_zustand_badschalter){ #Nur wenn die GA vergeben ist, die Binaereingaenge abfragen
                                  $daten = "000300";
                                  if($debug>=1){plugin_log($plugname,'Binäreingänge abrufen');}
                                  $return_value2 = command_senden($daten);
                              }
                                       
                              if($ga_aktstufe){ #Nur wenn die GA vergeben ist, die Ventilationsstufe abfragen
                                  $daten = "00CD00";
                                  if($debug>=1){plugin_log($plugname,'Ventilationsstufe abrufen');}
                                  $return_value2 = command_senden($daten);
                              }
                                       
                              #Hier werden die Stoermeldungen abgefragt
                              $daten = "00D900";
                              if($debug>=1){plugin_log($plugname,'Störungen abrufen');}
                              $return_value2 = command_senden($daten);
                          
                          
                              if($debug>=2){plugin_log($plugname,'ENDE Zyklische Abfrage');}
                              return;
                          }
                           
                           
                          # Ab hier wird das Datenpaket inklusive Checksumme zusammengestellt und an die ComfoAir uebertragen
                          sub command_senden{
                              my $data = $_[0];
                              if($debug>=2){plugin_log($plugname,'data: ' . $data);} 
                              $checksum = checksum_berechnen($data);
                              if($debug>=2){plugin_log($plugname,'Checksumme aus der Subroutine: '.$checksum);}
                              my $command = pack("H*","07F0" . $data . $checksum . "070F");
                              my $commandhex = $command;
                              
                              $commandhex =~ s/(.)/sprintf("0x%x ",ord($1))/eg;
                              if($debug>=2){plugin_log($plugname,'transmit: ' . $commandhex);} #Zeigt im Pluginlog das fertige Datenpaket, dass uebertragen wird
                                       if ($Kom_Art eq "S"){    
                                                       $seriel->write($command); #Befehl an die ComfoAir senden
                                       } elsif ($Kom_Art eq "M"){    
                                                       $plugin_info{$plugname.'_debug'} = $command;
                                                       syswrite($socket[$socknum], $command);
                                       }
                              $reciv = '';
                              $cin = '';
                              $sin = '';
                                  
                              $|=1;
                              my $exit=0;
                              while($exit < $Zaehler) 
                              {
                                  if ($Kom_Art eq "S"){
                                       ($cin, $sin) = $seriel->read(45);
                                  }elsif ($Kom_Art eq "M"){
                                                           $sin ='';
                                                           if ($fh) { # Antwort auslesen
                                                                          recv($fh,$sin,80,0);
                                                           }
                                                           $cin = length($sin);
                                  }
                                  if($cin > 0){
                                      $sin = unpack "H*", $sin;
                                      $reciv .= $sin;
                                      $exit=0;
                                  }else{
                                      $exit++
                                  }
                                  
                                  if($debug>=2){plugin_log($plugname,'reciv-direkt:     ' . $sin);}
                               
                                      if($reciv =~ /070f/i){           
                                          last;
                                      }
                              }#Ende While   
                          
                              if ($Kom_Art eq "S"){
                                  $seriel->write($ack); #ACK senden
                                  if($debug>=2){plugin_log($plugname,'ACK senden');}
                              } elsif ($Kom_Art eq "M"){
                                  syswrite($socket[$socknum], $ack); #ACK senden
                                  if($debug>=2){plugin_log($plugname,'ACK senden');}
                              }
                          
                              if($reciv eq ""){
                                  if($debug>=2){plugin_log($plugname,'FEHLER: Keine Daten empfangen!');}
                                  return;
                              }
                              
                              while ((length($reciv) > 3) && (substr($reciv,(length($reciv)-4),4) ne '070f')) #solange das Ende nicht 0f lautet
                              {
                                              if($debug>=2){plugin_log($plugname,'String vor Kuerzung Ende: '.$reciv);}
                                              $reciv = substr($reciv,0,-2); #String um die letzten zwei Zeichen kürzen
                                              if($debug>=2){plugin_log($plugname,'String nach Kuerzung Ende: '.$reciv);}
                              }   
                              
                           
                                  #Hier wird der empfangene String um Start- und Endbyte gekürzt
                                  $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
                                  $reciv = substr($reciv,0,($laenge-4)); #Entferne 07f0 vom Ende
                                  
                                  if(substr($reciv,(length($reciv)-4),4) eq '07f3'){
                                      $reciv = substr($reciv,0,($laenge-4));
                                      if($debug>=2){plugin_log($plugname,'String ohne 07f3: '.$reciv);}
                                  }
                          
                                  if($debug>=2){plugin_log($plugname,'Erste 4 Byte des Datenpakets: '.(substr($reciv,0,4)));}
                          
                                                                      
                                  while ((length($reciv) > 3) && (substr($reciv,0,4)) ne '07f0'){
                                      $reciv = substr($reciv,2); #falls noch ein falsche Zeichen am Anfang des Strings enthalten sind, werden diese hier entfernt.
                                      if($debug>=2){plugin_log($plugname,'reciv gekuerzt: '.$reciv);}
                                  }
                                  
                                  $reciv = substr($reciv,4);
                                  if($debug>=2){plugin_log($plugname,'String ohne 07f0 am Anfang: '.$reciv);}
                                  
                                  #Test einer Methode falls aussversehen mehrere Datenpakete auf einmal im Datenstring enthalten sind...
                                  if($reciv =~ /07f307f0/i){
                                      my @dataarray=split(/07f307f0/,$reciv);
                                      $reciv = @dataarray[1];
                                  }
                          
                                                       
                                  #Nun wird die Checksumme gelesen und aus dem Datenstring entfernt
                                  $checksum = 0;
                                  $checksum = substr($reciv,-2,2);
                                  if($debug>=2){plugin_log($plugname,'Checksumme gelesen: '.$checksum);}
                                  $laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
                                  $reciv = substr($reciv,0,($laenge-2));
                                  if($debug>=2){plugin_log($plugname,'Datenpaket ohne Checksumme: '.$reciv);}
                          
                                  #Hier wird die Subroutine für die Berechnung der Checksumme aufgerufen und das Ergebnis in $rcv_checksum zurück gegeben
                                  $rcv_checksum = checksum_berechnen($reciv);
                          
                                                      
                                  if($rcv_checksum eq $checksum){ #Hier wird geprüft ob die Checksumme korrekt ist
                                      if($debug>=2){plugin_log($plugname,'Checksumme OK ');}
                                      if($reciv =~ /00D209/i){ #Wenn die Temperaturen empfangen wurden und die Laenge passt
                          
                                          my $t1 = substr($reciv,8,2);
                                          my $t2 = substr($reciv,10,2);
                                          my $t3 = substr($reciv,12,2);
                                          my $t4 = substr($reciv,14,2);
                                                                                                     
                                          #Hier werden die Temperaturen "decodiert" damit sie einen Sinn ergeben
                                          $t1 =  (hex($t1)/2)-20;
                                          $t2 =  (hex($t2)/2)-20;
                                          $t3 =  (hex($t3)/2)-20;
                                          $t4 =  (hex($t4)/2)-20;
                          
                                          #Wenn die GA's vergeben wurde, die Temperaturen auf die GA's senden
                                          if($ga_aul_temp ne ''){knx_write($ga_aul_temp,$t1,9.001);}
                                          if($ga_zul_temp ne ''){knx_write($ga_zul_temp,$t2,9.001);}
                                          if($ga_abl_temp ne ''){knx_write($ga_abl_temp,$t3,9.001);}
                                          if($ga_fol_temp ne ''){knx_write($ga_fol_temp,$t4,9.001);}
                                          
                                          #Ab hier werden die RRD's mit den aktuellen Temperaturen aktualisiert:
                                          update_rrd($Name_rrd_AUL,"",$t1);
                                          update_rrd($Name_rrd_ZUL,"",$t2);
                                          update_rrd($Name_rrd_ABL,"",$t3);
                                          update_rrd($Name_rrd_FOL,"",$t4);
                                          
                                          plugin_log($plugname,'AUL: ' . $t1 . '°C, ZUL:' . $t2 . '°C, ABL: ' . $t3 . '°C, FOL: ' . $t4 . '°C');
                          
                                      }elsif($reciv =~ /000C06/i){ #Wenn der Status fuer die Ventilatoren empfangen wurden
                                          my $vent_zul = substr($reciv,6,2);
                                          my $vent_abl = substr($reciv,8,2);
                                          plugin_log($plugname,'ZUL: ' . hex($vent_zul) . '% ABL: ' . hex($vent_abl) . '%');
                                          knx_write($ga_status_ventilator_zul,hex($vent_zul),5.001);
                                          knx_write($ga_status_ventilator_abl,hex($vent_abl),5.001);    
                                          
                                      }elsif($reciv =~ /00CE0E/i){ #Wenn Status Ventilatorenstufe empfangen wurden
                                          my $akt_stufe = substr($reciv,22,2);
                                          plugin_log($plugname,'AKT_STUFE: ' . hex($akt_stufe));
                                          knx_write($ga_aktstufe,hex($akt_stufe),5.005);                                                                                
                          
                                      }elsif($reciv =~ /000E04/i){ #Wenn der Status fuer die Bypassklappe empfangen wurden
                                          my $bypass_prozent = substr($reciv,6,2);
                                          plugin_log($plugname,'Bypass: ' . hex($bypass_prozent) . '%');                
                                          knx_write($ga_status_bypass_prozent,hex($bypass_prozent),5.001);
                          
                                      }elsif($reciv =~ /00DE14/i){ #Wenn die Rueckmeldung der Betriebsstunden empfangen wurden
                                          my $betriebsstunden_filter = substr($reciv,36,4);
                                          plugin_log($plugname,'Betriebsstunden: ' . hex($betriebsstunden_filter) . 'h');                 
                                          knx_write($ga_betriebsstunden_filter,hex($betriebsstunden_filter) . 'h',16.000);
                                          
                                      }elsif($reciv =~ /000402/i){ #Wenn die Rueckmeldung der Binaereingaenge empfangen wurden
                                          my $zustand_badschalter = substr($reciv,8,1);
                                          plugin_log($plugname,'Zustand Badezimmerschalter: ' . $zustand_badschalter);                 
                                          knx_write($ga_zustand_badschalter,$zustand_badschalter,1.001);
                                          
                                      }elsif($reciv =~ /00DA11/i){ #Wenn die Rueckmeldung der Stoermeldungen empfangen wurden
                                          my $fehlerAlo = substr($reciv,6,2);
                                          my $fehlerAhi = substr($reciv,30,2);
                                          my $fehlerE = substr($reciv,8,2);
                                          my $fehlerFilter = substr($reciv,22,2);
                                          my $fehlerEA = substr($reciv,24,2);
                                          
                                          my $numAlo = 'A';
                                          my $numAhi = 'A';
                                          my $numE = 'A';
                                          my $numEA = 'A';
                                          
                                          $numAlo .= unpack("B*",pack("H*",$fehlerAlo));
                                          $numAhi .= unpack("B*",pack("H*",$fehlerAhi));
                                          $numE .= unpack("B*",pack("H*",$fehlerE));
                                          $numEA .= unpack("B*",pack("H*",$fehlerEA));
                                          
                                          
                                          $fehlerAlo = reverse($numAlo); #Wandle den Wert in Binaer und drehe die Reihenfolge um. z.B 0x02 = 00000010 = 010000000
                                          $fehlerAlo = index($fehlerAlo,'1')+1; # Zaehle an welcher Stelle die 1 auftaucht (von links gelesen) z.B. 01000000 = INDEX 2 = Fehler2
                                          
                                          if($fehlerAhi ne '00'){
                                              $fehlerAhi = index(reverse($numAhi),'1')+9;
                                          }else{
                                              $fehlerAhi = '';
                                          }
                                          
                                          $fehlerE = index(reverse($numE),'1')+1;
                                          $fehlerEA = index(reverse($numEA),'1')+1;
                                                                                                      
                                          if($fehlerAhi == 16){$fehlerAhi = 0;}
                                                                                                      
                                          if($ga_fehlercode){ #Wenn die GA fuer das uebertragen den Fehlercodes eingertagen wurde, ab hier auswerten
                                              if($fehlerAlo > 0){
                                                  plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAlo);
                                                  knx_write($ga_fehlercode,'A' . $fehlerAlo,16.001);
                                              }elsif($fehlerAhi ne ''){
                                                  plugin_log($plugname,'Aktueller Fehlercode: A' . $fehlerAhi);
                                                  knx_write($ga_fehlercode,'A' . $fehlerAhi,16.001);                        
                                              }elsif($fehlerE > 0){
                                                  plugin_log($plugname,'Aktueller Fehlercode: E' . $fehlerE);
                                                  knx_write($ga_fehlercode,'E' . $fehlerE,16.001);
                                              }elsif($fehlerEA > 0){
                                                  plugin_log($plugname,'Aktueller Fehlercode: EA' . $fehlerEA);
                                                  knx_write($ga_fehlercode,'EA' . $fehlerEA,16.001);
                                              }else{
                                                  plugin_log($plugname,'Aktueller Fehlercode: keiner' );
                                                  knx_write($ga_fehlercode,'keiner' . $fehlerEA,16.001);
                                              }    
                                          }
                                          
                                          if(hex($fehlerFilter) > 0){
                                              plugin_log($plugname,'Aktueller Fehler: Filter Voll');
                                              knx_write($ga_fehler_filter,1,1);
                                          }else{
                                              knx_write($ga_fehler_filter,0,1);
                                          }               
                                      }
                                  }else{
                                      if($debug>=1){plugin_log($plugname,'Checksumme fehlerhaft! Gelesen: '.$checksum.' Berechnet: '.$rcv_checksum);}
                                  }
                          } #ENDE Sub command_senden
                          
                          sub checksum_berechnen {    
                              my $chk_datasum = $_[0];
                              $rcv_checksum =0;
                              my $i;
                               $chk_datasum = $chk_datasum . "AD"; #+173 fuer die Checksummenberechnung
                                  if($debug>=2){plugin_log($plugname,'String für die Berechnung der Checksumme: '.$chk_datasum);}
                              $x07warschon = 0;
                              $laenge = length($chk_datasum);
                                  for($i = 0; $i< $laenge; $i++) {
                                      my $wertstring = substr($chk_datasum,$i,2);
                                      if($debug>=3){plugin_log($plugname,'Zahl: '.$wertstring);}
                                      my $wertbetrag = hex($wertstring);
                                      if ($wertbetrag == 7) {
                                          if ($x07warschon == 1) {
                                              $x07warschon = 0;
                                              $i++;
                                              next;
                                          } else {
                                              $x07warschon = 1;
                                          }
                                      }
                                  $rcv_checksum += $wertbetrag;
                                      if($debug>=3){plugin_log($plugname,'Summe: '.$rcv_checksum);}
                                  $i++;
                              }
                                      if($debug>=3){plugin_log($plugname,'Summe def: '.$rcv_checksum);}
                          
                              if($debug>=2){plugin_log($plugname,'Checksumme vor der Umwandlung: '.$rcv_checksum);}
                              $rcv_checksum = sprintf "%x\n" , $rcv_checksum; #Mache aus Integer wieder HEX
                              if($debug>=2){plugin_log($plugname,'Checksumme vor der Kürzung: '.$rcv_checksum);}
                              $rcv_checksum = substr($rcv_checksum,-3,2); #Verwende nur die letzten beiden Stellen
                              if($debug>=2){plugin_log($plugname,'Checksumme nach der Kürzung: '.$rcv_checksum);}
                              return $rcv_checksum;
                          } #Ende checksum_berechnen
                          
                           
                          
                          sub readConf
                          {
                              my $confFile = '/etc/wiregate/plugin/generic/conf.d/'.basename($plugname,'.pl').'.conf';
                              if (! -f $confFile)
                              {
                                  plugin_log($plugname, " no conf file [$confFile] found!"); 
                              }
                              else
                              {
                                  if($debug>=1){plugin_log($plugname, " reading conf file [$confFile].");}
                                  open(CONF, $confFile);
                                  my @lines = <CONF>;
                                  close($confFile);
                                  my $result = eval("@lines");
                          #        ($result) and plugin_log($plugname, "conf file [$confFile] returned result[$result]");
                                  if ($@) 
                                  {
                                      if($debug>=2){plugin_log($plugname, " conf file [$confFile] returned:");}
                                      my @parts = split(/\n/, $@);
                                      if($debug>=2){plugin_log($plugname, " --> $_") foreach (@parts);}
                                  }
                              }
                          } # readConf
                          Gruss Patrik alias swiss

                          Kommentar


                            Hallo Patrik,

                            Das ganze scheint jetzt tatsächlich stabieler zu laufen! Werte kommen jetzt zwar nur bei jedem zweiten Aufruf vom Plugin, aber dafür momentan zuverlässiger! Danke für die Arbeit daran bis spät in die Nacht!

                            Und noch eine entscheidende Sache, das Senden geht wieder! Das ganze war ein Problem auf der Netzwerk Ebene zwischen WG und IP Interface, habe mich zurück entsinnt schon mal so ein Problem mit dem HS4 und der IP Schnittstelle gehabt zu haben. Sehr merwürdige Angelegenheit, Telegramme Kommen rein, aber senden geht nicht.. Schuld ist dabei ein Netgear Switch. hätte ich gleich drauf kommen können mal in den tatsächlichen WG Busverkehr zu schauen.. aber nun läuft es ja wieder!

                            Das sagt der Log von den letzten paar Stunden:

                            Share Text Post e2r6
                            Schöne Grüße
                            Moritz

                            Kommentar


                              Hallo Moritz

                              Es freut mich, dass der Einsatz was gebracht hat. Wiegesagt... Zufrieden bin ich immer noch nicht. Vor allem weil die Comfoair scheinbar immer mal wieder mehrere Rückmeldungen in ein Paket packt was ich im Plugin noch nicht richtig abfangen kann. Das erkennt man auch daran, das die empfangengn Rückmeldungen zum teil stark von den abgefrageten Werten abweicht.

                              z.B. wird die Temperatur abgefragt und als Antwort kommt die Lüfterstufe. Das Stört das Plugin erst malnicht. Auch diese Antwort wird verarbeitet aber eigentlich würde ich auf die Anfrage der Temperatu auch die Temperatur als Rückmeldun erwarten...

                              Die Frage ist, wiso dieses Verhalten auftritt. Ich kann mir das eigentlich nur so vorstellen...

                              - Da du nur dieses eine Plugin als einziges laufen hast, wird dies in einer Endlosschleife ausgeführt (scheinbar unabhängig von cyklus).

                              Der Moxa hat ein FIFO Puffer. Da die Abfragen so rasend schnell hinter einander ankommen, dass die Comfoair nicht schnell genug antworten kann, gibt es warscheinlich diesen Versatz:

                              Temp anfragen -> Lüfterstufe empfangen

                              Auch könnte das der Grund sein, wesshalb zwischendurch nix mehr empfangen wird. Ich nehme an, dass irgend wann der Puffer überläuft.

                              Warscheinlich könnte man die Empfangstabilität verbessern, wenn man die Laufzeit erhöht. Dies wirkt sich aber nachteilig auf eventuell vorhandene andere Plugins aus. Desshalb darf ich das Plugin nicht künstlich mit z.B. sleep ausbremsen

                              Aber ich lasse mir da mal noch was einfallen. Ich denke das Problem haben aber nur die Nutzer des Moxa. Da dort immer ganze Datenblöcke empfangen werden die dann schon mal mehrere Antworten auf einmal enthalten... Dadurch werden warscheinlich auch die Lücken entstehen. Mal sehen ob sich der Detenverker via Moxa verbessert, wenn man kleinere Blöcke auf einmal einlesen würde...
                              Gruss Patrik alias swiss

                              Kommentar


                                Zitat von swiss Beitrag anzeigen
                                ...bist meines wissens der erste mit einer ComoAir 200 Luxe aber da die auch mit der selben CC EASE gesteuert wird wie die 350 und die 550 sollte die Steuerung über das Plugin trotzdem möglich sein.
                                das hört sich schonmal gut an, hoffe es ist dann auch so...


                                Würde aber jetzt noch gern wissen was ich genau benötige um meine ca. 5m entfernte ComfoAir überhaupt ans Wiregate zu bringen? Möglichst ohne basteln...
                                Zitat von nEiMi Beitrag anzeigen
                                Nochmal kurz etwas zum Anschluss an das Wiregate, bevor ich was falsches bestelle...

                                auf jeden Fall benötige ich den Seriell/RS232 auf USB-Wandler...
                                und dann entweder ein Nullmodem-Kabel mit Buchse auf Stecker(wenn der Wandler am WG steckt) - was auch schwer erhältilich ist, da Nullmodem normalerweise Buchse auf Buchse ist
                                oder
                                USB-Verlängerung(wenn der Wandler an der ComfoAir steckt)

                                Und hiernochmal der Thread in dem ich gerne wissen würde wie ihr eure KWL visualisiert habt:
                                cu Yfkt5A
                                DALI(ABB DG/S1.1), KODI Odroid, TrueNAS, Zehnder ComfoAir 200 L Luxe
                                microk8s-Cluster: HomeAssistant, MusicAssistant, mosquitto, TVHeadend, jellyfin

                                Kommentar

                                Lädt...
                                X