I'm implementing Tuya's Smart Lock API in my Node.js backend service to programmatically create temporary passwords. Here's my current implementation:
Code: Select all
async function getPasswordTicket(deviceId) { return await tuya.request({ method: "POST", path: `/v1.0/devices/${deviceId}/door-lock/password-ticket`, }); } function encryptPassword(password, ticketKey) { try { validateInput(password, ticketKey); const clientSecret = process.env.TUYA_SECRET_KEY; if (!clientSecret) { throw new Error("TUYA_SECRET_KEY is not configured"); } // Step 1: Decrypt the ticket_key using client secret const decryptedTicketKey = decryptPassword(ticketKey, clientSecret); if (!decryptedTicketKey) { throw new Error("Ticket key decryption failed"); } // Step 2: Encrypt the password using decrypted ticket key const key = CryptoJS.enc.Utf8.parse(decryptedTicketKey); const encrypted = CryptoJS.AES.encrypt(password, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, }); return encrypted.ciphertext.toString(CryptoJS.enc.Hex).toUpperCase(); } catch (error) { console.error("Encryption error:", error); throw error; } } async function createTemporaryPassword({ deviceId, name, password, effectiveTime, invalidTime, phone, timeZone = "+01:00", bookingId = null, }) { try { const ticketResponse = await getPasswordTicket(deviceId); const encryptedPassword = await encryptPassword( password, ticketResponse.result.ticket_key ); console.log("Encrypted Password:", encryptedPassword); const requestBody = { name, password: encryptedPassword, password_type: "ticket", ticket_id: ticketResponse.result.ticket_id, effective_time: effectiveTime, invalid_time: invalidTime, time_zone: timeZone, type: 0, }; if (phone) requestBody.phone = phone; const response = await tuya.request({ method: "POST", path: `/v1.0/devices/${deviceId}/door-lock/temp-password`, body: requestBody, }); if (bookingId && response.success) { // Find the booking first const booking = await Booking.findById(bookingId); if (booking) { // Check if the device exists in tuya_lock array const lockIndex = booking.tuya_lock.findIndex( (lock) => lock.device_id === deviceId ); if (lockIndex >= 0) { // Device exists, update its passwords await Booking.findOneAndUpdate( { _id: bookingId, "tuya_lock.device_id": deviceId }, { $push: { "tuya_lock.$.passwords": { name, effectiveTime, invalidTime, code: password, code_id: response.result.id.toString(), }, }, }, { new: true } ); } else { // Device doesn't exist, add new device to array await Booking.findByIdAndUpdate( bookingId, { $push: { tuya_lock: { device_id: deviceId, passwords: [ { name, effectiveTime, invalidTime, code: password, code_id: response.result.id.toString(), }, ], }, }, }, { new: true } ); } } } return response; } catch (error) { console.error("Error creating temporary password:", error); throw error; } }
The API call returns a success response and I can verify the password is created because:
It appears in the Tuya app's password list
Attempting to create the same password via app returns "lock pwd repeat" error
Despite successful API response and password creation, the password fails verification at the physical lock (error: "verify failed"). The same password creation flow works when done through Tuya's app.
Questions:
Are there additional API endpoints I need to call after password creation to sync with the physical lock?
Is there a specific format or encryption required for the password parameter that isn't documented?
Does anyone have a working example of creating a verified temporary password via their API?
I've reviewed the API documentation (https://developer.tuya.com/en/docs/clou ... gsgd4cgysr) but couldn't find any additional required steps
Lock details:
Model category: jtmspro
API version: v1.0