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
JavaScript
"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