EBUS-ECMD

Aus FHEMWiki

Ebus ECMD

Eine einfache aber sichere Anbindung des eBusd an FHEM kann mit ECMD durchgeführt werden. Es ist egal ob eBusd auf dem selben Raspberry wie FHEM läuft oder ein eigener Raspberry dafür verwendet wird. Die gezeigten Beispiele sind nur eine der Möglichkeiten die zum Ziel führen, sollen aber sicher und schnell zu den ersten Erfolgen führen.

ECMD in FHEM aktivieren

# fhem.cfg
define EBUS ECMD telnet '''ip-raspi-ebusd''':8888   # IP Adresse ebusd einsetzen!!!
attr EBUS classdefs bai00.class=/opt/fhem/FHEM/bai00.cfg
attr EBUS icon usb
attr EBUS requestSeparator 000
attr EBUS room Vaillant

# Die Definition von ECMD, "ip-raspi-ebusd" muss durch die IP-Adresse ersetzt werden. Beispiel:
define EBUS ECMD telnet 192.168.0.10:8888

Die Kommunikation erfolgt über Telnet und 8888 ist das Port über welches kommuniziert werden soll.

ECMD Devices definieren

# fhem.cfg
define Vorlauf ECMDDevice bai00.class
attr Vorlauf IODev EBUS
attr Vorlauf group Vaillant
attr Vorlauf icon sani_supply_temp
attr Vorlauf room Vaillant

define Ruecklauf ECMDDevice bai00.class
attr Ruecklauf IODev EBUS
attr Ruecklauf group Vaillant
attr Ruecklauf icon sani_return_temp
attr Ruecklauf room Vaillant

define PumpeWatt ECMDDevice bai00.class
attr PumpeWatt IODev EBUS
attr PumpeWatt group Vaillant
attr PumpeWatt icon measure_power
attr PumpeWatt room Vaillant

define Fanspeed ECMDDevice bai00.class
attr Fanspeed IODev EBUS
attr Fanspeed group Vaillant
attr Fanspeed icon vent_ventilation_level_automatic
attr Fanspeed room Vaillant

define HKurve ECMDDevice bai00.class
attr HKurve IODev EBUS
attr HKurve group Vaillant
attr HKurve icon temp_control
attr HKurve room Vaillant

Jeder Messwert der von eBusd abgefragt werden soll, muss dazu ein ECMDDevice erstellt werden. Hier ein paar Beispiele mit Vorlauf, Rücklauf, Leistung der Pumpe, Ventilatorgeschwindigkeit und der Heizkurve.

EBUS-USB Interface

Zu jedem ECMDDevice muss es eine Classdefinition in der bai00.cfg geben. Der Name "bai00.cfg" ist frei gewählt und wird in diesen Beispielen verwendet.

ECMD Classdefinition

Um eine Klasse zu definieren muss der dazu benötigte Code in eine eigene Datei (hier bai00.cfg) geschrieben werden.

################## bai00.cfg ###############
# 
#!/usr/bin/perl
# Aussentemperatur
get Aussentemp cmd {"r -f outsidetemp temp\n"}
get Aussentemp expect ".*\n*"
get Aussentemp postproc { $_ }
#
# vorlauftemperatur
get Vorlauf cmd {"r -m 10 status01 temp1.0\n"}
get Vorlauf expect "\d+\.\d+\n\n"
get Vorlauf postproc { sprintf("%5.1f",$_) }
#
# Ruecklauftemperatur
get Ruecklauf cmd {"r -m 10 status01 temp1.1\n"}
get Ruecklauf expect "\d+\.\d+\n\n"
get Ruecklauf postproc { sprintf("%5.1f",$_) }
#
# Pumpenleistung
get PumpeWatt cmd {"r -f PumpPower\n"}
get PumpeWatt expect "\d+\n\n"
get PumpeWatt postproc { sprintf("%5.0f",$_) }
#
# Fanspeed
get Fanspeed cmd {"r -f SDFanSpeed\n"}
get Fanspeed expect "\d+\n\n"
get Fanspeed postproc { sprintf("%5.0f",$_) }  
#
# Heizkurve lesen
get HKurve cmd {"r -f Hc1HeatCurve\n"}
get HKurve expect "\d+\.\d+\n"
get HKurve postproc { sprintf("%3.1f",$_) }
#
# HeizkurveSchreiben
get HeizkurveSchreiben cmd {"write 430 Hc1HeatCurve ".Value("HeizkurveEinstellen")."\n"}
get HeizkurveSchreiben expect ".*\n\n"
get HeizkurveSchreiben postproc  { $_ }

Für jeden Device wird hier festgelegt wie die Befehls Syntax der Abfrage auszusehen hat (cmd) und wie die Ergebnisse zu filtern sind (expect), wie soll das Ausgabeformat sein (postproc). Diese Definitionen bitte in einer neuen Datei "bai00.cfg" speichern.

ECMD Zyklische Abfrage

# Abfrage Timersteuerung, fhem.cfg
define EBUS.Timer at +*00:15:00 get Aussentemp Aussentemp;;get Vorlauf Vorlauf;;get Ruecklauf Ruecklauf;;get PumpeWatt PumpeWatt;get Fanspeed Fanspeed;;get PumpeWatt PumpeWatt;;get HKurve HKurve
attr EBUS.Timer group VaillantControl
attr EBUS.Timer icon time_timer
attr EBUS.Timer room Vaillant
attr EBUS.Timer verbose 0

Um die Daten auch zyklisch vom eBus abholen zu können, wird ein Timer gesetzt. Hier wird alle 15 Minuten abgefragt. Tipp: unwichtige Daten wie Druck oder irgendwelche Zähler definiert man in einem zweiten Timer, welcher dann zB. auf 1 Stunde getriggert wird. So wird die Häufigkeit des Zugriffs auf den eBus entlastet.

ECMD Solar, Warmwasser und Heizkreis

ECMD Beispiel Solar

Im nachfolgenden Beispiel wird der EBUS über das Modul ECMD angebunden und mit drei Klassen von Kommandos versehen. IP Adresse ist jene des Device wo der eBusd läuft.

define EBUS ECMD telnet 192.168.0.192:8888
attr EBUS classdefs HK.SOL.class=/opt/fhem/FHEM/ebus_solar.cfg:HK.WW.class=/opt/fhem/FHEM/ebus_ww.cfg:HK.Hz.class=/opt/fhem/FHEM/ebus_hz.cfg
attr EBUS room System
#--
define HK.Hz ECMDDevice HK.Hz.class
attr HK.Hz IODev EBUS
attr HK.Hz group heating
attr HK.Hz room Verbrauch
#--
define HK.WW ECMDDevice HK.WW.class
attr HK.WW IODev EBUS
attr HK.WW group heating
attr HK.WW room Verbrauch
#--
define HK.SOL ECMDDevice HK.SOL.class
attr HK.SOL IODev EBUS
attr HK.SOL group solarGenerator
attr HK.SOL room Solaranlage
#--
# Diese drei Klassen werden als separate Files mit den in der EBUS-Definition stehenden Dateinamen ''ebus_hz.cfg'', ''ebus_ww.cfg'' sowie ''ebus_solar.cfg'' angelegt. Alle drei ECMD-Devices werden nun zyklisch (z.B. jede Minute) abgefragt mit
define EBUS.Timer at +*00:01:00 get HK.Hz A.Temp;;get HK.Hz state;;get HK.WW state;;get HK.SOL state
attr EBUS.Timer group heatingControl
attr EBUS.Timer room Verbrauch
attr EBUS.Timer verbose 0

ECMD Classdefinition Heizkreis

Die erste Datei definiert die FHEM-Readings für die Abfrage von Außentemperatur und Heizkreis:

# Außentemperatur
get A.Temp cmd {"cyc broad temp\n"}
get A.Temp expect ".*"
get A.Temp postproc { my $rval;\
 if(($_ eq "")||($_ eq "no data stored") ){\
   $rval = "err";\
 }else{\
   $rval=sprintf("%5.2f °C",$_);\
 }\
$rval; }
# Heizkeis HK1
get state cmd {"cyc mv HK1_temp\n"}
get state expect ".*"
get state postproc { my ($bval,$rval,$tval,$pval,$qval,$sval,$xval,$zval);\
my $hash  = $defs{"%NAME"};\
 if( ($_ eq "")||($_ eq "no data stored") ){\
    $bval = "err";\
    $rval = "err";\
    $tval = "err";\
    $pval = "err";\
    $qval = "err";\
    $sval = "err";\
    $xval = "err";\
    $zval = "err";\
 }else{\
    my @values=split(' ',$_);\
    if( $values[0] < 15 ){\
       $bval = "err";\
       $rval = "err";\
       $tval = "err";\
       $pval = "err";\
       $qval = "err";\
       $sval = "err";\
       $xval = "err";\
       $zval = "err";\
    } else { \
       $bval = HzBedarf();\
       $rval = sprintf("%5.2f °C",$values[0]);\
       $tval = sprintf("%5.2f °C",$values[1]);\
       if( $values[2] == 0 ){\
          $pval = "OFF";\
          $qval = "0 0";\
       }elsif( $values[2] == 1 ){\
          $pval = "ON (HK)";\
          $qval = "80 0";\
       }elsif( $values[2] == 2 ){\
          $pval = "ON (WW)";\
          $qval = "0 80";\
       }else{\
          $pval = "unknown";\
          $qval = "err";\
       }\
       $sval = sprintf("%d",$values[2]);\
       $xval = sprintf("%5.2f %5.2f %s %5.2f",\
         $values[0],$values[1],$qval,$bval);\
       $zval = sprintf("VL.T %5.2f °C, RL.T %5.2f °C, %s",\
         $values[0],$values[1],$pval);\
    }\
  }\
readingsSingleUpdate($hash, "VL.T", $rval, 1);\
readingsSingleUpdate($hash, "RL.T", $tval, 1);\
readingsSingleUpdate($hash, "Pumpe", $pval, 1); \
readingsSingleUpdate($hash, "Pumpe.P", $qval, 1); \
readingsSingleUpdate($hash, "Bedarf", $bval, 1); \
readingsSingleUpdate($hash, "Status", $sval, 1);\
readingsSingleUpdate($hash, "reading", $xval, 1);\
$zval; }

ECMD Classdefinition Warmwasserkreis

Die zweite Datei definiert die FHEM-Readings für den Warmwasserkreis:

# Warmwasserkreis
get state cmd {"cyc broad getstatus_WW\n"}
get state expect ".*"
get state postproc { my ($rval,$tval,$bval,$pval,$xval,$zval);\
my $hash  = $defs{"%NAME"};\
 if( ($_ eq "")||($_ eq "no data stored") ){\
    $rval = "err";\
    $tval = "err";\
    $bval = "err";\
    $pval = "err";\
    $xval = "err";\
    $zval = "err";\
 }else{\
    my @values=split(' ',$_);\
    if( $values[1] < 15 ){\
       $rval = "err";\
       $tval = "err";\
       $bval = "err";\
       $pval = "err";\
       $xval = "err";\
       $zval = "err";\
    }else {\
       $rval = sprintf("%5.2f °C",$values[0]);\
       $tval = sprintf("%5.2f °C",$values[1]);\
       $bval = ($values[2] == 80) ? "ON (WW)" : "OFF";\
       $pval = ($values[3] == 1) ? "ON" : "OFF";\
       $xval = sprintf("%5.2f %5.2f %5.2f %d %d",\
        $values[0],0.0,$values[1],$values[2],$values[3]);\
       $zval = sprintf("SF1.T %5.2f °C, %s",\
        $values[0],$pval);\
    }\
  }\
$zval; }

ECMD Classdefinition Solarkreis

Die dritte Datei definiert die FHEM-Readings für den Solarkreis:

# Solarkreis
get state cmd {"cyc broad getstatus_SOL\n"}
get state expect ".*"
get state postproc { my ($rval,$pval,$qval,$lval,$yval,$xval,$zval);\
my $hash  = $defs{"%NAME"};\
 if( ($_ eq "")||($_ eq "no data stored") ){\
    $rval = "err";\
    $pval = "err";\
    $qval = "err";\
    $lval = "err";\
    $yval = "err";\
    $xval = "err";\
    $zval = "err";\
 }else{\
    my @values=split(' ',$_);\
    $rval = sprintf("%5.2f °C",$values[0]);\
    $pval = ($values[1] == 1)?"ON":"OFF";\
    $qval = ($values[1] == 1)?65:0;\
    $lval = sprintf("%5.2f %%",$values[2]);\
    $yval = sprintf("%d",$values[3]);\
    $xval = sprintf("%5.2f %d %5.2f %d",\
      $values[0],$qval,$values[2],$values[3]);\
    $zval = sprintf("Coll.T %5.2f °C, %s",\
      $values[0],$pval);\
 }\
readingsSingleUpdate($hash, "Coll.T", $rval, 1);\
readingsSingleUpdate($hash, "Pumpe", $pval, 1);\
readingsSingleUpdate($hash, "Pumpe.P", $qval." W", 1);\
readingsSingleUpdate($hash, "Load",  $lval, 1);\
readingsSingleUpdate($hash, "Yield", $yval, 1);\
readingsSingleUpdate($hash, "reading", $xval, 1);\
$zval; }

ECMD Classdefinition Heizkurve

Diese Klassendefinition muss in der bai00.cfg gespeichert (angehängt) werden. in diesem Beispiel wurde sie für die Vaillant Calormatic 430 definiert, bei einer Calormatic 470 muss der Text entsprechend geändert werden.

# HeizkurveSchreiben Calormatic 430, bai00.cfg
get HeizkurveSchreiben cmd {"write -c 430 Hc1HeatCurve ".Value("HeizkurveEinstellen")."\n"}
get HeizkurveSchreiben expect ".*\n\n"
get HeizkurveSchreiben postproc  { $_ }

Heizkurve schreiben

#fhem.cfg
define HeizkurveEinstellen dummy
attr HeizkurveEinstellen group Heizkurve_Einstellen
attr HeizkurveEinstellen icon temp_control
attr HeizkurveEinstellen room Vaillant,Heizung
attr HeizkurveEinstellen setList state:0.20,0.40,0.50,0.60,0.70,0.80,0.90,1.00,1.10,1.20,1.30,1.40,1.50,1.60,1.70
attr HeizkurveEinstellen webCmd state

define HeizkurveSchreiben_Click notify HeizkurveEinstellen {\
 fhem("get HeizkurveSchreiben HeizkurveSchreiben");;\
}
attr HeizkurveSchreiben_Click group heatingControl
attr HeizkurveSchreiben_Click room Vaillant

define HeizkurveSchreiben ECMDDevice bai00.class
attr HeizkurveSchreiben IODev EBUS
attr HeizkurveSchreiben group Heizkurve_Einstellen
attr HeizkurveSchreiben room Vaillant

Ein kleines Demo wie man via ECMD und eBus die Daten zurück in die Register der Therme (Calormatic) schreiben kann. Hier wird die Heizkurve verstellt. Ein Dummy wird definiert und über eine setList werden die verschiedenen Kurven vorgegeben. Dank setList kann es zu keinen unerlaubten Eingaben kommen. Über den notify wird die Heizkurve dann schließlich gesetzt.

EBUS-USB Interface

Vorsicht: überlegt euch bitte gut was ihr zurück schreiben wollt, es kann unter Umständen gefährlich für eure Hardware oder Gesundheit werden (zB: Warmwasser auf über 60 Grad). Alles was hier durchgeführt wird, führt ihr auf eigene Verantwortung durch.

Wochenprogramme

Ein gerne benutztes Feature ist die Änderung der Zeitprogramme des Heizgerätes in FHEM. Die Visualisierung ist etwas komplexer weil alles x 7 ist und hier Steuerzeichen beim Zusammensetzen des Sendestrings vorkommen die nicht direkt übergeben werden können. Einzelne Timer können damit nicht geschrieben werden, daher wird bei jeder Timereingabe der komplette String geschrieben. Der Einfachheit halber und wegen der Übersicht sind nur 2 Tagesprogramme (Timer) im Beispielcode vorgesehen (die Calormatic kann 3).

Wochentimer
#####################
#  Timer-Programme  #
#####################
get Mo cmd {"r -f hc1Timer.Monday\n"}
get Mo expect ".*\n\n"
get Mo postproc { Vaillant_Timer($_); }
get Di cmd {"r -f hc1Timer.Tuesday\n"}
get Di expect ".*\n\n"
get Di postproc { Vaillant_Timer($_); }
get Mi cmd {"r -f hc1Timer.Wednesday\n"}
get Mi expect ".*\n\n"
get Mi postproc { Vaillant_Timer($_); }
get Do cmd {"r -f hc1Timer.Thursday\n"}
get Do expect ".*\n\n"
get Do postproc { Vaillant_Timer($_); }
get Fr cmd {"r -f hc1Timer.Friday\n"}
get Fr expect ".*\n\n"
get Fr postproc { Vaillant_Timer($_); }
get Sa cmd {"r -f hc1Timer.Saturday\n"}
get Sa expect ".*\n\n"
get Sa postproc { Vaillant_Timer($_); }
get So cmd {"r -f hc1Timer.Sunday\n"}
get So expect ".*\n\n"
get So postproc { Vaillant_Timer($_); }
get ZeitfensterSchreibenMo cmd {"write -c 430 hc1Timer.Monday ".ReadingsVal("TimeMo","HHMM1",0) . chr(59) . ReadingsVal("TimeMo","HHMM2",0) . chr(59) . ReadingsVal("TimeMo","HHMM3",0) . chr(59) . ReadingsVal("TimeMo","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenMo expect ".*\n\n"
get ZeitfensterSchreibenMo postproc  { $_ }
get ZeitfensterSchreibenDi cmd {"write -c 430 hc1Timer.Tuesday ".ReadingsVal("TimeDi","HHMM1",0) . chr(59) . ReadingsVal("TimeDi","HHMM2",0) . chr(59) . ReadingsVal("TimeDi","HHMM3",0) . chr(59) . ReadingsVal("TimeDi","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenDi expect ".*\n\n"
get ZeitfensterSchreibenDi postproc  { $_ }
get ZeitfensterSchreibenMi cmd {"write -c 430 hc1Timer.Wednesday ".ReadingsVal("TimeMi","HHMM1",0) . chr(59) . ReadingsVal("TimeMi","HHMM2",0) . chr(59) . ReadingsVal("TimeMi","HHMM3",0) . chr(59) . ReadingsVal("TimeMi","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenMi expect ".*\n\n"
get ZeitfensterSchreibenMi postproc  { $_ }
get ZeitfensterSchreibenDo cmd {"write -c 430 hc1Timer.Thursday ".ReadingsVal("TimeDo","HHMM1",0) . chr(59) . ReadingsVal("TimeDo","HHMM2",0) . chr(59) . ReadingsVal("TimeDo","HHMM3",0) . chr(59) . ReadingsVal("TimeDo","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenDo expect ".*\n\n"
get ZeitfensterSchreibenDo postproc  { $_ }
get ZeitfensterSchreibenFr cmd {"write -c 430 hc1Timer.Friday ".ReadingsVal("TimeFr","HHMM1",0) . chr(59) . ReadingsVal("TimeFr","HHMM2",0) . chr(59) . ReadingsVal("TimeFr","HHMM3",0) . chr(59) . ReadingsVal("TimeFr","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenFr expect ".*\n\n"
get ZeitfensterSchreibenFr postproc  { $_ }
get ZeitfensterSchreibenSa cmd {"write -c 430 hc1Timer.Saturday ".ReadingsVal("TimeSa","HHMM1",0) . chr(59) . ReadingsVal("TimeSa","HHMM2",0) . chr(59) . ReadingsVal("TimeSa","HHMM3",0) . chr(59) . ReadingsVal("TimeSa","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenSa expect ".*\n\n"
get ZeitfensterSchreibenSa postproc  { $_ }
get ZeitfensterSchreibenSo cmd {"write -c 430 hc1Timer.Sunday ".ReadingsVal("TimeSo","HHMM1",0) . chr(59) . ReadingsVal("TimeSo","HHMM2",0) . chr(59) . ReadingsVal("TimeSo","HHMM3",0) . chr(59) . ReadingsVal("TimeSo","HHMM4",0) . chr(59) . "24:00" . chr(59) . "24:00" . chr(59) . "selected\n"}
get ZeitfensterSchreibenSo expect ".*\n\n"
get ZeitfensterSchreibenSo postproc  { $_ }

Dieses Beispiel ist für eine Vaillant Calormatic 430, bei einer Calormatic 470 müssen die entsprechenden Zeilen angepasst werden (write -c 430....) .

#########################################################
#
#                      Vaillant_Timer
# Datenstring = 03:30;19:30;20:00;20:00;20:00;20:00;Mo-Fr
#########################################################
# 99_myUtils.pm
sub Vaillant_Timer($)
{
 my @values=split(/[; ]/,$_);
 #-- suppress leading zero ?
 for(my $i=0;$i<7;$i++){ 
   $values[$i]=~s/^0//;
 }
 my $sval=sprintf("%s-%s",$values[0],$values[1]);
 $sval  .=sprintf(", %s-%s",$values[2],$values[3])
   if($values[2] ne $values[3]);
 $sval  .=sprintf(", %s-%s",$values[4],$values[5])
   if($values[4] ne $values[5]);
 return $sval;
}

Oben gezeigtes Beispiel benötigt den Vaillant_Timer (von Prof. Peter Henning). Dieser Code muss in die 99_myUtils.pm gespeichert werden. Unter diesem Link gibt es weiterreichende Informationen zu diesem Thema.