Solarbatterieen von Marstek mit MQTT steuern

Begonnen von AlexMuc, 05 Juni 2025, 14:15:09

Vorheriges Thema - Nächstes Thema

AlexMuc

Hallo zusammen,
hat schon wer Erfahrungen mit Akkus dieses Herstellers. Die lassen sich per MQTTT abfragen (sogar der B2500 Saturn), aber im Forum findet meine Suche nix. Angeblich haben die die Besonderheit, das man die regelmäßig abfragen muß.
Da zumindest der B2500 seine Daten nicht von sich aus publizieren, muß man den mit einem MQTT Befehl ,,anstupsen". Dann erhält man alle Daten und muß die dann in ,,lesbare" Daten übersetzen. Hat das hier schonmal jemand versucht und mag mir ein Partisan geben oder seine Integration hier beschreiben? Oder bin ich der erste, der diesen Speicher von Fhem aus überwachen und steuern will?

Alex


AlexMuc

Nein, Marstek wird dort ja nicht erwähnt :-)
Aber ich werde mir das mal zu Gemüte führen und probieren, ob ich damit etwas anfangen kann. Sieht zumindest auf den ersten Blick schon mal zielführend aus um mir was eigenes zusammenzustricken. Inzwischen ist mein Speicher ja auch angekommen, nur fehlt es heute etwas an Sonne, um ihn wieder zu befüllen.
Auf jeden Fall vielen Dank für den Link.

Alex

joachimS

Hi Alex,
Bist du mit mqtt weiter gekommen?
Überlege mir eine Venus E zu kaufen
Lg
Joachim
Gruss
Joachim

(fhem auf Synology DS209, CUL, FS20, FHT, EM, HM, Keymatic, Hue, OpenDTU)

AlexMuc

Hallo Joachim,
ja, bin ich inzwischen. Ich mag aber noch kein Beispiel posten weil da fehlt einfach noch zuviel und bei dem was da ist, da bin ich auch nicht sicher, ob das alles so elegant ist.
Prinzipiell geht das aber, ich denke, deine Venus kann kommen wenn die deine Wünsche erfüllt :-)

AlexMuc

Hallo zusammen,
derzeit hole ich mit dem Attribut periodicCmd 1x pro Minute die Daten vom Speicher ab. Der Aufruf landet aber jedesmal im globalen Logfile und spamt das voll. Gibt es da einen Weg, das zu verhindern.
Leider muß ich die Daten "aktiv" anfordern, der Speicher sendet freiwilig nix an den Server. Das macht er nur, wenn er mit der Cloud verbunden ist und die will ich ja nicht.


Gruß
Alex

TomLee


AlexMuc

#7
Theoretisch ja, praktisch wird irgendwo empfohlen, verbose 3 zu nutzen. Muß ich mal schauen, ob mir dann andere Infos aus dem Log verlorengehen. Oder kann man verbose einzelne devices setzen?
Erledigt, man kann :-)

AlexMuc

So, ich wage mich mal mit einem ersten Versuch aus der Deckung.
Am Ende vom Text gibt 1x das (aktuelle) Device und 1x eine Funktion, mit der ich die Rückgabewerte zerlege und in die Readings stecke. Die kommt in die myUtils. Wer was eleganteres kennt, immer her damit. Andere Verbesserungen sind auch willkommen.
Insbesondere suche ich Beispiele, mit denen ich das "UI" aufpumpen kann. Gerade die Zeitpläne sollte man auch mit Ein/Aus Schalter und Menüs für die Zeiten eingeben können. Da ist also noch viel Erweiterungspotential vorhanden :-)

Das Device:
Internals:
   CFGFN     
   CID        HMJ_2_
   DEF        HMJ_2_
   FUUID      6845db47-f33f-0381-cd1f-3c376463aa32835f
   IODev      MQTT2_FHEM_Server
   LASTInputDev MQTT2_FHEM_Server
   MQTT2_FHEM_Server_CONN MQTT2_FHEM_Server_172.30.81.226_48446
   MQTT2_FHEM_Server_MSGCNT 5979
   MQTT2_FHEM_Server_TIME 2025-06-16 13:41:28
   MSGCNT     5979
   NAME       Marstek_B2500D_Sued
   NR         2553
   STATE      <div style="text-align: right;">
in: <b>298 W</b> ∑<b>2.742 kWh</b>,
out: <b>115 W</b>,
Akku: <b>2.217 kWh</b> (<b>99 %</b>),
<font color="black"><b>23 °C</b></font>
</div>
   TYPE       MQTT2_DEVICE
   eventCount 6554
   periodicCounter 5240
   OLDREADINGS:
   READINGS:
     2025-06-12 16:08:40   IODev           MQTT2_FHEM_Server
     2025-06-16 13:41:28   am              0
     2025-06-16 13:41:28   batBootloaderVersion 107
     2025-06-16 13:41:28   batCapacityExtraBat1 0
     2025-06-16 13:41:28   batCapacityExtraBat2 0
     2025-06-16 13:41:28   batCapacityHost 99
     2025-06-15 09:36:02   batChannelOfCTCH 255
     2025-06-16 13:41:28   batChargeMode   0
     2025-06-16 13:41:28   batCtHostChannel 255
     2025-06-16 13:41:28   batCtHostStatus 0
     2025-06-16 13:41:28   batDeviceID     5
     2025-06-16 13:41:28   batDischargeMode 0
     2025-06-16 13:41:28   batDischargePlan1End 23:56
     2025-06-16 13:41:28   batDischargePlan1Start 0:1
     2025-06-16 13:41:28   batDischargePlan1State 1
     2025-06-16 13:41:28   batDischargePlan1Value 800
     2025-06-16 13:41:28   batDischargePlan2End 23:59
     2025-06-16 13:41:28   batDischargePlan2Start 0:0
     2025-06-16 13:41:28   batDischargePlan2State 0
     2025-06-16 13:41:28   batDischargePlan2Value 80
     2025-06-16 13:41:28   batDischargePlan3End 23:59
     2025-06-16 13:41:28   batDischargePlan3Start 0:0
     2025-06-16 13:41:28   batDischargePlan3State 0
     2025-06-16 13:41:28   batDischargePlan3Value 80
     2025-06-16 13:41:28   batDischargePlan4End 23:59
     2025-06-16 13:41:28   batDischargePlan4Start 0:0
     2025-06-16 13:41:28   batDischargePlan4State 0
     2025-06-16 13:41:28   batDischargePlan4Value 80
     2025-06-16 13:41:28   batDischargePlan5End 23:59
     2025-06-16 13:41:28   batDischargePlan5Start 0:0
     2025-06-16 13:41:28   batDischargePlan5State 0
     2025-06-16 13:41:28   batDischargePlan5Value 80
     2025-06-16 13:41:28   batDischargeState 0
     2025-06-16 13:41:28   batDoD          90
     2025-06-15 12:59:07   batEnergyLimitingInOut 0
     2025-06-16 13:41:28   batEnergyLimiting_InOut 1
     2025-06-15 12:59:07   batEnergyRatedIn 1472
     2025-06-15 12:59:07   batEnergyRatedOut 1989
     2025-06-16 13:41:28   batEnergyRated_In 297
     2025-06-16 13:41:28   batEnergyRated_Out 2010
     2025-06-16 13:41:28   batEnergyTotal  1399
     2025-06-16 13:41:28   batEnergyTotal_Inverter 2009
     2025-06-16 13:41:28   batEnergyTotal_Out 855
     2025-06-16 13:41:28   batEnergyTotal_PV 2742
     2025-06-16 13:41:28   batEnergy_kWh   2217
     2025-06-16 13:41:28   batEnergy_perc  99
     2025-06-16 13:41:28   batExtraBatConnected1 0
     2025-06-16 13:41:28   batExtraBatConnected2 0
     2025-06-15 13:44:08   batFirmware     110.9
     2025-06-16 13:41:28   batFirmwareFC4VersionNumber 202310231502
     2025-06-16 13:41:28   batFirmwareMain 110
     2025-06-16 13:41:28   batFirmwareSub  9
     2025-06-13 23:28:19   batLimitingInOut 0
     2025-06-16 13:41:28   batOutputThreshold 800
     2025-06-16 13:41:28   batPowerOfInverter 115
     2025-06-16 13:41:28   batPowerOut     115
     2025-06-16 13:41:28   batPowerOut1    25
     2025-06-16 13:41:28   batPowerOut2    90
     2025-06-16 13:41:28   batPowerPV1     146
     2025-06-16 13:41:28   batPowerPV2     152
     2025-06-16 13:41:28   batPowerPVin    298
     2025-06-16 13:41:28   batPowerTransmitted 0
     2025-06-16 13:41:28   batSMAutoPowerSize 80
     2025-06-16 13:41:28   batSMClip_1     0
     2025-06-16 13:41:28   batSMClip_2     0
     2025-06-16 13:41:28   batSMClip_3     0
     2025-06-16 13:41:28   batSMConnected  0
     2025-06-16 13:41:28   batScene        0
     2025-06-09 18:39:30   batSensorConnected 0
     2025-06-16 13:41:28   batSignPos_Ex1_Ex2 0
     2025-06-16 13:41:28   batSignPos_Host 2
     2025-06-16 13:41:28   batStateOut1    1
     2025-06-16 13:41:28   batStateOut2    1
     2025-06-16 13:41:28   batStatePV1     1
     2025-06-16 13:41:28   batStatePV2     1
     2025-06-09 08:13:50   batTemp         17
     2025-06-16 13:41:28   batTempAlarmCharging 0
     2025-06-16 13:41:28   batTempAlarmDischarging 0
     2025-06-16 13:41:28   batTempDischarging 0
     2025-06-13 22:37:16   batTempError    0
     2025-06-16 13:41:28   batTempHigh     24
     2025-06-16 13:41:28   batTempLow      23
     2025-06-16 13:41:28   bn              0
     2025-06-13 23:22:18   c1              0
     2025-06-16 13:41:28   ct_t            7
     2025-06-16 13:26:01   ctrl            cd=01
     2025-06-16 13:41:28   sm              0
     2025-06-16 13:41:28   state           info
     2025-06-13 22:37:16   uv              107
Attributes:
   IODev      MQTT2_FHEM_Server
   alias      Marstek Süd
   devicetopic hame_energy/HMJ-2/App/009c17e02b39/ctrl
   event-on-change-reading .*
   group      Zähler
   icon       batterie
   periodicCmd info:1
   readingList HMJ_2_:hame_energy/HMJ-2/device/009c17e02b39/ctrl:.* {splitCD01( $NAME, $EVENT)}
HMJ_2_:hame_energy/HMJ-2/App/009c17e02b39/ctrl:.* ctrl
   room       0_Strom,MQTT2_DEVICE
   setList    info:noArg $DEVICETOPIC cd=01
 plan1_400:noArg $DEVICETOPIC cd=20,md=0,a1=0,b1=00:01,e1=23:56,v1=400
 plan1_600:noArg $DEVICETOPIC cd=20,md=0,a1=0,b1=00:01,e1=23:56,v1=600
 plan1_700:noArg $DEVICETOPIC cd=20,md=0,a1=0,b1=00:01,e1=23:56,v1=700
 plan1_800:noArg $DEVICETOPIC cd=20,md=0,a1=0,b1=00:01,e1=23:56,v1=800
 batDoD:slider,0,5,100 $DEVICETOPIC cd=05,md=$EVTPART1
 planx:slider,0,5,100 $DEVICETOPIC cd=20,md=0,a1=0,b1=00:01,e1=23:56,v1=$EVTPART1


   stateFormat {
    my $tempLow = ReadingsNum( "$name","batTempLow","—-");
    my $tempHigh = ReadingsNum( "$name","batTempHigh","—-");
    my $temp = int( ($tempLow + $tempHigh)/2);
    my $tempCol;
    if ($tempLow < 7) {$tempCol = "blue"}
    elsif ($tempHigh > 28) {$tempCol = "red"}
    else {$tempCol = "black"};
   
    sprintf( qq{<div style="text-align: right;">\
in: <b>%.0f W</b> ∑<b>%.3f kWh</b>, \
out: <b>%.0f W</b>, \
Akku: <b>%.3f kWh</b> (<b>%d %%</b>), \
<font color="$tempCol"><b>%.0f °C</b></font>\
</div>}
    , ReadingsNum( "$name","batPowerPVin",0)
    , ReadingsNum( "$name","batEnergyTotal_PV",0) /1000
    , ReadingsNum( "$name","batPowerOut",0)
    , ReadingsNum( "$name","batEnergy_kWh",0) /1000
    , ReadingsNum( "$name","batEnergy_perc","—-")
    , $temp    )

}
   userReadings batPowerPVin:batPowerPV2.* {
  ReadingsVal( $name, "batPowerPV1", 0)
+ ReadingsVal( $name, "batPowerPV2", 0)
},
batPowerOut:batPowerOut2.* {
  ReadingsVal( $name, "batPowerOut1", 0)
+ ReadingsVal( $name, "batPowerOut2", 0)
},
batFirmware:batFirmwareMain.* {
    sprintf( "%d.%d",
          ReadingsVal( $name, "batFirmwareMain", 0),
        ReadingsVal( $name, "batFirmwareSub", 0)
    )
}
   verbose    2

die Funktion:
sub
splitCD01($$)
{
    my ($dev, $answer) = @_;

    my $ret;
    my $i=0;
    for my $element (split m{,}x, $answer) {
        $i = $i+1;
        my ($key, $val) = split m{=}x, $element;
        if ($key eq "p1") {fhem("setreading $dev batStatePV1 $val"); next };
        if ($key eq "p2") {fhem("setreading $dev batStatePV2 $val"); next  };
        if ($key eq "o1") {fhem("setreading $dev batStateOut1 $val"); next  };
        if ($key eq "o2") {fhem("setreading $dev batStateOut2 $val"); next  };
       
        if ($key eq "w1") {fhem("setreading $dev batPowerPV1 $val"); next  };
        if ($key eq "w2") {fhem("setreading $dev batPowerPV2 $val"); next  };
        if ($key eq "g1") {fhem("setreading $dev batPowerOut1 $val"); next  };
        if ($key eq "g2") {fhem("setreading $dev batPowerOut2 $val"); next  };

        if ($key eq "kn") {fhem("setreading $dev batEnergy_kWh $val"); next };
        if ($key eq "pe") {fhem("setreading $dev batEnergy_perc $val"); next };
        if ($key eq "do") {fhem("setreading $dev batDoD $val"); next };
       
        if ($key eq "cs") {fhem("setreading $dev batChargeMode $val"); next };
        if ($key eq "cd") {fhem("setreading $dev batDischargeMode $val"); next };
        if ($key eq "md") {fhem("setreading $dev batDischargeState $val"); next };

        if ($key eq "d1") {fhem("setreading $dev batDischargePlan1State $val"); next };
        if ($key eq "e1") {fhem("setreading $dev batDischargePlan1Start $val"); next };
        if ($key eq "f1") {fhem("setreading $dev batDischargePlan1End $val"); next };
        if ($key eq "h1") {fhem("setreading $dev batDischargePlan1Value $val"); next };

        if ($key eq "d2") {fhem("setreading $dev batDischargePlan2State $val"); next };
        if ($key eq "e2") {fhem("setreading $dev batDischargePlan2Start $val"); next };
        if ($key eq "f2") {fhem("setreading $dev batDischargePlan2End $val"); next };
        if ($key eq "h2") {fhem("setreading $dev batDischargePlan2Value $val"); next };

        if ($key eq "d3") {fhem("setreading $dev batDischargePlan3State $val"); next };
        if ($key eq "e3") {fhem("setreading $dev batDischargePlan3Start $val"); next };
        if ($key eq "f3") {fhem("setreading $dev batDischargePlan3End $val"); next };
        if ($key eq "h3") {fhem("setreading $dev batDischargePlan3Value $val") ; next };

        if ($key eq "d4") {fhem("setreading $dev batDischargePlan4State $val"); next };
        if ($key eq "e4") {fhem("setreading $dev batDischargePlan4Start $val"); next };
        if ($key eq "f4") {fhem("setreading $dev batDischargePlan4End $val"); next };
        if ($key eq "h4") {fhem("setreading $dev batDischargePlan4Value $val") ; next };
       
        if ($key eq "d5") {fhem("setreading $dev batDischargePlan5State $val"); next };
        if ($key eq "e5") {fhem("setreading $dev batDischargePlan5Start $val"); next };
        if ($key eq "f5") {fhem("setreading $dev batDischargePlan5End $val"); next };
        if ($key eq "h5") {fhem("setreading $dev batDischargePlan5Value $val") ; next };
       
        if ($key eq "vv") {fhem("setreading $dev batFirmwareMain $val"); next };
        if ($key eq "sv") {fhem("setreading $dev batFirmwareSub $val"); next };
        if ($key eq "fc") {fhem("setreading $dev batFirmwareFC4VersionNumber $val") ; next };
        if ($key eq "id") {fhem("setreading $dev batDeviceID $val") ; next };
        if ($key eq "uv") {fhem("setreading $dev batBootloaderVersion $val"); next };

        if ($key eq "tl") {fhem("setreading $dev batTempLow $val") ; next };
        if ($key eq "th") {fhem("setreading $dev batTempHigh $val") ; next };

        if ($key eq "a0") {fhem("setreading $dev batCapacityHost $val"); next };
        if ($key eq "a1") {fhem("setreading $dev batCapacityExtraBat1 $val") ; next };
        if ($key eq "a2") {fhem("setreading $dev batCapacityExtraBat2 $val") ; next };
         if ($key eq "b1") {fhem("setreading $dev batExtraBatConnected1 $val"); next };
        if ($key eq "b2") {fhem("setreading $dev batExtraBatConnected2 $val"); next };
       
        # readonly ?
        if ($key eq "lv") {fhem("setreading $dev batOutputThreshold $val"); next };
       
        # for Smartmeter
        if ($key eq "c0") {fhem("setreading $dev batCtHostChannel $val"); next };
        if ($key eq "c1") {fhem("setreading $dev batCtHostStatus $val"); next };
        if ($key eq "sg") {fhem("setreading $dev batSMConnected $val"); next };
          if ($key eq "sp") {fhem("setreading $dev batSMAutoPowerSize $val") ; next };
        if ($key eq "st") {fhem("setreading $dev batPowerTransmitted $val") ; next };
         if ($key eq "m0") {fhem("setreading $dev batSMClip_1 $val"); next };
        if ($key eq "m1") {fhem("setreading $dev batSMClip_2 $val"); next };
        if ($key eq "m2") {fhem("setreading $dev batSMClip_3 $val"); next };
       
        if ($key eq "m3") {fhem("setreading $dev batPowerOfInverter $val"); next };

       
        # Temperaturalarm on/off = 0/1
         if ($key eq "tc") {fhem("setreading $dev batTempAlarmCharging $val"); next };
        if ($key eq "tf") {fhem("setreading $dev batTempAlarmDischarging $val"); next };
        if ($key eq "tc_dis") {fhem("setreading $dev batTempDischarging $val"); next };
       
        # Tageswerte
         if ($key eq "bc") {fhem("setreading $dev batEnergyTotal $val"); next };
        if ($key eq "bs") {fhem("setreading $dev batEnergyTotal_Out $val"); next };
        if ($key eq "pt") {fhem("setreading $dev batEnergyTotal_PV $val"); next };
        if ($key eq "it") {fhem("setreading $dev batEnergyTotal_Inverter $val"); next };

        if ($key eq "lmf") {fhem("setreading $dev batEnergyLimiting_InOut $val"); next };
        if ($key eq "lmi") {fhem("setreading $dev batEnergyRated_In $val"); next };
        if ($key eq "lmo") {fhem("setreading $dev batEnergyRated_Out $val"); next };

         if ($key eq "cj") {fhem("setreading $dev batScene $val"); next };
       
        # Batterie lädt / entlädt
         if ($key eq "l0") {fhem("setreading $dev batSignPos_Host $val"); next };
        if ($key eq "l1") {fhem("setreading $dev batSignPos_Ex1_Ex2 $val"); next };
       
        # unknown
        #if ($key eq "sm") {fhem("setreading $dev batSMConnected $val"); next };
        #if ($key eq "ct_t") {fhem("setreading $dev bat $val"); next };

        #if ($key eq "am") {fhem("setreading $dev bat $val"); next };


        $ret->{$key}=$val;
    }
    return $ret;
}

joachimS

Zitat von: AlexMuc am 14 Juni 2025, 18:39:22Hallo Joachim,
ja, bin ich inzwischen. Ich mag aber noch kein Beispiel posten weil da fehlt einfach noch zuviel und bei dem was da ist, da bin ich auch nicht sicher, ob das alles so elegant ist.
Prinzipiell geht das aber, ich denke, deine Venus kann kommen wenn die deine Wünsche erfüllt :-)
Habe gehört, dass die Venus doch kein mqtt hat.
Antwort des Supports
Zitat"...vielen Dank für Ihre Nachricht. Derzeit ist der MQTT-Zugang ausschließlich für B2500-Geräte freigegeben. Für den Venus E steht diese Schnittstelle aktuell nicht zur Verfügung, da wir an der Optimierung unserer Sicherheitsmechanismen arbeiten.

Sollte sich in Zukunft etwas am Funktionsumfang ändern und der MQTT-Zugang für den Venus E freigegeben werden, informieren wir unsere Kunden selbstverständlich umgehend.

Vielen Dank für Ihr Verständnis."
Gruss
Joachim

(fhem auf Synology DS209, CUL, FS20, FHT, EM, HM, Keymatic, Hue, OpenDTU)

AlexMuc

Hallo Joachim,
höre dich doch mal dort https://www.photovoltaikforum.com/board/156-pv-anlage-ohne-eeg-balkonkraftwerke/ um. Ich meine, dort war auch die Venus im bei den MQTT Geschichten dabei.
Offiziell ist MQTT wohl nur beim Jupiter freigegeben. Beim B2500 aka Saturn und baugleichen kann man MQTT über ein Webtool von tomquist einschalten. Der macht auch etliche weitere Tools die sehr nützlich sind :-)

AlexMuc

Hallo zusammen,
Kann man eigentlich der Funktion bei der Readinglist auch als 3. Parameter den vorher gesendeten Befehl mitgeben. Anschließend steht ja in State der Befehl aus dem Menü, aber ich brauch den schon bei der Auswertung.
Es sieht nämlich so aus, als ob Marstek bei den Rückgabewerten je nach Befehl die gleichen Kürzel, aber mit unterschiedlicher Bedeutung verwendet.

rudolfkoenig

ZitatKann man eigentlich der Funktion bei der Readinglist auch als 3. Parameter den vorher gesendeten Befehl mitgeben.
set in MQTT2_DEVICE setzt das state Reading, und das kann man mit ReadingsVal($devName,"$state","") abfragen.
Das STATE Internal (das was angezeigt wird) kann man mit Value($devName) abfragen, aber das kann auch was anderes sein, wenn man stateFormat gesetzt hat.

Etwas offtopic: gibt es einen Grund, warum man im obigen Beispiel die Readings einzeln per setreading setzt?
Das ist ziemlich CPU-Intensiv, ich wuerde eher sowas vorschlagen:
sub
splitCD01($$)
{
  my ($dev, $answer) = @_;
  my $ret = {};
  my %map = (
      p1=>"batStatePV1",
      p2=>"batStatePV2",
      o1=>"batStateOut1",
      o2=>"batStateOut2",
      ...
  );
  for my $element (split m{,}x, $answer) {
    my ($key, $val) = split m{=}x, $element;
    $ret{$map{$key}} = $val if($map{$key});
  }
  return $ret;
}
(ungetestet)

AlexMuc

Hallo Rudi,
der Grund: als jemand ohne Ahnung war es der einfachste Weg für mich und es funktionierte:-) Ich werde aber deinen Vorschlag übernehmen den der ist garantiert besser als meine ,,hau drauf Lösung".
Aber eine Frage hab ich dazu doch noch. Bei meiner Vorgehensweise hatte noch vor, einige Werte per Funktion zB 0/1 in off/on umzuwandeln. Kann ich dann anstatt
o2=>"batStateOut2", einfach myFunc( o2,"off","on","Bypass on")=> "batStateOut2" schreiben wenn $val im Bereich von 0 bis 2 ist?

AlexMuc

Hallo Rudi,
du schriebst ungetestet und prompt kommt bei mir der Fehler
Global symbol "%ret" requires explicit package name (did you forget to declare "my %ret"?) at ./FHEM/99_myUtils_B2500.pm line 35.

Damit andere nicht blind in den gleichen Fehler laufen, hier die Korrektur:
Korrekt muß in der Schleife die Zeile
$ret{$map{$key}} = $val if($map{$key});lauten
$ret[b]->[/b]{ $map{$key}} = $val if ($map{ $key});
Auf jeden Fall ist die Funktion jetzt deutlich übersichtlicher.
Ich bin gespannt, ob ich eine Lösung zu Umschreibung diverser Werte herausbekomme. Meine Perlkentnisse beziehe ich nämlich zu 90% aus google :-)