Miele@Home XKM 3100W Protokollanalyse

Na super. Da hat uns Mile ja schön veräppelt! :mad:
Als sie gesehen haben dass mischo22 ihre App reverse engineered hat, haben sie schnell geschrieben dass wir bloß warten sollen, weil sie ja selbst eine API schreiben.

Dann über ein Jahr später veröffentlichen sie endliche eine API und sie funktioniert nur in Verbindung mit der Cloud! :banghead:

Ich habe schon viel Geld für das WLAN-Modul ausgegeben und jetzt soll ich auch noch mit meinen Daten zahlen?!
Nein danke!

Ich möchte einfach nicht, dass Mile genau weiß wann wie viel und wie lange ich wasche. Alu Hut auf Geschweige denn die NSA das BKA und jeder Hacker der da beim Mile ein und aus geht.

Ich werde in meinem Urlaub versuchen auf die Arbeit von mischo22 aufzubauen und eine Offline API schreiben. Aber so link wie Mile zu seien scheint, traue ich ihnen zu, dass sie mittlerweile die Verschlüsselung geändert haben um uns auszusperren. Mal sehen.

Eins ist klar. Das war meine letzte Mile Maschine!

Für die meisten Nutzer bzw. Entwickler dürfte es von Vorteil sein, unkompliziert auf die Daten von außerhalb des LAN zugreifen zu können. IPS ist da eher untypisch.

Okay … dann ist schon mal klar, welcher Hersteller mir NICHT ins Haus kommt. Meinen Kunden werde ich dann wohl auch etwas anderes empfehlen müssen.

Schade Miele, das klang anfangs alles richtig gut.

Hey Leute,

das ganze hat mir doch keine Ruhe gelassen. :o
Ich habe mal schnell eine Art Gateway in node.js zusammengebastelt: GitHub - Ich-Eben/MieleXKM3100WGateway

Damit bekommt man Zugriff auf seine Miele Geräte ohne sich mit der Verschlüsselung rumschlagen zu müssen.
Danke an mischo22 und phenx! Ihr habt da echt tolle reverse engineering Arbeit geleistet!

Ich verstehen nicht warum Miele - die ja schon vorhandene - API nicht einfach dokumentiert. Eigentlich scheint mir das Ganze nämlich ziemlich solide.

Na ja, ich geh jetzt besser schlafen. Bei Fragen melde ich mich morgen noch mal.

Ich nochmal.

Habe noch ein wenig rum probiert und festgestellt, dass das XKM3100W wohl nur einen Verbindungspartner gleichzeitig akzeptiert. Das bedeutet, wenn ihr die groupId / den groupKey der app nicht kennt, müsst ihr das XKM3100W auf Werkseinstellungen zurück setzen. Das geht einfach über das Menü eures Miele Geräts. (Siehe Bedienungsanleitung)

Nachdem ihr euer Miele Gerät wieder in euer WLAN eingebunden habt, (entweder über die miele@home app oder per WPS) gibt es nun zwei Möglichkeiten:

1. Ihr installiert euch die App Packet Capture und schneidet die Kommunikation zwischen Miele App und Gerät mit. Im ersten Paket welches die App an das Gerät schickt findet ihr die benötigte ID und den Key. Diese könnt ihr nun in der datei mielgeGateway.js in Zeile 19 und 20 eintragen und danach die API nutzen.

oder

2. Ihr nutzt die init Funktion des Gateways um ihn direkt bei dem Miele gerät zu registrieren. Dann könnt ihr euch aber nicht mehr mit der miele@home app mit dem Gerät verbinden, bis ihr das XKM3100W wieder auf Werkseinstellungen setzt.

Installation Gateway:
Um das Gateway ausführen zu können benötigt ihr node.js (google: node.js installieren).
Laded euch die Dateien mieleGateway.js und package.json von Github herunter und kopiert sie in einen Ordner.
Danach mit gedrückter Shift Taste einen Rechtsklick auf den Hintergrund von diesem Ordner.
Eingabeaufforderung hier öffnen
Nun tippt ihr „npm install“ ein.
Danach könnt ihr das Gateway mit „node mieleGateway.js“ starten.
Wenn ihr nun im Webbrowser „http://127.0.0.1:3000/explore/<IP-ADRESSE-MIELE_GERÄT>/“ eingebt, (die IP-Adresse eures Miele Gerätes findet ihr z.B. in eurem Router heraus) könnt ihr euch die Daten des Gerätes anzeigen lassen.
Wenn ein Fehler angezeigt wird, stimmt die groupId / der Key nicht und ihr müsst erst doch ein init ausführen (Erklärung siehe oben).
Um ein init auszuführen einfach das Gateway starten und „http://127.0.0.1:3000/init/<IP-ADRESSE-MIELE_GERÄT>/“ im Browser eingeben. Dort sollte dann so etwas zurück kommen:

[{"Success":{"GroupID":"0000000000000000"}},{"Success":{"GroupKey":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}]

Außerdem habe ich noch herausgefunden wie man das Miele Gerät ohne die Miele@home App in sein WLAN einbinden kann:

GET /WLAN/Scan/ HTTP/1.1 
Accept: application/vnd.miele.v1 + json 
Connection: keep-alive 
Host: 192.168.1.1

HTTP/1.1 200 OK 
Content-Type: application/vnd.miele.v1+json; charset=utf-8 
access-control-allow-origin:* 
access-control-allow-headers:Authorization 
Date: Tue, 01 Jan 2013 00:04:00 GMT 
Content-Length:164

{
 "Result":
 [
  {"SSID":"FreeCookies", "Sec":"WPA", "RSSI":-86},
  {"SSID":"BlaBla", "Sec":"WPA", "RSSI":-67},
  {"SSID":"ItHurtsWhenIP", "Sec":"WPA", "RSSI":-83}
 ]
}

PUT /WLAN/ HTTP/1.1 
Accept: application/vnd.miele.v1 + json 
Content-Type: application / vnd.miele.v1 + json; charset = utf - 8 
Content-Length: 55 
Host: 192.168.1.1

{"SSID":"BlaBla","Sec":2,"Key":"PASSWORD"}

[
 {"Success":{"SSID":"meinz"}},
 {"Success":{"Sec":"WPA"}},
 {"Success":{"Key":"***"}}
]

Mal schauen, wenn ich ganz viel Langeweile habe, schreibe ich dafür vielleicht noch eine App.

Hattest du hier noch Einstellungen in Handy vornehmen müssen? Sobalt ich „Packet Capture“ lauschen lassen geht nichts mehr (Daten kommen eventuell nicht durch den VPN) Certificate-status ist aber ok. (Samsung S8 mit aktuellem Android)

Du meinst damit das erste Paket aus der Prozedur „Gerät hinzufügen“ in der Miele-App?

Hat das mal jemand mit Packet Capture gemacht und kann mir ein paar Tips geben?

Wenn jemand interessiert ist, habe ich schon ein script publiziert, dass sprichst mit der Miele cloud service, und publiziert die dateien durch MQTT. Das script wendet das offizielle API an, und ist hier verfügbar: GitHub - oklona/Miele-MQTT: A very simple script to read data from Miele@home cloud services, and publish using Mosquitto MQTT

Habe dein Skript ausprobiert, nachdem ich die ClientID und ClientKey von Miele bekommen habe. Läuft Prima! Ich versuche jetzt mal nach und nach die einzelnen Statuscodes für eine Waschmaschine zu bekommen. In deinem Skript ist ja ein Geschirrspüler enthalten.

Ja,das ist recht. Ich habe nur ein Geschirrspüler. Du hast die „-d“ Switch probiert? Es ist ein bisschen Arbeit, aber du kannst
im Mobil-app volgen, und sehen, welche beschreibungen gibt. Dann, jedenmahls als der status im App ist verändert, kannst du nochmal der „-d“ Switch versuchen. Danach sollte es ganz einfach sein, die Koden zu „übersetzen“.

Moin,

hat schon mal jemand versucht das Gateway auf einen Linux Rechner zum laufen zu bekommen?

Ich bekomme folgende Meldung:

/home/miele/mieleGateway.js:45
    let match = resourcePath.match(regex);
    ^^^

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:374:25)
    at Object.Module._extensions..js (module.js:417:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Function.Module.runMain (module.js:442:10)
    at startup (node.js:136:18)
    at node.js:966:3

Jemand einen Idee?

Hm nu bin ich schon etwas weiter die Notes Version war wohl zu alt…


->http://192.168.2.40:3000/init/192.168.2.244

{
  "error": 403
}

 ok: false,
     redirect: false,
     clientError: true,
     serverError: false,
     error:
      { Error: cannot PUT /Security/Commissioning/ (403)
          at Response.toError (/home/miele/node_modules/superagent/lib/node/response.js:94:15)
          at ResponseBase._setStatusProperties (/home/miele/node_modules/superagent/lib/response-base.js:123:16)
          at new Response (/home/miele/node_modules/superagent/lib/node/response.js:41:8)
          at Request._emitResponse (/home/miele/node_modules/superagent/lib/node/index.js:752:20)
          at parser (/home/miele/node_modules/superagent/lib/node/index.js:916:38)
          at IncomingMessage.res.on (/home/miele/node_modules/superagent/lib/node/parsers/json.js:19:7)
          at IncomingMessage.emit (events.js:187:15)
          at endReadableNT (_stream_readable.js:1094:12)
          at process._tickCallback (internal/process/next_tick.js:63:19)
        status: 403,
        text: '',
        method: 'PUT',
        path: '/Security/Commissioning/' },
     created: false,


Hallo zusammen

Ich versuche gerade das PHP Skript von oklona auf meinem Raspi zum laufen zu bringen.
Kann mir jemand sagen was mit „Type the full path to your mosquitto_pub binary:“ gemeint ist?:confused:

Besten Dank und Gruss

Hallo zusammen

Dank dem Skript von oklona https://github.com/oklona/Miele-MQTThabe ich mit kleinen Anpassungen die Offizielle API in IPS zum laufen gebracht.:loveips:
Was Ihr damit mach ist euch überlassen, für mich funktioniert es erstmals :slight_smile: sicher nicht perfekt, aber besser als nichts
Einfach euren Benutzernamen (email) und euer Passwort eingeben, zudem benötigt Ihr die „personal Client_Id“ und den „Client_Secret“ den ihr euch unter „developer@miele.com“ beschaffen könnt.

<?
$content="application/x-www-form-urlencoded";
$userid="euere email";
$password="euer pwd";
$country='ch-ch'; // eure Region z.b. DE-DE usw
$client_id="client ID";
$client_secret="client_secret";
$authorization='';
$url="https://api.mcs3.miele.com/thirdparty/auth";
$postdata='email=' . urlencode($userid) . '&password=' . urlencode($password) . '&redirect_uri=%2Fv1%2Fdevices&state=login&response_type=code&client_id=' . $client_id . '&vgInformationSelector=' . $country;
	
$method="POST";
		
$data=getRESTData($url,$postdata,$method,$content);

//var_dump ($data);


if (is_array($data) == FALSE){
	$params=(explode('?',$data))[1];
	foreach (explode('&', $params) as $part) {
		$param=explode("=",$part);	
			if(strstr($param[0],'code') <> FALSE ) {
			$code=$param[1];
			}
	}
}

//echo ($code);



if (strlen($code) >> 0 ) {
	$url='https://api.mcs3.miele.com/thirdparty/token?client_id=' . urlencode($client_id) . '&client_secret=' . $client_secret . '&code=' . $code . '&redirect_uri=%2Fv1%2Fdevices&grant_type=authorization_code&state=token';
	$postdata="";
	$method='POST';
	$data=getRESTData($url,$postdata,$method,$content);
	$access_token = $data["access_token"];
	$refresh_token = $data["refresh_token"];
	$tokenscreated = true;
}

//echo ($tokenscreated);


if (strlen($access_token) >> 0 ) {
	$url='https://api.mcs3.miele.com/v1/devices/';
	$authorization='Bearer ' . $access_token;
	$method='GET';
	$data=getRESTData($url,'',$method,'',$authorization);
	if (array_search("Unauthorized",$data) != "" ) {
		$authorization='Bearer ' . $config['access_token'];
		$method='GET';
		$data=getRESTData($url,'',$method,'',$authorization);
	}
	var_dump($data);
}


foreach ($data as $appliance) {
		$appliance_id=$appliance['ident']['deviceIdentLabel']['fabNumber'];
		echo "Data for appliance: " . $appliance_id . PHP_EOL;
		$appliance_type=$appliance['ident']['type']['value_localized'];
		switch ($appliance_type) {
			case "Clothes Dryer":
				$programStatus=$appliance['state']['status']['value_localized'];
				$programType= $appliance['state']['programType']['value_raw'];
				$programPhaseRaw=$appliance['state']['programPhase']['value_raw'];
				$dryingStep=$appliance['state']['dryingStep']['value_localized'];
				$techType=$appliance['ident']['deviceIdentLabel']['techType'];
				switch ($programPhaseRaw) {
					case "512":
						// Purpose unknown, observed when programmed (without phase) and off.
						$programPhase="Not running";
						break;
					case "513":
						$programPhase="Programm läuft";
						break;
					case "514":
						$programPhase="Trocknen";
						break;
					case "515":
						$programPhase="Machine Iron";
						break;
					case "516":
						$programPhase="Bügelfeucht";
						break;
					case "517":
						$programPhase="Normal";
						break;
					case "518":
						$programPhase="Normal Plus";
						break;
					case "519":
						$programPhase="Abkühlen";
						break;
					case "520":
						$programPhase="Bügelfeucht";
						break;
					case "521":
						$programPhase="Anti-crease";
						break;
					case "522":
						$programPhase="Fertig";
						break;
					case "523":
						$programPhase="Extra Trocken";
						break;
					case "524":
						$programPhase="Bügelfeucht";
						break;
					case "526":
						$programPhase="Befeuchten";
						break;
					case "528":
						$programPhase="Zeitgesteuertes Trocknen";
						break;
					case "529":
						$programPhase="Warmluft";
						break;
					case "530":
						$programPhase="Dampfglätten";
						break;
					case "531":
						$programPhase="Comfort Cooling";
						break;
					case "532":
						$programPhase="Rinse out lint";
						break;
					case "533":
						$programPhase="Rinses";
						break;
					case "534":
						$programPhase="Smoothing";
						break;
					case "538":
						$programPhase="Slightly Dry";
						break;						
					case "539":
						$programPhase="Safety Cooling";
						break;
					default:
						$programPhase="Unknown: " . $programPhaseRaw;
					break;
				}
				$timeleft=sprintf("%'.02d:%'.02d",$appliance['state']['remainingTime'][0],$appliance['state']['remainingTime'][1]);
				$timerunning=sprintf("%'.02d:%'.02d",$appliance['state']['elapsedTime'][0],$appliance['state']['elapsedTime'][1]);
				echo "Appliance type: " . $appliance_type . PHP_EOL;
				echo "Program status: " . $programStatus . PHP_EOL;
				echo "Program type: " . $programType . PHP_EOL;
				echo "Program phase: " . $programPhase . PHP_EOL;
				echo "Time left: " . $timeleft . PHP_EOL;
				echo "Time elapsed: " . $timerunning . PHP_EOL;
				echo "techType: " . $techType . PHP_EOL;
				echo "Prog: " .$dryingStep . PHP_EOL . PHP_EOL;
				break;
			default:
				echo "Appliance type " . $appliance_type . " is not defined. Please define it, or send information to have it added." . PHP_EOL;
				break;
		}
	}










function getRESTData($url,$postdata,$method,$content,$authorization='')
{
	$ch = curl_init($url);                                                                      
	curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);                                                                     
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	$headers=array();
	if(strlen($authorization)>> 0 ) {
		array_push($headers, 'Authorization: ' . $authorization);
	}
	
	if(strlen($content) >> 0 ) {
		array_push($headers, 'Content-Type: ' . $content);
	}
	
	
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
	if ( strcmp($method,"POST" ) == 0 ) {
		curl_setopt($ch,CURLOPT_POSTFIELDS, $postdata);
	}
	$result = curl_exec($ch);
	//echo($result);
	
	if (curl_getinfo($ch,CURLINFO_RESPONSE_CODE) == 302 ) {
		$returndata=curl_getinfo($ch,CURLINFO_REDIRECT_URL);
		//var_dump ($returndata);
	}
	else {
		$returndata=json_decode($result,true);
		//var_dump ($returndata);
	}
	
 return $returndata;
}
?>

Hallo,

soweit ich den Feed richtig gelesen habe gibt es noch kein Modul, das die miele@home-3rd-party-API verwendet?
der vorige Post von danimerz bietet ja schon eine erheblich Teil der Logik zum Verständnis der API.

Da ich meine erstes Geräte gerade bekommen habe (Waschmaschine), im Januar definitiv noch ein zweites Gerät bekomme (Wäschetrockner) , würde ich gerne ein Modul incl. Konfigurator erstellen. Wenn jemand Lust hat, zu testen, würde ich mich freuen.

Die API ist zwar schon etwas dürftig und erstmal nur „read-only“, aber ist ja auch ganz neu, vielleicht entwickelt sich da noch etwas.

demel

Ich teste gern!

Würde mich für einen Test ebenso zu verfügung stellen :slight_smile:

Danke und Gruess

Hallo,

Prima!

ich kommen ganz gut voran, hab meine Waschmaschine bereits grob laufen (ein Testzyklaus dauert so lange, sodaß ich möglich Daten-Konstellationen peu à peu prüfen kann).

Das ist das Modul: GitHub - demel42/IPSymconMieleAtHome: Interface for Miele@Home third-party-API to IP-Symcon (under construction)

Ich habe ein klassisches 3-Teilung:

  1. ein IO-Modul (Miele@Home IO), in dem die Zugangsdaten eingegeben werden (das zuerst anlegen)

  2. ein Konfiguration-Modul (Miele@Home Konfigurator), bei dem man jedes seiner Geräte auswählt, das angelegt werden soll.

  3. das Geräte-Modul (Miele@Home Gerät), das aber nicht von Hand sondern nur über den Konfigurator angelegt wird!

Nun ist es so, das die Felder meiner Meinung nach nicht für alle Gerätetype gleich sind (gibt’s bei der Waschmaschine die Drehzahl), daher lege ich in Abhängigkeit vom Typ bestimmte Felder an.

Noch ist die Doku nicht vorhanden, das kommt in den nächsten Tagen

Welche Gerätetypen habe ihr?

Zur Zeit habe ich noch keine weiteren Geräte mit Miele@Home verbunden, brauche also ein paar Informationen.

Was wäre am besten:

Anlegen der Module in der Reihenfolge IO, Konfigurator und im Konfigurator das oder die Geräte anlegen.
Dann

  • die Geräte-Konfigurationsseite aufrufen
  • Debug aktivieren
  • Daten abrufen betätigen
  • den Debug-Output sicher (Speichern als)

Die Daten mal anschauen, was relevant sein könnte und nachschauen, wie die Felder (zB in der Miele-App) zu betiteln sind.

Wenn ihr mir den Debug-Output des Geräte-Module mit diesen Informationen zukommen lasst, kann ich das fix einbauen …

Gruß
demel

Wow, das ging ja schnell, besten Dank.
Ich habe nur das Problem das ich mich nicht anmelden kann, bzw. wenn ich in der IO Instanz auf Zugang Prüfen klicke erscheint mir die beigefügte Fehlermeldung. Ich kann mir nur vorstellen das es mit den Sprachen zu tun hat… Da ich aus der Schweiz bin muss ich bei „meinem“ skript „ch-ch“ als Sprache einstellen.
Ach ja, mein bestehendes Skript funktioniert mit den gleichen Login Daten…

Zurzeit haben wir nur einen Tumbler der Miele@Home fähig ist. jedoch ist unsere Miele Waschmaschine nicht mehr die neuste und wer weiss, vielleicht bringt das Christkind ja eine neue :wink:

Tumbler Typ = TKR850WP

Gruess Dani

Ok, es ist definitiv die Sprache, habe es lokal im Modul kurz erweitert und funktioniert einwandfrei :slight_smile:
hier noch die Ausgabe aus dem Debug.

data={"status":{"value_raw":3,"value_localized":"Program selected","key_localized":"State"},"programType":{"value_raw":2,"value_localized":"","key_localized":"Programme"},"programPhase":{"value_raw":512,"value_localized":"","key_localized":"Phase"},"remainingTime":[1,51],"startTime":[0,0],"targetTemperature":[{"value_raw":-32768,"value_localized":null,"unit":"Celsius"}],"temperature":[{"value_raw":-32768,"value_localized":null,"unit":"Celsius"},{"value_raw":-32768,"value_localized":null,"unit":"Celsius"},{"value_raw":-32768,"value_localized":null,"unit":"Celsius"}],"signalInfo":false,"signalFailure":false,"signalDoor":true,"remoteEnable":{"fullRemoteControl":true,"smartGrid":false},"light":0,"elapsedTime":[0,0],"dryingStep":{"value_raw":2,"value_localized":"Normal","key_localized":"Drying level"},"ventilationStep":{"value_raw":null,"value_localized":"","key_localized":"Power Level"}}

mieleIOFehler.PNG

Besten Dank an danimerz und demel42 für ihre Arbeit.

Das Modul funktioniert mit dem Trockner TMM843WP. Eine neue Waschmaschine soll demnächst folgen.

Beim Skript hatte ich das Problem, dass meine Zugangsdaten nach einiger Zeit nicht mehr akzeptiert wurden.
Getriggert wurde das Skript alle 60 Sekunden nur bei laufendem Gerät.

Nach Anforderung eines neuen Passwortes in der Miele-App ist der Zugang wieder möglich.