UNPKG

spruthub-cli

Version:

CLI tool for managing Spruthub smart home devices

209 lines 8.39 kB
import chalk from 'chalk'; import ora from 'ora'; import { Sprut, Schema } from 'spruthub-client'; import configManager from '../config/manager.js'; class SprutHubClientWrapper { client = null; isConnected = false; async getClient(profileName) { if (this.client && this.isConnected) { if (process.env.VERBOSE) { console.log(chalk.cyan(`[DEBUG] Reusing existing connection`)); } return this.client; } const isVerbose = process.env.VERBOSE; const spinner = ora('Connecting to Spruthub device...').start(); try { const credStart = performance.now(); const credentials = await configManager.getCredentials(profileName); if (isVerbose) { const credTime = Math.round((performance.now() - credStart) * 100) / 100; spinner.text = `Loading credentials (${credTime}ms)...`; } // Create a quiet logger for connection phase to avoid spinner interference const quietLogger = { info: () => { }, warn: () => { }, error: () => { }, debug: () => { }, child: () => quietLogger, fatal: () => { }, trace: () => { } }; const clientStart = performance.now(); if (isVerbose) { spinner.text = `Creating Sprut client...`; } this.client = new Sprut({ wsUrl: credentials.wsUrl, sprutEmail: credentials.email, sprutPassword: credentials.password, serial: credentials.serial, logger: quietLogger, defaultTimeout: 10000 }); if (isVerbose) { const clientTime = Math.round((performance.now() - clientStart) * 100) / 100; spinner.text = `Client created (${clientTime}ms), waiting for connection...`; } // Wait for connection const connStart = performance.now(); await this.client.connected(); this.isConnected = true; if (isVerbose) { const connTime = Math.round((performance.now() - connStart) * 100) / 100; spinner.succeed(`Connected to Spruthub device (${connTime}ms)`); console.log(chalk.cyan(`[DEBUG] Connection established successfully`)); console.log(chalk.cyan(`[DEBUG] Client ready for API calls`)); } else { spinner.succeed('Connected to Spruthub device'); } return this.client; } catch (error) { spinner.fail('Failed to connect to Spruthub device'); if (error.message.includes('not found')) { throw new Error(`Authentication failed. Please check your credentials and run "spruthub-cli login" again.\nError: ${error.message}`); } if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) { throw new Error(`Cannot reach Spruthub server. Please check your network connection and server URL.\nError: ${error.message}`); } throw error; } } async execute(method, params = {}, profileName) { const client = await this.getClient(profileName); const spinner = ora(`Executing ${method}...`).start(); try { const result = await client.execute(method, params); if (result.isSuccess) { spinner.succeed(`${method} completed successfully`); return result; } else { spinner.fail(`${method} failed`); throw new Error(`API call failed: ${result.message} (code: ${result.code})`); } } catch (error) { spinner.fail(`${method} failed`); throw error; } } async callMethod(methodName, params = {}, profileName) { const isVerbose = process.env.VERBOSE; const connectionStart = performance.now(); if (isVerbose) { console.log(chalk.cyan(`[DEBUG] Getting client connection...`)); } const client = await this.getClient(profileName); if (isVerbose) { const connectionTime = Math.round((performance.now() - connectionStart) * 100) / 100; console.log(chalk.cyan(`[DEBUG] Connection ready in ${connectionTime}ms`)); console.log(chalk.cyan(`[DEBUG] Raw request to spruthub-client:`)); console.log(chalk.cyan(`[DEBUG] Method: ${methodName}`)); console.log(chalk.cyan(`[DEBUG] Params:`), JSON.stringify(params, null, 2)); } const apiStart = performance.now(); try { // Use spruthub-client's built-in callMethod which handles: // - Schema validation // - Parameter building from schema // - Enhanced methods // - Authentication const result = await client.callMethod(methodName, params); if (isVerbose) { const apiTime = Math.round((performance.now() - apiStart) * 100) / 100; console.log(chalk.cyan(`[DEBUG] API call completed in ${apiTime}ms`)); console.log(chalk.cyan(`[DEBUG] Raw response from spruthub-client:`)); console.log(chalk.cyan(`[DEBUG] Success: ${result.isSuccess}`)); console.log(chalk.cyan(`[DEBUG] Code: ${result.code}`)); console.log(chalk.cyan(`[DEBUG] Message: ${result.message || 'N/A'}`)); console.log(chalk.cyan(`[DEBUG] Data:`), JSON.stringify(result.data, null, 2)); } return result; } catch (error) { if (isVerbose) { const apiTime = Math.round((performance.now() - apiStart) * 100) / 100; console.log(chalk.red(`[DEBUG] API call failed in ${apiTime}ms:`)); console.log(chalk.red(`[DEBUG] Error details:`), JSON.stringify(error, null, 2)); } throw error; } } async testConnection(profileName) { try { const client = await this.getClient(profileName); const result = await client.version(); const config = await configManager.loadConfig(); return { connected: true, version: result.data, profile: profileName || config.currentProfile }; } catch (error) { const config = await configManager.loadConfig(); return { connected: false, error: error.message, profile: profileName || config.currentProfile }; } } async disconnect() { if (this.client) { try { await this.client.close(); } catch { // Ignore close errors } this.client = null; this.isConnected = false; } } // Schema access methods getSchema() { return Schema; } getAvailableMethods() { return Schema.getAvailableMethods(); } getMethodSchema(methodName) { return Schema.getMethodSchema(methodName); } getCategories() { return Schema.getCategories(); } getMethodsByCategory(category) { return Schema.getMethodsByCategory(category); } getRestMethods() { return Schema.getRestMethods(); } // Log streaming methods (WebSocket only) async subscribeLogs(callback) { const client = await this.getClient(); return client.subscribeLogs(callback); } async unsubscribeLogs(subscriptionId) { const client = await this.getClient(); return client.unsubscribeLogs(subscriptionId); } async unsubscribeAllLogs() { const client = await this.getClient(); return client.unsubscribeAllLogs(); } getActiveLogSubscriptions() { if (!this.client) return []; return this.client.getActiveLogSubscriptions(); } } // Export singleton instance export default new SprutHubClientWrapper(); //# sourceMappingURL=client.js.map