homebridge-tuya-laundry
Version:
Allows washer/dryer cycle completion notifications using Tuya smart plugs with power meter, now using local control.
160 lines • 8.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LaundryDeviceTracker = void 0;
const luxon_1 = require("luxon");
class LaundryDeviceTracker {
constructor(log, messageGateway, config, api, smartPlugService) {
this.log = log;
this.messageGateway = messageGateway;
this.config = config;
this.api = api;
this.smartPlugService = smartPlugService;
this.cumulativeConsumption = 0; // in watt-seconds (W·s)
this.lastMeasurementTime = luxon_1.DateTime.now(); // Time of the last measurement
this.lastDpsStatus = null; // Cached last DPS status
this.currentInterval = 5000; // Dynamic update interval in milliseconds
const deviceName = this.config.name || this.config.deviceId;
this.log.debug(`Initializing LaundryDeviceTracker with config: ${JSON.stringify(this.config, null, 2)}`);
}
async init() {
const deviceName = this.config.name || this.config.deviceId;
if (this.config.startValue < this.config.endValue) {
throw new Error('startValue cannot be smaller than endValue.');
}
if (!this.config.localKey) {
this.log.error(`Missing localKey for device ${deviceName}. Please provide a valid localKey.`);
return;
}
try {
const localDevices = await this.smartPlugService.discoverLocalDevices();
const selectedDevice = localDevices.find(device => device.deviceId === this.config.deviceId);
if (!selectedDevice) {
this.log.warn(`Device ${deviceName} not found on LAN.`);
return;
}
selectedDevice.localKey = this.config.localKey;
this.log.info(`Device ${deviceName} found on LAN. Starting power tracking.`);
this.detectStartStop(selectedDevice);
}
catch (error) {
this.log.error(`Error initializing device ${deviceName}: ${error.message}`);
}
}
async detectStartStop(selectedDevice) {
setInterval(async () => {
try {
const deviceName = this.config.name || this.config.deviceId;
const powerValue = await this.getPowerValue(selectedDevice);
if (typeof powerValue !== 'number') {
this.log.error(`Received invalid power value: ${powerValue} (expected a number).`);
return;
}
this.log.debug(`Current power for ${deviceName}: ${powerValue}W`);
this.incomingData(powerValue);
// Run the helper method to check start and stop conditions
await this.checkStartStopConditions(deviceName, powerValue);
}
catch (error) {
this.log.error(`Error during start/stop detection: ${error.message}`);
}
}, this.currentInterval);
}
// Helper method to get power value with caching
async getPowerValue(selectedDevice) {
var _a;
const dpsStatus = await this.smartPlugService.getLocalDPS(selectedDevice, this.log);
if (JSON.stringify(dpsStatus) === JSON.stringify(this.lastDpsStatus)) {
this.log.debug(`No change in device status for ${selectedDevice.deviceId}, skipping further checks.`);
return (_a = this.lastDpsStatus) === null || _a === void 0 ? void 0 : _a.dps[this.config.powerValueId];
}
this.lastDpsStatus = dpsStatus;
// Explizit prüfen, ob powerValue definiert ist, um 0 als gültigen Wert zu akzeptieren
const powerValue = dpsStatus === null || dpsStatus === void 0 ? void 0 : dpsStatus.dps[this.config.powerValueId];
return powerValue !== undefined ? powerValue : null;
}
// Method to dynamically adjust the interval based on activity
adjustInterval(isActive) {
this.currentInterval = isActive ? 1000 : 5000; // 1 second when active, 5 seconds when idle
this.log.debug(`Adjusted polling interval to ${this.currentInterval}ms based on activity.`);
}
// Helper method to check start and stop conditions
async checkStartStopConditions(deviceName, powerValue) {
// Check whether the machine started
if (!this.isActive && this.startDetected && this.startDetectedTime) {
const secondsDiff = luxon_1.DateTime.now().diff(this.startDetectedTime, 'seconds').seconds;
this.log.debug(`Checking start confirmation: ${secondsDiff} seconds since start detected.`);
if (secondsDiff > this.config.startDuration) {
this.log.info(`${deviceName} has started!`);
if (this.config.startMessage) {
await this.messageGateway.send(this.config.startMessage);
}
this.isActive = true;
this.updateAccessorySwitchState(true);
this.cumulativeConsumption = 0; // Reset cumulative consumption for the new cycle
this.startDetected = false; // Reset start detection
this.startDetectedTime = undefined;
this.adjustInterval(true); // Switch to a more frequent interval
}
}
// Accumulate energy consumption if the machine is running
if (this.isActive) {
const now = luxon_1.DateTime.now();
const timeDiff = now.diff(this.lastMeasurementTime, 'seconds').seconds;
this.lastMeasurementTime = now;
const energyConsumed = (powerValue / 10) * timeDiff; // in watt-seconds (W-s)
this.cumulativeConsumption += energyConsumed;
this.log.debug(`Added ${energyConsumed} W·s. Total cumulativeConsumption: ${this.cumulativeConsumption} W·s, ${(this.cumulativeConsumption / 3600000).toFixed(4)} kWh`);
}
// Check whether the machine has stopped
if (this.endDetected && this.endDetectedTime) {
const secondsDiff = luxon_1.DateTime.now().diff(this.endDetectedTime, 'seconds').seconds;
if (secondsDiff > this.config.endDuration && this.isActive) {
this.isActive = false;
const kWhConsumed = this.cumulativeConsumption / 3600000; // Convert watt-seconds to kWh
this.log.info(`Device finished the job. Total consumption: ${kWhConsumed.toFixed(2)} kWh`);
const endMessage = `${this.config.endMessage || ''} Total consumption: ${kWhConsumed.toFixed(2)} kWh.`;
this.messageGateway.send(endMessage);
this.updateAccessorySwitchState(false);
this.cumulativeConsumption = 0; // Reset for the next cycle
this.endDetected = false; // Reset end detection
this.endDetectedTime = undefined;
this.adjustInterval(false); // Switch to a less frequent interval
}
}
}
incomingData(value) {
const deviceName = this.config.name || this.config.deviceId;
this.log.debug(`Processing incoming power data for ${deviceName}: ${value}`);
if (value >= this.config.startValue) {
if (!this.isActive && !this.startDetected) {
this.startDetected = true;
this.startDetectedTime = luxon_1.DateTime.now();
this.log.debug(`Detected start value for ${deviceName}. Waiting ${this.config.startDuration} seconds for confirmation.`);
}
}
else {
this.startDetected = false;
this.startDetectedTime = undefined;
}
if (value <= this.config.endValue) {
if (this.isActive && !this.endDetected) {
this.endDetected = true;
this.endDetectedTime = luxon_1.DateTime.now();
this.log.debug(`Detected end value for ${deviceName}. Waiting ${this.config.endDuration} seconds for confirmation.`);
}
}
else {
this.endDetected = false;
this.endDetectedTime = undefined;
}
}
updateAccessorySwitchState(isOn) {
if (this.config.exposeStateSwitch && this.accessory) {
const service = this.accessory.getService(this.api.hap.Service.Switch);
service === null || service === void 0 ? void 0 : service.setCharacteristic(this.api.hap.Characteristic.On, isOn);
this.log.debug(`Updated accessory switch state for ${this.config.name}: ${isOn ? 'On' : 'Off'}`);
}
}
}
exports.LaundryDeviceTracker = LaundryDeviceTracker;
//# sourceMappingURL=laundryDeviceTracker.js.map