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

175 lines 7.43 kB
"use strict"; /** * Standardized error classification and handling utilities */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ClassifiedTapoError = exports.ErrorClassifier = exports.TapoErrorType = void 0; var TapoErrorType; (function (TapoErrorType) { TapoErrorType["SESSION_EXPIRED"] = "session_expired"; TapoErrorType["DEVICE_BUSY"] = "device_busy"; TapoErrorType["NETWORK_ERROR"] = "network_error"; TapoErrorType["AUTHENTICATION_FAILED"] = "authentication_failed"; TapoErrorType["FEATURE_NOT_SUPPORTED"] = "feature_not_supported"; TapoErrorType["INVALID_PARAMETER"] = "invalid_parameter"; TapoErrorType["DEVICE_OFFLINE"] = "device_offline"; TapoErrorType["PROTOCOL_ERROR"] = "protocol_error"; TapoErrorType["TIMEOUT"] = "timeout"; TapoErrorType["UNKNOWN"] = "unknown"; })(TapoErrorType || (exports.TapoErrorType = TapoErrorType = {})); /** * Centralized error classification utility */ class ErrorClassifier { constructor(customPatterns) { this.patterns = { ...ErrorClassifier.DEFAULT_PATTERNS, ...customPatterns }; } /** * Classify an error into a standard type */ classify(error) { const errorMessage = error.message.toLowerCase(); // Check each pattern category if (this.matchesPatterns(errorMessage, this.patterns.sessionPatterns)) { return this.createClassifiedError(error, TapoErrorType.SESSION_EXPIRED, true, 1000, 'Re-authenticate and retry the operation'); } if (this.matchesPatterns(errorMessage, this.patterns.busyPatterns)) { return this.createClassifiedError(error, TapoErrorType.DEVICE_BUSY, true, 2000, 'Wait and retry the operation'); } if (this.matchesPatterns(errorMessage, this.patterns.networkPatterns)) { return this.createClassifiedError(error, TapoErrorType.NETWORK_ERROR, true, 5000, 'Check network connectivity and device availability'); } if (this.matchesPatterns(errorMessage, this.patterns.authPatterns)) { return this.createClassifiedError(error, TapoErrorType.AUTHENTICATION_FAILED, false, undefined, 'Verify username and password credentials'); } if (this.matchesPatterns(errorMessage, this.patterns.featurePatterns)) { return this.createClassifiedError(error, TapoErrorType.FEATURE_NOT_SUPPORTED, false, undefined, 'Check device capabilities before calling this method'); } if (this.matchesPatterns(errorMessage, this.patterns.parameterPatterns)) { return this.createClassifiedError(error, TapoErrorType.INVALID_PARAMETER, false, undefined, 'Check parameter values and ranges'); } if (this.matchesPatterns(errorMessage, this.patterns.offlinePatterns)) { return this.createClassifiedError(error, TapoErrorType.DEVICE_OFFLINE, true, 10000, 'Check device power and network connection'); } if (this.matchesPatterns(errorMessage, this.patterns.protocolPatterns)) { return this.createClassifiedError(error, TapoErrorType.PROTOCOL_ERROR, true, 3000, 'Retry with different protocol or check device firmware'); } if (this.matchesPatterns(errorMessage, this.patterns.timeoutPatterns)) { return this.createClassifiedError(error, TapoErrorType.TIMEOUT, true, 5000, 'Increase timeout or check network stability'); } // Default to unknown return this.createClassifiedError(error, TapoErrorType.UNKNOWN, true, 3000, 'Check device status and try again'); } /** * Check if error message matches any of the given patterns */ matchesPatterns(errorMessage, patterns) { return patterns.some(pattern => errorMessage.includes(pattern.toLowerCase())); } /** * Create a classified error object */ createClassifiedError(originalError, type, isRetryable, retryAfterMs, suggestedAction) { return { type, originalError, isRetryable, retryAfterMs, suggestedAction }; } /** * Determine if an error is retryable */ static isRetryableError(error) { const classifier = new ErrorClassifier(); const classified = classifier.classify(error); return classified.isRetryable; } /** * Get suggested retry delay for an error */ static getRetryDelay(error) { var _a; const classifier = new ErrorClassifier(); const classified = classifier.classify(error); return (_a = classified.retryAfterMs) !== null && _a !== void 0 ? _a : 3000; } /** * Create a user-friendly error message */ static createUserFriendlyMessage(error) { const classifier = new ErrorClassifier(); const classified = classifier.classify(error); const baseMessage = `${classified.type.replace('_', ' ').toUpperCase()}: ${error.message}`; if (classified.suggestedAction) { return `${baseMessage}\nSuggestion: ${classified.suggestedAction}`; } return baseMessage; } } exports.ErrorClassifier = ErrorClassifier; ErrorClassifier.DEFAULT_PATTERNS = { sessionPatterns: [ 'klap 1002', 'klap -1012', 'session expired', 'invalid terminal uuid', 'session timeout', 'authentication expired' ], busyPatterns: [ 'device busy', 'command timing issue', 'resource busy', 'device not ready', 'operation in progress' ], networkPatterns: [ 'network error', 'connection failed', 'econnrefused', 'enotfound', 'ehostunreach', 'enetunreach', 'connection reset' ], authPatterns: [ 'authentication failed', 'invalid credentials', 'unauthorized', 'login failed', 'wrong username', 'wrong password' ], featurePatterns: [ 'feature not supported', 'method not found', 'not supported', 'unsupported operation', 'capability not available' ], parameterPatterns: [ 'invalid parameter', 'parameter out of range', 'invalid value', 'parameter missing', 'malformed request' ], offlinePatterns: [ 'device offline', 'device not found', 'device unreachable', 'no response', 'device disconnected' ], protocolPatterns: [ 'protocol error', 'malformed response', 'invalid response', 'handshake failed', 'encryption error' ], timeoutPatterns: [ 'timeout', 'request timeout', 'response timeout', 'operation timeout', 'connection timeout' ] }; /** * Enhanced error class with classification */ class ClassifiedTapoError extends Error { constructor(originalError, classifier) { const errorClassifier = classifier !== null && classifier !== void 0 ? classifier : new ErrorClassifier(); const classification = errorClassifier.classify(originalError); super(ErrorClassifier.createUserFriendlyMessage(originalError)); this.name = 'ClassifiedTapoError'; this.classification = classification; } isRetryable() { return this.classification.isRetryable; } getRetryDelay() { return this.classification.retryAfterMs; } getSuggestedAction() { return this.classification.suggestedAction; } } exports.ClassifiedTapoError = ClassifiedTapoError; //# sourceMappingURL=error-classifier.js.map