UNPKG

homebridge-tuya-laundry

Version:

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

235 lines 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PowerConsumptionTracker = void 0; const errors_1 = require("./errors"); const QuickChart = require('quickchart-js'); 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 PowerConsumptionTracker { constructor(deviceManager, log) { this.deviceManager = deviceManager; this.log = log; this.powerValues = []; this.startThreshold = null; this.stopThreshold = null; } trackPower(currentDPS) { this.powerValues.push(currentDPS); if (this.powerValues.length > 20) { this.powerValues.shift(); } this.calculateThresholds(); } calculateThresholds() { const averagePower = this.powerValues.reduce((sum, val) => sum + val, 0) / this.powerValues.length; const stdDev = Math.sqrt(this.powerValues.reduce((sum, val) => sum + Math.pow(val - averagePower, 2), 0) / this.powerValues.length); this.startThreshold = averagePower + stdDev * 2; this.stopThreshold = averagePower + stdDev; this.log.info(Color.info(`New thresholds: Start ${this.startThreshold}, Stop ${this.stopThreshold}`)); } async generatePowerConsumptionChart(timestamps, powerData, duration) { this.log.info(Color.info('Generating power consumption chart with sliding window...')); const chartWidth = Math.max(600, duration * 10); const chartHeight = Math.max(300, duration * 5); this.log.debug(Color.info(`Setting chart size: width=${chartWidth}, height=${chartHeight}`)); const chart = new QuickChart(); chart.setWidth(chartWidth); chart.setHeight(chartHeight); const windowSize = Math.min(powerData.length, 100); const displayedPowerData = powerData.slice(-windowSize); const displayedTimestamps = timestamps.slice(-windowSize); chart.setConfig({ type: 'line', data: { labels: displayedTimestamps, datasets: [ { label: 'Power Consumption (Watts)', data: displayedPowerData, fill: false, borderColor: 'rgba(75, 192, 192, 1)', borderWidth: 2, pointBackgroundColor: 'rgba(75, 192, 192, 1)', }, ], }, options: { responsive: true, title: { display: true, text: 'Power Consumption Over Time (Sliding Window)', }, tooltips: { enabled: true, callbacks: { label: function (tooltipItem) { return `Power: ${tooltipItem.yLabel} W`; }, }, }, scales: { xAxes: [ { display: true, scaleLabel: { display: true, labelString: 'Time', }, }, ], yAxes: [ { display: true, scaleLabel: { display: true, labelString: 'Power (Watts)', }, }, ], }, plugins: { datalabels: { display: true, align: 'top', backgroundColor: 'rgba(75, 192, 192, 0.8)', borderRadius: 3, color: 'white', font: { size: 6, weight: 'bold', }, formatter: function (value) { return `${value} W`; }, }, }, }, }); this.log.debug(Color.info(`Displaying last ${windowSize} data points in chart`)); // Verwenden Sie die POST-Methode mit `getShortUrl` try { const shortUrl = await chart.getShortUrl(); // Hier await verwenden this.log.info(Color.success(`Shortened Chart URL: ${shortUrl}`)); return shortUrl; } catch (error) { this.log.error(Color.error(`Error generating shortened chart URL: ${errors_1.errorMessage(error)}`)); return null; } } async trackPowerConsumption(deviceId, localKey, powerValueId, connection, generateChart, duration, retryCount = 3, retryDelay = 5000) { const powerData = []; const timestamps = []; let stopTracking = false; let retries = 0; try { this.log.info(Color.info(`Starting to track power consumption for device ${deviceId} with PowerValueId ${powerValueId}. Will generate chart: ${generateChart} for duration: ${duration}`)); connection.write(Color.info(`Starting to track power consumption for device ${deviceId} with PowerValueId ${powerValueId}. Will generate chart: ${generateChart} for duration ${duration}\n`)); let selectedDevice = null; while (retries < retryCount) { const localDevices = await this.deviceManager.discoverLocalDevices(); selectedDevice = localDevices.find((device) => device.deviceId === deviceId); if (selectedDevice) { this.log.info(Color.success(`Device ${deviceId} found on the network.`)); selectedDevice.localKey = localKey; break; } else { retries++; this.log.warn(Color.warning(`Device with ID ${deviceId} not found, retrying... (${retries}/${retryCount})`)); connection.write(Color.warning(`Device with ID ${deviceId} not found, retrying... (${retries}/${retryCount})\n`)); await new Promise(resolve => setTimeout(resolve, retryDelay)); } } if (!selectedDevice) { this.log.error(Color.error(`Device with ID ${deviceId} not found after ${retryCount} attempts.`)); connection.write(Color.error(`Device with ID ${deviceId} not found after ${retryCount} attempts.\n`)); return; } const trackingInterval = setInterval(async () => { if (stopTracking) return; try { const dpsStatus = await this.deviceManager.getLocalDPS(selectedDevice); if (!dpsStatus) { this.log.error(Color.error('Failed to retrieve DPS Status.')); connection.write(Color.error('Failed to retrieve DPS Status.\n')); return; } if (!dpsStatus.dps.hasOwnProperty(powerValueId)) { this.log.error(Color.error(`PowerValueId ${powerValueId} not found in DPS data.`)); connection.write(Color.error(`PowerValueId ${powerValueId} not found in DPS data.\n`)); return; } const powerValue = dpsStatus.dps[powerValueId]; const currentTime = new Date().toLocaleTimeString(); powerData.push(powerValue); timestamps.push(currentTime); this.log.info(Color.info(`Power consumption value for PowerValueId ${powerValueId}: ${powerValue}.`)); connection.write(Color.info(`Power consumption for device ${deviceId} (PowerValueId ${powerValueId}): ${powerValue} at ${currentTime}.\n`)); } catch (error) { const msg = errors_1.errorMessage(error); this.log.error(Color.error(`Error tracking power consumption for device ${deviceId}: ${msg}`)); connection.write(Color.error(`Error tracking power consumption for device ${deviceId}: ${msg}\n`)); clearInterval(trackingInterval); } }, this.calculateInterval(duration)); if (duration !== null && duration !== undefined) { setTimeout(async () => { stopTracking = true; clearInterval(trackingInterval); this.log.info(Color.info(`Stopped tracking power consumption for device ${deviceId} after ${duration} seconds.`)); if (generateChart) { const chartUrl = await this.generatePowerConsumptionChart(timestamps, powerData, duration); // await hinzufügen connection.write(Color.success(`Power consumption chart generated. View it here: ${chartUrl}\n`)); } }, duration * 1000); } } catch (error) { const msg = errors_1.errorMessage(error); this.log.error(Color.error(`Error tracking power consumption for device ${deviceId}: ${msg}`)); connection.write(Color.error(`Error tracking power consumption for device ${deviceId}: ${msg}\n`)); } } calculateInterval(duration) { if (duration <= 60) { return 1000; } else if (duration <= 300) { return 5000; } else if (duration <= 1800) { return 10000; } else { return 30000; } } } exports.PowerConsumptionTracker = PowerConsumptionTracker; //# sourceMappingURL=powerConsumptionTracker.js.map