homebridge-vesync-v2
Version:
A Homebridge plugin for controlling VeSync smart devices including outlets, air purifiers, and humidifiers
153 lines (136 loc) • 4.9 kB
JavaScript
"use strict";
const BaseDevice = require('./BaseDevice');
class FanDevice extends BaseDevice {
constructor(accessory, client, log, debug, Service, Characteristic) {
super(accessory, client, log, debug, Service, Characteristic);
}
configureService() {
if (!this.accessory) {
throw new Error('Accessory is not initialized');
}
// Fan service
const fanService = this.accessory.getService(this.Service.Fan) ||
this.accessory.addService(this.Service.Fan, this.accessory.context.name);
// Required characteristics
fanService.getCharacteristic(this.Characteristic.On)
.on('get', this.getPowerState.bind(this))
.on('set', this.setPowerState.bind(this));
fanService.getCharacteristic(this.Characteristic.RotationSpeed)
.setProps({
minValue: 0,
maxValue: 100,
minStep: 8.33 // 100/12 for 12 speed levels
})
.on('get', this.getRotationSpeed.bind(this))
.on('set', this.setRotationSpeed.bind(this));
fanService.getCharacteristic(this.Characteristic.SwingMode)
.on('get', this.getSwingMode.bind(this))
.on('set', this.setSwingMode.bind(this));
}
// Helper method to get fan status
async _getStatus() {
if (!this.client) {
throw new Error('Client is not initialized');
}
const response = await this.client.bypassV2({
method: 'getTowerFanStatus',
source: 'APP'
});
if (!response || !response.data) {
throw new Error('Invalid response from device');
}
// Cache the status data
this.lastStatus = response.data;
return response;
}
async getPowerState(callback) {
try {
const status = await this._getStatus();
if (!status.data || typeof status.data.powerSwitch === 'undefined') {
throw new Error('Invalid power state data');
}
callback(null, status.data.powerSwitch === 1);
} catch (error) {
this.log.error('Error getting power state:', error);
callback(error);
}
}
async setPowerState(value, callback) {
try {
await this.client.bypassV2({
data: {
powerSwitch: value ? 1 : 0,
switchIdx: 0
},
method: 'setSwitch',
source: 'APP'
});
callback();
} catch (error) {
this.log.error('Error setting power state:', error);
callback(error);
}
}
async getRotationSpeed(callback) {
try {
const status = await this._getStatus();
if (!status.data || typeof status.data.manualSpeedLevel === 'undefined') {
throw new Error('Invalid speed data');
}
// Convert 1-12 to 0-100
const speed = status.data.manualSpeedLevel;
const percentage = Math.round((speed / 12) * 100);
callback(null, percentage);
} catch (error) {
this.log.error('Error getting rotation speed:', error);
callback(error);
}
}
async setRotationSpeed(value, callback) {
try {
// Convert 0-100 to 1-12
const level = Math.max(1, Math.min(12, Math.round((value / 100) * 12)));
await this.client.bypassV2({
data: {
manualSpeedLevel: level,
levelType: 'wind',
levelIdx: 0
},
method: 'setLevel',
source: 'APP'
});
callback();
} catch (error) {
this.log.error('Error setting rotation speed:', error);
callback(error);
}
}
async getSwingMode(callback) {
try {
const status = await this._getStatus();
if (!status.data || typeof status.data.oscillationSwitch === 'undefined') {
throw new Error('Invalid swing mode data');
}
callback(null, status.data.oscillationSwitch === 1);
} catch (error) {
this.log.error('Error getting swing mode:', error);
callback(error);
}
}
async setSwingMode(value, callback) {
try {
await this.client.bypassV2({
data: {
oscillationSwitch: value ? 1 : 0
},
method: 'setOscillationSwitch',
source: 'APP'
});
callback();
} catch (error) {
this.log.error('Error setting swing mode:', error);
callback(error);
}
}
}
module.exports = FanDevice;