MieleRESTServer -- Miele@home Without Cloud Possible

@akappner

I’m trying to connect my Miele TWD360WP dryer. Wifi credentials were uploaded successfully and I can see the dryer is connected to the wifi network. I successfully generated the keys.json file, but I got stuck at provisioning the keys. When I run the provision-key.sh script, I get error “403 Forbidden” for the http attempt, and “failed: Connection refused” for the https.

Is there anything I should try to solve this?

The complete output is:

./provision-key.sh 192.168.21.62 ./keys.json

Trying HTTP
--2026-02-26 12:20:59--  http://192.168.21.62/Security/Commissioning
Connecting to 192.168.21.62:80... connected.
HTTP request sent, awaiting response... 403 Forbidden
2026-02-26 12:20:59 ERROR 403: Forbidden.

Trying HTTPS
--2026-02-26 12:20:59--  https://192.168.21.62/Security/Commissioning
Connecting to 192.168.21.62:443... failed: Connection refused.

Hi,

you seem to be running into the same problem that @bencope is having. Which is weird because I have a TWD360 and it’s running fine. Have you ever attempted using it with the official app - does that work?

@bencope and @seashrimp: It appears there are some code paths where the device rejects HTTP requests that contain an incorrect time, which would also explain why the official app fails. I have updated the provisioning script to include the current time. If your device is set to a device other than the current time (which it should tell you in the HTTP response), please modify the script to send the same “fake” time. It appears that these devices are programmed to reject HTTP requests that are more than 2 days off from the current time.

@akappner
When inspecting the HTTP reply, I found that the dryer was indeed set to a wrong date and time.

I tried to set several similar “fake” time values in the provisioning script, but it did not help.

I then tried to factory reset the wifi settings on the dryer and repeated the procedure from the beginning. This time, I connected the dryer to a different wifi network and gave it access to the internet (it was not allowed to access the internet before). When inspecting the HTTP reply, the date and time is now correct.

When running the script now, I can see that the times are correct and both match, but the key provisioning still fails with the same errors.

@akappner
I also tested the original Miele App (v4.17.0).

The dryer has already been connected to the wifi (since the provision-wifi.sh script has already been executed before). The Miele App, when given access to the same wifi, immediately found the dryer without any problems. I added the dryer in the App and everything seems to work fine, including the remote start.

When exploring the information about the dryer in the App, I noticed that it says “Wi-Fi module version 03.88” and there seems to be an update available to version “04.05”. (Found when opening the tumble dryer and tapping the Settings cogwheel button in the top right corner and then selecting “Information”.)

Maybe updating it could help. Nevertheless, before running the update, I would prefer to check whether this could really be the cause of the troubles. Would it possible for you to check which version your TWD360 dryer has?

Thanks for the info – quite strange! The good news is that my dryer is HTTP-based, not HTTPS-based, so we should be able to sniff the provisioning. Would you be able to reset it, repeat the provisioning from the official app by running Wireshark or another packet sniffer, and sending me the log?

@akappner I’ve sent you a personal message

Thanks for the packet cap. Strangely, the app provisions the device with a key of only 1… let’s see if we can do the same. Please use the config below to try to connect. Then reset the device, and attempt to provision those same keys through the script as well. If that resolves it, there must have been some change in the software that only allows this weird key to be set. If that does not resolve it, there’s some issue with the provisioning script.

endpoints:
washer:
[…]
groupId: “1111111111111111”
groupKey: “11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111”

Following seashrimps example, I also now have my TWD640WP connected to the official Miele app! Using a completely different unrestricted wifi network.

After checking I could remove and register it again, I tried the provisioning script which failed. So when l re-add it to the app, I ran a packet capture which I’ll DM you, but from the traces in my network monitor and the capture I can’t see any http requests and looks like it’s using some TLS websocket to push the credentials so I’m not sure what to do now.

Hi,

THanks – unfortunately your packet log got truncated (you probably used tcpdump without the -s0 option), but the pairing is all behind TLS anyways, so we cannot see anything passively about the pairing. I’d be interested though in getting a full-length packet dump from you now without disconnecting the device, capturing the mDNS packets it sent.

From what I can tell, yours and seashrimp’s device may have the same issue, and I’m optimistic we’re close to understanding and solving it.

I tried running the server with the “1…” keys, but it doesn’t work.

From the server output, I see that the dryer responds with error 404.

(Details in DM.)

Dear all,
thank you very much for the findings and the great work.

I can confirm a compatible and working Miele WWE380 washer model:

  1. The SSID is Miele@home-{some suffix}, connecting with device serial as password
  2. Miele device is running a DHCP server
  3. Push wifi credentials with provision-wifi.sh
  4. Generate keys with generate-keys.py
  5. Push keys with provision-key.sh
  6. Edit MieleRESTServer.config
  7. Run server

Notes so far:

  • Url should be http://<ip>:5001/generate-summary, because
    http://<ip>:5001/generate-summary/ (with slash) will return 404.
  • http://<ip>:5001/walkdop2tree/washer returns “Internal Server Error”
  • http://<ip>:5001/wakeup/washer returns 404

Please drop me a line if you need any more information.

Well done!

hey, do you need any more info from us?
If I try to grab a full packet dump you want it throughout the entire commissioning cycle of the app?

Yes, that might be helpful! Thanks – also if you can observe the communication module’s hardware string of your machine somehow from your app (e.g. “EK057” or “EK037”), that would be great. Hopeful that we can figure it out with that info.

Hi @becope – check out this issue: Successful connection of TEC575WPNDS , getting basic information · Issue #29 · akappner/MieleRESTServer · GitHub . Looks like someone figured out why your and @seashrimp 's devices didn’t work with the provided provisioning – could you try that process and let me know if it works for you?

I took a different approach and used the SuperVision path. With that I create a fake device in HA and create subscriptions on every local device. So I get push notifications! Also I got the GroupKey used in the official provisioning from the cloud endpoint, so I do not break the official Miele app.

Still in early development, but feel free to take a look: GitHub - tiehfood/ha-miele-at-lan: Home Assistant integration to control Miele devices via local API · GitHub
After initial setup (or if GroupKey is already known) communication is also truly local.

Thank you all for your great reverse engineering, was a great starting point!

This worked for me. It made sense, because I did not remove the devices from the Miele app to begin with. After that it worked perfectly.

Very interesting. I tested it and it worked. I retrieved the GroupID and Key from my previous setup with MieleRESTServer. However, I cannot get all details as expected, like the stop button or elapsed and remaining time for example. The only button I have is Wake.

One thing I cannot get my head around (with any intergration) is whether I can extract more information as mentioned in the Miele 3rd Party API: Miele Developer Portal

I want for example to get this information. Is this even possible?

    
"fillingLevels": 
{  
"twinDosContainer1FillingLevel": 47,
 
"twinDosContainer2FillingLevel": 53,
..
..
  }

You can -- but only for DOP2 supported devices.

@tishylediathysio
Neat port!!

dop2rs was unable to parse the TwinDOS structure you were looking for, but I was able to add support for it based on Only partial device support for G6865-W · Issue #3 · tiehfood/ha-miele-at-lan · GitHub . Now you can run the dop2rs parser (cargo run -- 0540000206310000000000060001100003000104040002040500030401000510002000010700000002072ee000030500000004080000000000050500000007020000081000030001020000020200000304000009100003000102000002020000030400000a10000300010200000205000000030400000b100003000102000002020000030400000e100003000101000002010000030100000f10000300010200000201000003040000100500000011070000001205ffff001310000300010200000203000003040000161000030001020000020500000003040600171000030001020000020500000003040000181000030001020000020203000304000019100003000102000002020000030400001a100003000102000002020000030400001e100003000102000002080000000000030400001f100003000102000002080000000000030400002310000300010200000201000003040000241000030001020000020100000304000025100003000102000002050000000304000026100003000102000002050000000304000028100003000102000002020000030400002a10000300010200000205000000030400002c100003000102000002020200030419002d10000300010200000205000000030400002e100003000102000002010000030400000610002300010401000210000600010400000204000003080000000000040800000000000508000000000006080000000000031000060001020000020500000003050000000405000000050500000006040000041000060001020000020500000003050000000405000000050500000006040000051000060001020000020800000000000308000000000004080000000000050800000000000604000006100006000102000002080000000000030800000000000408000000000005080000000000060400000721000200080002020100030536b0000405000000050500780006100003000102080002026400030400000710000300010208000202000003048c0008100003000102080002022a0003048c000910000300010208000202310003048c00080002020100030536b0000405000000050500730006100003000102080002026400030400000710000300010208000202000003048c0008100003000102080002022a0003048c000910000300010208000202310003048c0008020000090200000a050000000b0400000c0100000d100003000102000002010000030400000e100003000102000002020000030400000f10000300010200000201000003040000100100001410000300010200000208000000000003040000151000030001020000020800000000000304000016100003000102000002080000000000030400001710000300010200000208000000000003040000181000030001020000020200000304000019100003000102080002010000030400001a100003000102000002010000030400001b100006000102080002020000030214000402020005020000060400001c10000300010200000205000000030400001d10000300010200000205000000030400001e10000300010200000205000000030400001f1000030001020000020500000003040000201000030001020000020300000304000021100003000102000002010000030400002210000300010200000201000003040000231000030001020000020500000003040000241000030001020000020200000304000025100003000102000002010000030400002610000300010200000201000003040000091700142f6c2f662f672f6800142f6d00000000000000000000000000000000000000000000000000000000000a0400000b0100) and you'll see all the consumables info decoded.

Perhaps we should start collecting DOP2 trees from different devices to make this less manual.

Oh my! That has worked! :partying_face:

I’ve got both of my devices provisioned with the same group and key and both showing up in homeassistant.

Now I’m dying to see if the wash2dry feature works but I’m so happy someone managed to figure it out!

Thanks again for all the hardwork!