eufy-security-client icon indicating copy to clipboard operation
eufy-security-client copied to clipboard

[Question]: How to perform a simple lock and unlock for Smart Lock C210

Open nyousefzai opened this issue 1 year ago • 2 comments

Ask your question

I attempted to find an example but had no luck. I'm currently stuck and relying on trial and error. Can anyone share an example of how to perform a simple lock and unlock using this library for devices it supports? It does indicate support for Smart Lock C210.

// Main execution
(async () => {
  try {
    const config = {
      username,
      password,
      country: "US",
      language: "en",
    };

    // Initialize and connect
    const eufySecurity = await EufySecurity.initialize(config);
    await eufySecurity.connect();
    console.log("Connected to Eufy!");

    // Lock test
    console.log("\nTesting lock...");

    // Wait between operations
    await new Promise((resolve) => setTimeout(resolve, 5000));

    // Unlock test
    await new Promise((r) => setTimeout(r, 3000));

    // Now you can do eufySecurity.getDevices(), lock/unlock, etc.
    // Get devices
    const devices = await eufySecurity.getDevices();
    console.log(
      "Devices found:",
      devices.map((d) => d.getName())
    );

    const lock = devices.find((d) => d.getName() === "Test_01");
    if (!lock) {
      throw new Error("Lock (Test_01) not found!");
    }
    const rawProps = lock.getRawProperties();
    console.log("Raw properties:");
    console.dir(rawProps, { depth: null });
    console.log("Device Type:", lock.getDeviceType());

    console.log(
      "Lock methods:",
      Object.getOwnPropertyNames(Object.getPrototypeOf(lock))
    );
    lock.updateRawProperty(1276, 4);
  } catch (err) {
    console.error("Error:", err);
  }
})();

nyousefzai avatar Apr 05 '25 00:04 nyousefzai

I'm still trying to figure out this client due to the lack of good documentation. The docs say it's fully supported by this client, but I'm not seeing it. Any help would be greatly appreciated.

const { EufySecurity, CommandName } = require("eufy-security-client");

const username = "[email protected]";
const password = "password-here";

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Get all methods, including inherited ones
const getMethods = (obj) => {
  const methods = [];
  for (let prop in obj) {
    if (typeof obj[prop] === "function") methods.push(prop);
  }
  return methods;
};

async function controlLock(lock, shouldLock, station) {
  try {
    const initial = await lock.getProperties();
    console.log(
      `Initial: ${initial.locked ? "LOCKED" : "UNLOCKED"} (status: ${
        initial.lockStatus
      })`
    );

    if (shouldLock === initial.locked) {
      console.log("Already in desired state");
      return true;
    }

    // Debug methods
    console.log("Lock methods:", getMethods(lock));
    console.log(
      "Station methods:",
      station ? getMethods(station) : "No station"
    );

    // Try direct device unlock
    if (lock.unlock) {
      console.log(`Attempting direct P2P ${shouldLock ? "LOCK" : "UNLOCK"}...`);
      if (shouldLock) {
        console.warn("Direct P2P lock not attempted; method unknown");
      } else {
        await lock.updateR();
        await delay(2000);
        const directState = await lock.getProperties();
        console.log(
          `Direct P2P Check: ${
            directState.locked ? "LOCKED" : "UNLOCKED"
          } (status: ${directState.lockStatus})`
        );
        if (directState.locked === shouldLock) {
          console.log(
            `Direct P2P Success: ${shouldLock ? "LOCKED" : "UNLOCKED"}`
          );
          return true;
        }
      }
    } else {
      console.warn("Direct unlock not supported on lock object");
    }

    // Try station P2P unlock
    if (station && station.isConnected()) {
      console.log(
        `Attempting station P2P ${shouldLock ? "LOCK" : "UNLOCK"}...`
      );
      if (shouldLock) {
        console.warn("Station P2P lock not attempted; method unknown");
      } else if (station.unlock) {
        await station.unlock(lock);
        await delay(2000);
        const p2pState = await lock.getProperties();
        console.log(
          `Station P2P Check: ${
            p2pState.locked ? "LOCKED" : "UNLOCKED"
          } (status: ${p2pState.lockStatus})`
        );
        if (p2pState.locked === shouldLock) {
          console.log(
            `Station P2P Success: ${shouldLock ? "LOCKED" : "UNLOCKED"}`
          );
          return true;
        }
      } else {
        console.warn("Station.unlock not available, trying raw command...");
        // Try raw P2P command
        if (station.executeCommand) {
          await station.executeCommand(
            CommandName.DeviceUnlock,
            lock.getSerial()
          );
          await delay(2000);
          const rawState = await lock.getProperties();
          console.log(
            `Raw P2P Check: ${
              rawState.locked ? "LOCKED" : "UNLOCKED"
            } (status: ${rawState.lockStatus})`
          );
          if (rawState.locked === shouldLock) {
            console.log(
              `Raw P2P Success: ${shouldLock ? "LOCKED" : "UNLOCKED"}`
            );
            return true;
          }
        }
      }
    } else {
      console.warn("No P2P station connection, using cloud API...");
    }

    // Cloud API fallback
    console.log(`Sending cloud ${shouldLock ? "LOCK" : "UNLOCK"}...`);
    const payload = {
      device_sn: lock.getSerial(),
      station_sn: lock.getStationSerial(),
      params: [{ param_type: 6000, param_value: shouldLock ? "4" : "3" }],
    };

    const response = await lock.api.requestEufyCloud({
      method: "POST",
      url: "v1/app/upload_devs_params",
      body: JSON.stringify(payload),
    });

    console.log("[API Response]:", response.body);

    if (response.body.code !== 0) {
      throw new Error(`API failed: ${response.body.msg}`);
    }

    const maxAttempts = 10;
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      await delay(1500);
      const state = await lock.getProperties();
      console.log(
        `Cloud Check #${attempt}: ${
          state.locked ? "LOCKED" : "UNLOCKED"
        } (status: ${state.lockStatus})`
      );
      if (state.locked === shouldLock) {
        console.log(`Cloud Success: ${shouldLock ? "LOCKED" : "UNLOCKED"}`);
        console.log("Verify in Eufy app and physically check lock.");
        return true;
      }
    }
    console.error("State updated in API but lock didn’t move");
    return false;
  } catch (err) {
    console.error("Control lock failed:", err.message);
    return false;
  }
}

async function main() {
  let eufyClient;
  try {
    eufyClient = await EufySecurity.initialize({
      username,
      password,
      country: "US",
      language: "en",
      p2pConnectionSetup: "QUICKEST",
      logging: { level: "debug" },
      persistentStorage: true,
    });

    eufyClient.on("connect", () => console.log("[EVENT] SDK connected"));
    eufyClient.on("p2p session established", (station) => {
      console.log(
        `[EVENT] P2P active with station: ${station?.getSerial() || "unknown"}`
      );
    });
    eufyClient.on("p2p session error", (err) =>
      console.error("[EVENT] P2P error:", err.message)
    );
    eufyClient.on("station added", (station) =>
      console.log(
        `[EVENT] Station: ${station.getSerial()} (Connected: ${station.isConnected()})`
      )
    );
    eufyClient.on("device added", (device) =>
      console.log(`[EVENT] Device: ${device.getName()} (${device.getSerial()})`)
    );

    await eufyClient.connect();
    console.log("Connected to Eufy");

    await delay(5000);
    const devices = await eufyClient.getDevices();
    const stations = await eufyClient.getStations();
    console.log(
      "Devices:",
      devices.map((d) => `${d.getName()} (${d.getSerial()})`).join(", ")
    );
    console.log(
      "Stations:",
      stations
        .map((s) => `${s.getSerial()} (Connected: ${s.isConnected()})`)
        .join(", ")
    );

    const lock = devices.find((d) => d.getName() === "Test_01");
    if (!lock) throw new Error("Lock 'Test_01' not found");
    console.log(
      "Target Lock:",
      lock.getSerial(),
      "Station:",
      lock.getStationSerial()
    );

    const station = stations.find(
      (s) => s.getSerial() === lock.getStationSerial()
    );
    if (station) {
      console.log("Station found:", station.getSerial());
      if (!station.isConnected()) {
        console.log("Connecting to station...");
        await station.connect();
        await delay(2000);
        console.log("[P2P] Station connected:", station.isConnected());
      } else {
        console.log("[P2P] Station already connected");
      }
    } else {
      console.warn("No station found for lock, relying on cloud...");
    }

    const command = process.argv[2]?.toLowerCase();
    if (command === "lock") await controlLock(lock, true, station);
    else if (command === "unlock") await controlLock(lock, false, station);
    else console.log("Usage: node lockUnlock.js <lock|unlock>");
  } catch (err) {
    console.error("Main process error:", err.message);
    process.exit(1);
  } finally {
    if (eufyClient) {
      await eufyClient.close();
      console.log("Connection closed");
    }
  }
}

main().catch((err) => {
  console.error("Unexpected error:", err);
  process.exit(1);
});
Connected to Eufy
[EVENT] Station: T8950T1124360B3B (Connected: false)
[EVENT] Station: T8507K50250700A8 (Connected: false)
[EVENT] Station: T84D0K2024491539 (Connected: false)
[EVENT] Device: Drive way camera (T8950T1124360B3B)
[EVENT] Device: Test_01 (T8507K50250700A8)
[EVENT] Device: Front Door lock (T84D0K2024491539)
[EVENT] SDK connected
Devices: Drive way camera (T8950T1124360B3B), Test_01 (T8507K50250700A8), Front Door lock (T84D0K2024491539)
Stations: T8950T1124360B3B (Connected: true), T8507K50250700A8 (Connected: false), T84D0K2024491539 (Connected: false)
Target Lock: T8507K50250700A8 Station: T8507K50250700A8
Station found: T8502K10250700A8
Connecting to station...
[P2P] Station connected: true
Initial: LOCKED (status: 4)
Lock methods: [
  'setMaxListeners',
  'getMaxListeners',
  'emit',
  'addListener',
  'on',
  'prependListener',
  'once',
  'prependOnceListener',
  'removeListener',
  'off',
  'removeAllListeners',
  'listeners',
  'rawListeners',
  'listenerCount',
  'eventNames'
]
Station methods: [
  'setMaxListeners',
  'getMaxListeners',
  'emit',
  'addListener',
  'on',
  'prependListener',
  'once',
  'prependOnceListener',
  'removeListener',
  'off',
  'removeAllListeners',
  'listeners',
  'rawListeners',
  'listenerCount',
  'eventNames'
]
Direct unlock not supported on lock object
Attempting station P2P UNLOCK...
Control lock failed: This functionality is not implemented or supported by this device
Connection closed
``

nyousefzai avatar Apr 07 '25 20:04 nyousefzai

I have the same door lock and I can pull sensor data but lock and unlock do not work. I am using the Eufy Security add-on in Home Assistant, which uses version 3.2.0 and this door lock is in the supported devices list.

Attempting station P2P UNLOCK... Control lock failed: This functionality is not implemented or supported by this device

Is this model using MQTT instead of P2P?

mainmachine avatar Oct 09 '25 18:10 mainmachine