tsvesync
Version:
A TypeScript library for interacting with VeSync smart home devices
231 lines (205 loc) • 8.52 kB
text/typescript
import { VeSyncHumidifier } from './humidifier';
import { VeSync } from '../vesync';
import { Helpers } from '../helpers';
import { logger } from '../logger';
/**
* VeSync Humidifier 200/300S Class
*/
export class VeSyncHumid200300S extends VeSyncHumidifier {
protected readonly modes = ['auto', 'manual', 'sleep'] as const;
protected readonly features = ['humidity', 'mist', 'display', 'timer', 'auto_mode', 'night_light'];
protected readonly mistLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
protected readonly humidityRange = { min: 30, max: 80 };
constructor(details: Record<string, any>, manager: VeSync) {
super(details, manager);
logger.debug(`Initialized VeSyncHumid200300S device: ${this.deviceName}`);
}
/**
* Get device details
*/
async getDetails(): Promise<Boolean> {
logger.debug(`Getting details for device: ${this.deviceName}`);
const [response, status] = await this.callApi(
'/cloud/v2/deviceManaged/bypassV2',
'post',
{
...Helpers.reqBody(this.manager, 'bypassV2'),
cid: this.cid,
configModule: this.configModule,
payload: {
data: {},
method: 'getHumidifierStatus',
source: 'APP'
}
},
Helpers.reqHeaderBypass()
);
const success = this.checkResponse([response, status], 'getDetails');
if (success && response?.result?.result) {
const result = response.result.result;
// Update device status based on enabled field
this.deviceStatus = result.enabled ? 'on' : 'off';
this.details = {
mode: result.mode || '',
humidity: result.humidity || 0, // Current humidity
target_humidity: result.configuration?.auto_target_humidity || result.target_humidity || 0, // Target humidity from configuration
mist_level: result.mist_level || 0,
mist_virtual_level: result.mist_virtual_level || 0,
water_lacks: result.water_lacks || false,
humidity_high: result.humidity_high || false,
water_tank_lifted: result.water_tank_lifted || false,
display: result.display || false,
automatic_stop: result.automatic_stop_reach_target || false,
automatic_stop_configured: result.configuration?.automatic_stop || false,
auto_target_humidity: result.configuration?.auto_target_humidity || 0,
configuration: result.configuration || {},
connection_status: result.connection_status || null,
night_light_brightness: result.night_light_brightness || 0
};
// Log raw response for debugging
logger.debug(`Raw API Response: ${JSON.stringify(response)}`);
logger.debug(`Parsed Result: ${JSON.stringify(result)}`);
logger.debug(`Successfully got details for device: ${this.deviceName}`);
}
return success;
}
/**
* Set mist level
*/
async setMistLevel(level: number): Promise<boolean> {
if (!this.mistLevels.includes(level)) {
const error = `Invalid mist level: ${level}. Must be one of: ${this.mistLevels.join(', ')}`;
logger.error(`${error} for device: ${this.deviceName}`);
throw new Error(error);
}
logger.info(`Setting mist level to ${level} for device: ${this.deviceName}`);
const [response, status] = await this.callApi(
'/cloud/v2/deviceManaged/bypassV2',
'post',
{
...Helpers.reqBody(this.manager, 'bypassV2'),
cid: this.cid,
configModule: this.configModule,
payload: {
data: {
id: 0,
level: level,
type: 'mist'
},
method: 'setVirtualLevel', // Use setVirtualLevel per YAML spec
source: 'APP'
}
},
Helpers.reqHeaderBypass()
);
const success = this.checkResponse([response, status], 'setMistLevel');
logger.debug(`Mist Level API Response: ${JSON.stringify(response)}`);
logger.debug(`Mist Level API Status: ${status}`);
if (success) {
this.details.mist_level = level;
this.details.mist_virtual_level = level;
// Get latest state and log it
await this.getDetails();
logger.debug(`Device state after mist level change: ${JSON.stringify(this.details)}`);
} else {
logger.error(`Failed to set mist level to ${level} for device: ${this.deviceName}`);
}
return success;
}
/**
* Get configuration
*/
get configuration(): any {
return this.details.configuration || {};
}
/**
* Get mist level
* Override base class to return virtual level
*/
get mistLevel(): number {
return this.details.mist_virtual_level || 0;
}
/**
* Get target humidity
* Override base class to return target humidity from configuration
*/
get humidity(): number {
return this.details.auto_target_humidity || 0;
}
/**
* Get current humidity
* Provides access to the current humidity reading
*/
get currentHumidity(): number {
return this.details.humidity || 0;
}
/**
* Get automatic stop configuration
*/
get automaticStopConfigured(): boolean {
return this.details.automatic_stop_configured || false;
}
/**
* Get night light brightness
*/
get nightLightBrightness(): number {
return this.details.night_light_brightness || 0;
}
/**
* Return JSON details for humidifier
* Override to include current humidity and target humidity
*/
displayJSON(): string {
const baseInfo = JSON.parse(super.displayJSON());
// Add current humidity from details
baseInfo['Humidity'] = this.details.humidity?.toString() || '0';
// Add target humidity from configuration
baseInfo['Target Humidity'] = this.details.auto_target_humidity?.toString() || '0';
// Add mist virtual level
baseInfo['Mist Virtual Level'] = this.details.mist_virtual_level?.toString() || '0';
return JSON.stringify(baseInfo, null, 4);
}
/**
* Set night light brightness
*/
async setNightLightBrightness(brightness: number): Promise<boolean> {
if (brightness < 0 || brightness > 100) {
const error = `Invalid brightness: ${brightness}. Must be between 0 and 100`;
logger.error(`${error} for device: ${this.deviceName}`);
throw new Error(error);
}
logger.debug(`Setting night light brightness to ${brightness} for device: ${this.deviceName}`);
const [response, status] = await this.callApi(
'/cloud/v2/deviceManaged/bypassV2',
'post',
{
...Helpers.reqBody(this.manager, 'bypassV2'),
cid: this.cid,
configModule: this.configModule,
payload: {
data: {
brightness
},
method: 'setNightLight',
source: 'APP'
}
},
Helpers.reqHeaderBypass()
);
// Check for error code 11000000 which indicates feature not supported
if (response?.result?.code === 11000000) {
logger.warn(`Night light control not supported by device: ${this.deviceName} (${this.deviceType})`);
return false;
}
const success = this.checkResponse([response, status], 'setNightLightBrightness');
if (success) {
this.details.night_light_brightness = brightness;
// Get latest state and log it
await this.getDetails();
logger.debug(`Device state after night light brightness change: ${JSON.stringify(this.details)}`);
} else {
logger.error(`Failed to set night light brightness to ${brightness} for device: ${this.deviceName}`);
}
return success;
}
}