UNPKG

homebridge-tuya-laundry

Version:

Allows washer/dryer cycle completion notifications using Tuya smart plugs with power meter, now using local control.

278 lines 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandHandler = void 0; const powerConsumptionTracker_1 = require("./powerConsumptionTracker"); const deviceManager_1 = require("./deviceManager"); class Color { static colorize(text, color) { return `${color}${text}${this.reset}`; } static info(text) { return this.colorize(text, this.cyan); } static success(text) { return this.colorize(text, this.green); } static warning(text) { return this.colorize(text, this.yellow); } static error(text) { return this.colorize(text, this.red); } } Color.reset = '\x1b[0m'; Color.red = '\x1b[31m'; Color.green = '\x1b[32m'; Color.yellow = '\x1b[33m'; Color.blue = '\x1b[34m'; Color.magenta = '\x1b[35m'; Color.cyan = '\x1b[36m'; class CommandHandler { constructor(tuyaApiService, log) { this.tuyaApiService = tuyaApiService; this.log = log; this.selectedCommand = ''; this.selectedPlug = null; this.smartPlugsCache = []; this.generateChart = false; this.duration = undefined; this.configData = {}; const apiInstance = this.tuyaApiService.getApiInstance(); if (!apiInstance) throw new Error(Color.error('Tuya API is not authenticated.')); this.deviceManager = new deviceManager_1.DeviceManager(apiInstance, this.log); this.powerConsumptionTracker = new powerConsumptionTracker_1.PowerConsumptionTracker(this.deviceManager, this.log); } async handleCommand(input, connection) { input = input.trim(); const [command, ...args] = input.split(/\s+/); if (!this.selectedCommand) { this.parseOptions(args); this.log.info(Color.info(`Command received: "${command}" with options: chart=${this.generateChart}, duration=${this.duration}`)); const commandMap = { 'discover': () => this.handleDiscover(connection), 'track': () => this.handleTrack(connection), 'exportConfig': () => this.handleExportConfig(connection), }; (commandMap[command] || (() => this.unknownCommand(connection, command)))(); } else { this.handleExistingCommand(input, connection); } } parseOptions(args) { this.generateChart = args.includes('--chart'); const durationArg = args.find(arg => arg.startsWith('--duration=')); this.duration = durationArg ? parseInt(durationArg.split('=')[1], 10) : undefined; } async handleDiscover(connection) { this.selectedCommand = 'discover'; connection.write(Color.info('Starting LAN discovery...\n')); try { const localDevices = await this.deviceManager.discoverLocalDevices(); if (localDevices.length === 0) { this.log.warn(Color.warning('No devices discovered on LAN.')); connection.write(Color.warning('No LAN devices found.\n')); connection.write("RESETTING COMMAND"); return this.resetCommand(); } connection.write(Color.info(`Found ${localDevices.length} local devices. Fetching cloud devices for comparison...\n`)); const matchedDevices = await this.deviceManager.matchLocalWithCloudDevices(localDevices); if (matchedDevices.length === 0) { this.log.warn(Color.warning('No devices matched with the cloud.')); connection.write(Color.warning('No matching devices found in the cloud.\n')); connection.write("RESETTING COMMAND"); return this.resetCommand(); } this.smartPlugsCache = matchedDevices; connection.write(Color.success(this.formatDeviceList(matchedDevices, 'Matched smart plugs') + 'Select the device number: \n')); this.selectedCommand = 'selectDevice'; } catch (error) { this.log.error(Color.error('Error during device discovery:'), error); connection.write(Color.error('Error during discovery.\n')); connection.write("RESETTING COMMAND"); this.resetCommand(); } } handleTrack(connection) { connection.write("HANDLETRACK"); this.selectedCommand = 'track'; if (this.smartPlugsCache.length === 0) { connection.write(Color.warning('No devices found. Please run "discover" first.\n')); this.log.warn(Color.warning('No devices in smartPlugsCache.')); connection.write("RESETTING COMMAND"); this.resetCommand(); return; } connection.write(Color.info(this.formatDeviceList(this.smartPlugsCache, 'Available smart plugs') + 'Select the device number: \n')); this.selectedCommand = 'selectDeviceTrack'; } async handleExistingCommand(input, connection) { const trimmedInput = input.trim(); connection.write("SELECTED COMMAND"); connection.write(this.selectedCommand); if (this.selectedCommand === 'selectDevice') { const index = parseInt(trimmedInput, 10) - 1; if (!isNaN(index) && index >= 0 && index < this.smartPlugsCache.length) { this.selectedPlug = this.smartPlugsCache[index]; await this.displayDeviceDetails(connection, this.selectedPlug); connection.write(Color.success("\nDevice selected successfully! You can now use 'track' or 'exportConfig' commands.\n")); connection.write("RESETTING COMMAND"); this.resetCommand(); } else { connection.write(Color.warning('Invalid selection. Please enter a valid device number.\n')); } } else if (this.selectedCommand === 'selectDeviceTrack') { const index = parseInt(trimmedInput, 10) - 1; if (!isNaN(index) && index >= 0 && index < this.smartPlugsCache.length) { this.selectedPlug = this.smartPlugsCache[index]; await this.displayDeviceDetails(connection, this.selectedPlug); connection.write(Color.success("\nDevice selected successfully!\n")); this.selectedCommand = 'awaitingPowerValueIdTrack'; this.log.info('Command set to awaitingPowerValueIdTrack'); connection.write('Please enter the PowerValueID (e.g.: 19): \n'); } else { connection.write(Color.warning('Invalid selection. Please enter a valid device number.\n')); } } else if (this.selectedCommand === 'awaitingPowerValueIdTrack') { const powerValueId = parseInt(trimmedInput, 10) - 1; this.log.info(`Extracted PowerValueId: ${powerValueId}`); if (!powerValueId || isNaN(Number(powerValueId))) { connection.write('Invalid PowerValueID. Please try again with a valid number.\n'); this.log.error('Invalid PowerValueID provided.'); return; } if (this.selectedPlug) { await this.powerConsumptionTracker.trackPowerConsumption(this.selectedPlug.deviceId, this.selectedPlug.localKey, String(powerValueId), connection, this.generateChart, this.duration); this.selectedCommand = ''; // Reset the stored flags after use this.generateChart = false; this.duration = undefined; } else { connection.write('No valid device selected.\n'); this.log.error('No valid device selected for tracking.'); } } else { this.handleConfigFlow(trimmedInput, connection); } } handleConfigFlow(input, connection) { const prompts = { 'awaitingConfigName': { message: 'Enter the power value ID (e.g., 19): ', nextCommand: 'awaitingPowerValueId', property: 'name' }, 'awaitingPowerValueId': { message: 'Enter the start power value threshold: ', nextCommand: 'awaitingStartValue', property: 'powerValueId' }, 'awaitingStartValue': { message: 'Enter the duration (seconds) for start detection: ', nextCommand: 'awaitingStartDuration', property: 'startValue' }, 'awaitingStartDuration': { message: 'Enter the end power value threshold: ', nextCommand: 'awaitingEndValue', property: 'startDuration' }, 'awaitingEndValue': { message: 'Enter the duration (seconds) for end detection: ', nextCommand: 'awaitingEndDuration', property: 'endValue' }, 'awaitingEndDuration': { message: 'Enter the start message: ', nextCommand: 'awaitingStartMessage', property: 'endDuration' }, 'awaitingStartMessage': { message: 'Enter the end message: ', nextCommand: 'awaitingEndMessage', property: 'startMessage' }, 'awaitingEndMessage': { message: 'Should the state be exposed as a switch? (true/false): ', nextCommand: 'awaitingExposeStateSwitch', property: 'endMessage' }, 'awaitingExposeStateSwitch': { message: '', nextCommand: '', property: 'exposeStateSwitch' }, }; if (this.selectedCommand in prompts) { const { message, nextCommand, property } = prompts[this.selectedCommand]; if (property) { this.configData[property] = property === 'exposeStateSwitch' ? input.toLowerCase() === 'true' : input; } connection.write(Color.info(message)); this.selectedCommand = nextCommand || ''; if (!nextCommand) { this.displayFinalConfig(connection); connection.write("RESETTING COMMAND"); this.resetCommand(); } } else { connection.write(Color.warning('Unexpected input. Please start a new command.\n')); } } handleExportConfig(connection) { if (!this.selectedPlug) { connection.write(Color.warning("Please select a device first by using the 'discover' command.\n")); return; } // Basisdaten aus dem ausgewählten Gerät vorbereiten this.configData = { deviceId: this.selectedPlug.deviceId, localKey: this.selectedPlug.localKey, ipAddress: this.selectedPlug.ip, protocolVersion: this.selectedPlug.version, }; connection.write(Color.info("Let's configure your laundry device.\n")); connection.write(Color.info("Enter the name of the device: ")); this.selectedCommand = 'awaitingConfigName'; } displayFinalConfig(connection) { const finalConfig = { laundryDevices: [this.configData] }; connection.write(Color.success("Generated Config:\n")); connection.write(JSON.stringify(finalConfig, null, 2) + "\n"); } async trackPowerConsumption(powerValueId, connection) { if (!powerValueId || isNaN(Number(powerValueId))) { connection.write(Color.warning('Invalid PowerValueID. Please try again with a valid number.\n')); this.log.error('Invalid PowerValueID provided.'); return; } if (this.selectedPlug) { await this.powerConsumptionTracker.trackPowerConsumption(this.selectedPlug.deviceId, this.selectedPlug.localKey, powerValueId, connection, this.generateChart, this.duration); connection.write("RESETTING COMMAND"); this.resetCommand(); } else { connection.write(Color.error('No valid device selected.\n')); this.log.error('No valid device selected for tracking.'); } } async displayDeviceDetails(connection, plug) { try { const dpsStatus = await this.deviceManager.getLocalDPS(plug); const details = ` Selected device details: Name: ${plug.displayName} Tuya Device ID: ${plug.deviceId} Local Key: ${plug.localKey || 'N/A'} IP Address: ${plug.ip} Protocol Version: ${plug.version} DPS Status: ${JSON.stringify(dpsStatus)} \n`; connection.write(Color.success(details)); } catch (error) { this.log.error(Color.error('Failed to retrieve DPS Status:'), error); connection.write(Color.error('Failed to retrieve DPS Status.\n')); } } formatDeviceList(devices, title) { return `${title}:\n` + devices.map((plug, index) => `${index + 1}: Name: ${plug.displayName}, Device ID: ${plug.deviceId}, IP: ${plug.ip}`).join('\n') + '\n'; } unknownCommand(connection, command) { this.log.warn(Color.warning(`Unknown command: ${command}`)); connection.write(Color.warning(`Unknown command: ${command}\n`)); } resetCommand() { this.selectedCommand = ''; this.generateChart = false; this.duration = undefined; this.configData = {}; } showHelp(connection) { const helpMessage = Color.info(` Welcome to the Smart Plug Controller! Available commands: 1. discover - Discover connected smart plugs 2. track - Track power consumption of a smart plug 3. exportConfig - Generate JSON configuration template for the selected plug Type a command to begin. `); connection.write(helpMessage); } } exports.CommandHandler = CommandHandler; //# sourceMappingURL=commandHandler.js.map