UNPKG

node-red-contrib-tplink-tapo-connect-api

Version:

This unofficial node-RED node allows connection to TP-Link Tapo devices. This project has been enhanced with AI support to enable new features. Starting with v0.50, we have added support for the KLAP protocol. To prioritize the operation of this node, we

104 lines 4.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TapoAuth = void 0; const http_client_1 = require("./http-client"); const crypto_1 = require("./crypto"); class TapoAuth { constructor(ip, credentials) { this.httpClient = new http_client_1.TapoHttpClient(ip); this.credentials = credentials; } async authenticate() { try { console.log('Starting authentication...'); const handshakeResponse = await this.performHandshake(); console.log('Handshake successful, performing login...'); const loginResponse = await this.performLogin(handshakeResponse); console.log('Login successful'); this.session = { sessionKey: handshakeResponse.key, sessionId: handshakeResponse.session, token: loginResponse.token }; return this.session; } catch (error) { console.error('Authentication error details:', error); // Handle specific Tapo error codes if ((error === null || error === void 0 ? void 0 : error.errorCode) === -1010) { const detailedMessage = [ 'Authentication failed with error -1010.', 'This typically indicates one of the following issues:', '1. Incorrect username or password', '2. Device requires local account instead of cloud account', '3. Device is not properly configured for remote access', '4. Account may be locked or suspended', '', 'Troubleshooting steps:', '- Verify credentials are correct in the Tapo app', '- Try using device local account if available', '- Check if device is online and accessible', '- Ensure device firmware is up to date' ].join('\n'); throw new Error(detailedMessage); } throw new Error(`Authentication failed: ${error}`); } } async performHandshake() { const keyPair = crypto_1.TapoCrypto.generateKeyPair(); const handshakeRequest = { method: 'handshake', params: { key: crypto_1.TapoCrypto.base64Encode(keyPair.publicKey) } }; const response = await this.httpClient.post('/app', handshakeRequest); return response.result; } async performLogin(handshake) { console.log('Preparing login with username:', this.credentials.username); // Try different credential encoding methods for compatibility const credentials = { username: this.credentials.username, password: this.credentials.password }; const loginRequest = { method: 'login_device', params: credentials }; const encryptedRequest = this.encryptRequest(loginRequest, handshake.key); const response = await this.httpClient.post('/app', { method: 'securePassthrough', params: encryptedRequest }, { Cookie: `TP_SESSIONID=${handshake.session}` }); return response.result; } async secureRequest(request) { if (!this.session) { throw new Error('Not authenticated. Call authenticate() first.'); } const encryptedRequest = this.encryptRequest(request, this.session.sessionKey); const response = await this.httpClient.post('/app', { method: 'securePassthrough', params: encryptedRequest }, { Cookie: `TP_SESSIONID=${this.session.sessionId}`, Authorization: `Bearer ${this.session.token}` }); return response.result; } encryptRequest(request, key) { const requestString = JSON.stringify(request); const iv = crypto_1.TapoCrypto.generateRandomString(16); const encrypted = crypto_1.TapoCrypto.aesEncrypt(requestString, key, iv); return { request: encrypted }; } getSession() { return this.session; } isAuthenticated() { return !!this.session; } clearSession() { this.session = undefined; } } exports.TapoAuth = TapoAuth; //# sourceMappingURL=auth.js.map