Hacking my Eufy Robot Vac

March 10, 2025

It was a Sunday with nothing planned so having recently bought a Eufy robot vacuum I decided the day was best spent trying to hack into it.

The journey began, as usual, with a few Google searches; "Eufy vacuum node.js", "Eufy Vacuum SDK" etc which returned some interesting results. The top being for a repository called eufy-robovac , a node.js library that utilised the TuyAPI for connecting to local Smart Home devices, so confident this was a good place to start I cloned the git repo and started reading through the setup instructions.

This is where I started to encounter some issue.

The TuyAPI

For the TuyAPI to connect to my robot (named Panic Attack), it needs a device ID and a Local Key. The device ID is needed so the TuyAPI knows which device it's connecting to (you could have many) without needing the device's IP Address, and the local key is really a secret key that the Eufy App/Cloud uses to authenticate with the robot when sending requests to it.

The ADB method

In the documentation for the eufy-robovac package, there's a small section on how to get these values using Android Debug Bridge. So connecting my Android to my computer and running ADB, I opened the Eufy app a few times.

I spent a good hour or so on this approach, clicking around on different sections of the app to see if loading new sections outputted the two values I needed, after some digging I managed to pull out the DEVICE ID from the logs.

Emulation Station

With the first method not working, the second approach was to run an older version of the Eufy app on an Emulator such as BlueStacks. This sounded like a good approach as others had mentioned how the latest versions of the Eufy app had patched a lot of issues including leaking the local key.

I installed BlueStacks, and then an older version of the Eufy app on the emulator. Signed into my account and saw Panic Attack already listed. However, when I went to clicked into the Device view, I got a "Please update your app to access this device" error and unfortunately the logs weren't much help without getting onto the device page itself.

Key Grabber

Finally, I came across a python package called eufy-clean-local-key-grabber . This package emulated a sign-in request to Eufy's servers using an email and password, then grabbed data such as the Home ID, Devices and their IDs and Local Keys. Bingo! I had the two keys needed to run the eufy-robovac demo.

Device: XXX XXX XXX, 
device ID xxxxxxxxxxxxxxxxxxxxxx, 
local key xxxxxxxxxxxxxxxx

Note: When reading about Device IDs and Local Keys, some places mentioned how they were 20 characters and 16 characters respectively, however my own key was over 20 characters but still worked just fine in the end.

The Real Fun begins

With the keys acquired, I plugged them into the eufy-robovac demo and ran the node script.

node demo.js device_id local_key status

Nothing.

The script simply didn't work, so I started toying around with different options passed to the TuyAPI class inside src/index.ts

In here, the TuyAPI is instantiated with an object of options such as the IP and version. This is where I discovered I should be adding the IP of the device to the options, something missed out in the demo config.

// demo.js
let config = {
		deviceId: process.argv[2],
		localKey: process.argv[3],
};

// src/index.ts
this.api = new TuyAPI(
    {
        id: config.deviceId,
        key: config.localKey,
        ip: config.ip,
        port: config.port || 6668,
        version: '3.3',
        issueRefreshOnConnect: true
    }
);

To grab the IP address, I ran Fing on my phone and searched for any devices with port 6668 open, then plugged the IP into the TuyAPI config object.

Running the demo again I managed to connect to the device, however it would instantly disconnect too, I tested several different changes; was the version the correct one? did issueRefreshOnConnect cause issues? but I couldn't get the device to stay connected long enough to send or receive data before the socket disconnected.

This is the part in the story where I spent hours iterating over different config options in an attempt to make contact. Google searches didn't really return anything helpful apart from a few GitHub threads on Home Assistant integrations with the package. A search through the package's open and closed issues also returned no clues as to why I couldn't connect to Panic Attack.

My last resort was to try the TuyAPI directly and see if I could connect for longer, so I cloned the Github Repository for the package, and created a demo.js file in there. The code for the demo looked a little like this

import TuyAPI from "index.js"

const options = {
	id: deviceID,
	key: localKey,
	ip: deviceIP,
	version: 3.3,
	nullPayloadOnJSONError: true,
    issueGetOnConnect: false,
    issueRefreshOnConnect: false,
    issueRefreshOnPing: true,
}

const robot = new TuyAPI(options)

(async () => {
  await device.find();

  await device.connect();

  let status = await device.get();

  device.disconnect();
})();

This didn't work either, however when flicking through the code for the package, I noticed a version 3.4 and 3.5 code too so I swapped out my options.version for 3.4 and then 3.5 and viola!

I had "hacked" Panic Attack.

Turns out, my Eufy vacuum was using a newer protocol version — something not clearly documented anywhere I looked.

This was a very long journey to get here and took most of my Sunday and a few early hours of Monday to get working. To finish it off, I created a small library that fixed the issues I was having with both eufy-robovac and tuyapi packages and sent a command to Panic Attack to go home, which he did.

Now I can write:

await panicAttack.connect()
const status = await panicAttack.status()
await panicAttack.disconnect()

Or stream 10-second updates to the terminal with:

await panicAttack.connect();

panicAttack.device.on("data", data => {
	console.log(data);
});

Packages

Thanks to the following packages for the help!
eufy-robovac
eufy-clean-local-key-grabber
tuyapi

The one I created
eufy-robot-vac-sdk