iRayanKhan / homebridge-tuya

A Homebridge plugin to control Tuya devices locally.
MIT License
402 stars 166 forks source link

New Device: Energy Monitoring Support #486

Open shivanathd opened 1 month ago

shivanathd commented 1 month ago

Hi Everyone, I dont know how to code this but i used CHAT-GPT canvas and tried creating the code , Assuming we're planning to use Fakegato - History Library ([](https://github.com/simont77/fakegato-history))

I wish if someone can look at the code, and if we can implement the Power Metering to all the devices it would be great to pull this into EVE app..

cc: @iRayanKhan


// Code from GPT
const TuyaDevice = require('tuya-device');
const fakegatoHistory = require('fakegato-history');
const { Service, Characteristic } = require('homebridge');

class TuyaPowerAccessory {
  constructor(log, config, api) {
    this.log = log;
    this.config = config;
    this.api = api;

    this.name = config.name;
    this.deviceId = config.deviceId;
    this.accessToken = config.accessToken;
    this.dpCurrent = config.dpCurrent || '18';
    this.dpVoltage = config.dpVoltage || '20';
    this.dpPower = config.dpPower || '19';
    this.tuyaDevice = this.initializeTuyaDevice();

    // Setup Fakegato History Service to track energy data with manual timer
    this.historyService = new fakegatoHistory('energy', this, {
      storage: 'fs',
      path: this.api.user.persistPath(),
      disableTimer: true,
      disableRepeatLastData: true,
    });

    // Register Service and Characteristics for Power
    this.service = new Service.Outlet(this.name);
    this.service
      .getCharacteristic(Characteristic.On)
      .on('set', this.setOn.bind(this))
      .on('get', this.getOn.bind(this));

    this.service
      .getCharacteristic(Characteristic.OutletInUse)
      .on('get', this.getOutletInUse.bind(this));

    // Adding characteristics to track power consumption
    this.currentConsumptionCharacteristic = this.service.addCharacteristic(Characteristic.CurrentConsumption);
    this.totalConsumptionCharacteristic = this.service.addCharacteristic(Characteristic.TotalConsumption);
    this.voltageCharacteristic = this.service.addCharacteristic(Characteristic.Voltage);
    this.electricCurrentCharacteristic = this.service.addCharacteristic(Characteristic.ElectricCurrent);

    this.currentConsumptionCharacteristic.on('get', this.getCurrentConsumption.bind(this));
    this.voltageCharacteristic.on('get', this.getVoltage.bind(this));
    this.electricCurrentCharacteristic.on('get', this.getElectricCurrent.bind(this));

    // Periodically fetch power consumption data every 10 minutes
    this.refreshPowerConsumption();
  }

  // Initialize Tuya Device
  initializeTuyaDevice() {
    return new TuyaDevice({ id: this.deviceId, key: this.accessToken });
  }

  // Method to periodically refresh power data
  refreshPowerConsumption() {
    setInterval(async () => {
      try {
        const powerData = await this.fetchPowerData();
        if (powerData) {
          const { current, voltage, power } = powerData;
          // Add entry to the Fakegato history service
          this.historyService.addEntry({ time: Math.round(new Date().valueOf() / 1000), power: power });
          // Update HomeKit characteristics with the latest power data
          this.currentConsumptionCharacteristic.updateValue(power);
          this.voltageCharacteristic.updateValue(voltage);
          this.electricCurrentCharacteristic.updateValue(current);
        }
      } catch (error) {
        this.log.error('Error fetching power data:', error);
      }
    }, 600000); // Refresh every 10 minutes
  }

  // Fetch power consumption data from Tuya device
  async fetchPowerData() {
    try {
      const response = await this.tuyaDevice.get({ schema: true });
      if (response && response.dps) {
        // Extract power data points from the response
        const powerData = {
          current: response.dps[this.dpCurrent] || 0,
          voltage: response.dps[this.dpVoltage] || 0,
          power: response.dps[this.dpPower] || 0,
        };
        return powerData;
      }
    } catch (error) {
      this.log.error('Error fetching power data from Tuya API:', error);
    }
    return null;
  }

  // Get the current power consumption
  getCurrentConsumption(callback) {
    this.fetchPowerData()
      .then((data) => {
        if (data) {
          callback(null, data.power);
        } else {
          callback(new Error('Could not fetch current consumption'));
        }
      })
      .catch((error) => callback(error));
  }

  // Get the current voltage
  getVoltage(callback) {
    this.fetchPowerData()
      .then((data) => {
        if (data) {
          callback(null, data.voltage);
        } else {
          callback(new Error('Could not fetch voltage'));
        }
      })
      .catch((error) => callback(error));
  }

  // Get the current electric current
  getElectricCurrent(callback) {
    this.fetchPowerData()
      .then((data) => {
        if (data) {
          callback(null, data.current);
        } else {
          callback(new Error('Could not fetch electric current'));
        }
      })
      .catch((error) => callback(error));
  }

  // Set the power state of the device
  async setOn(value, callback) {
    try {
      await this.tuyaDevice.set({ dps: 1, set: value });
      callback(null);
    } catch (error) {
      this.log.error('Error setting power state:', error);
      callback(error);
    }
  }

  // Get the current power state of the device
  async getOn(callback) {
    try {
      const response = await this.tuyaDevice.get({ dps: 1 });
      if (response && typeof response === 'boolean') {
        callback(null, response);
      } else {
        callback(new Error('Could not fetch power state'));
      }
    } catch (error) {
      this.log.error('Error getting power state:', error);
      callback(error);
    }
  }

  // Determine if the outlet is currently in use
  getOutletInUse(callback) {
    this.fetchPowerData()
      .then((data) => {
        if (data) {
          callback(null, data.power > 0);
        } else {
          callback(new Error('Could not determine outlet usage'));
        }
      })
      .catch((error) => callback(error));
  }

  // Return all services provided by this accessory
  getServices() {
    return [this.service, this.historyService];
  }
}

module.exports = (homebridge) => {
  fakegatoHistory(homebridge);
  homebridge.registerAccessory('homebridge-tuya-power', 'TuyaPowerAccessory', TuyaPowerAccessory);
};