UNPKG

tsvesync

Version:

A TypeScript library for interacting with VeSync smart home devices

409 lines (408 loc) 14.8 kB
"use strict"; /** * VeSync Bulb Base Class */ Object.defineProperty(exports, "__esModule", { value: true }); exports.VeSyncBulb = exports.bulbConfig = void 0; const vesyncBaseDevice_1 = require("./vesyncBaseDevice"); const helpers_1 = require("./helpers"); const logger_1 = require("./logger"); // Bulb configuration exports.bulbConfig = { 'ESL100': { module: 'VeSyncBulbESL100', features: ['dimmable'], colorModel: 'none' }, 'ESL100CW': { module: 'VeSyncBulbESL100CW', features: ['dimmable', 'color_temp'], colorModel: 'none' }, 'XYD0001': { module: 'VeSyncBulbXYD0001', features: ['dimmable', 'color_temp', 'rgb_shift'], colorModel: 'hsv' }, 'ESL100MC': { module: 'VeSyncBulbESL100MC', features: ['dimmable', 'rgb_shift'], colorModel: 'rgb' } }; /** * VeSync Bulb Base Class */ class VeSyncBulb extends vesyncBaseDevice_1.VeSyncBaseDevice { constructor(details, manager) { var _a; super(details, manager); this.brightness = 0; this.colorTemp = 0; this.colorValue = 0; this.colorHue = 0; this.colorSaturation = 0; this.colorMode = ''; this.features = ((_a = exports.bulbConfig[this.deviceType]) === null || _a === void 0 ? void 0 : _a.features) || []; this.rgbValues = { red: 0, green: 0, blue: 0 }; } hasFeature(feature) { return this.features.includes(feature); } getColorModel() { var _a, _b; return (_b = (_a = exports.bulbConfig[this.deviceType]) === null || _a === void 0 ? void 0 : _a.colorModel) !== null && _b !== void 0 ? _b : 'none'; } getBrightness() { return this.brightness; } /** * Get bulb details */ async getDetails() { logger_1.logger.debug(`[${this.deviceName}] Getting bulb details`); const isV2Device = this.deviceType === 'XYD0001'; const endpoint = isV2Device ? '/cloud/v2/deviceManaged/bypassV2' : '/cloud/v1/deviceManaged/bypass'; const body = isV2Device ? { ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'), cid: this.cid, configModule: this.configModule, payload: { data: {}, method: 'getLightStatusV2', source: 'APP' } } : { ...helpers_1.Helpers.reqBody(this.manager, 'bypass'), cid: this.cid, configModule: this.configModule, jsonCmd: { getLightStatus: 'get' } }; const [response, statusCode] = await this.callApi(endpoint, 'post', body, helpers_1.Helpers.reqHeaders(this.manager)); return this.processStandardBypassDetails(response, statusCode); } /** * Update bulb details */ async update() { logger_1.logger.debug(`[${this.deviceName}] Updating bulb information`); const success = await this.getDetails(); logger_1.logger.info(`[${this.deviceName}] Successfully updated bulb information`); return success; } /** * Turn bulb on */ async turnOn() { logger_1.logger.debug(`[${this.deviceName}] Turning bulb on`); const isV2Device = this.deviceType === 'XYD0001'; const endpoint = isV2Device ? '/cloud/v2/deviceManaged/bypassV2' : '/cloud/v1/deviceManaged/bypass'; const body = isV2Device ? { ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'), cid: this.cid, configModule: this.configModule, payload: { data: { enabled: true, id: 0 }, method: 'setSwitch', source: 'APP' } } : { ...helpers_1.Helpers.reqBody(this.manager, 'bypass'), cid: this.cid, configModule: this.configModule, jsonCmd: { light: { action: 'on' } } }; const [response] = await this.callApi(endpoint, 'post', body, helpers_1.Helpers.reqHeaders(this.manager)); if ((response === null || response === void 0 ? void 0 : response.code) === 0) { this.deviceStatus = 'on'; logger_1.logger.info(`[${this.deviceName}] Successfully turned bulb on`); return true; } logger_1.logger.error(`[${this.deviceName}] Failed to turn bulb on: ${JSON.stringify(response)}`); return false; } /** * Turn bulb off */ async turnOff() { logger_1.logger.debug(`[${this.deviceName}] Turning bulb off`); const isV2Device = this.deviceType === 'XYD0001'; const endpoint = isV2Device ? '/cloud/v2/deviceManaged/bypassV2' : '/cloud/v1/deviceManaged/bypass'; const body = isV2Device ? { ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'), cid: this.cid, configModule: this.configModule, payload: { data: { enabled: false, id: 0 }, method: 'setSwitch', source: 'APP' } } : { ...helpers_1.Helpers.reqBody(this.manager, 'bypass'), cid: this.cid, configModule: this.configModule, jsonCmd: { light: { action: 'off' } } }; const [response] = await this.callApi(endpoint, 'post', body, helpers_1.Helpers.reqHeaders(this.manager)); if ((response === null || response === void 0 ? void 0 : response.code) === 0) { this.deviceStatus = 'off'; logger_1.logger.info(`[${this.deviceName}] Successfully turned bulb off`); return true; } logger_1.logger.error(`[${this.deviceName}] Failed to turn bulb off: ${JSON.stringify(response)}`); return false; } /** * Set bulb brightness */ async setBrightness(brightness) { if (!this.features.includes('dimmable')) { logger_1.logger.error(`[${this.deviceName}] Dimming not supported`); return false; } const brightnessLevel = VeSyncBulb.clamp(Math.round(brightness), 0, 100); logger_1.logger.debug(`[${this.deviceName}] Setting brightness to ${brightnessLevel}`); const isV2Device = this.deviceType === 'XYD0001'; const endpoint = isV2Device ? '/cloud/v2/deviceManaged/bypassV2' : '/cloud/v1/deviceManaged/bypass'; const body = isV2Device ? { ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'), cid: this.cid, configModule: this.configModule, payload: { data: { brightness: brightnessLevel, colorMode: '', colorTemp: '', force: 0, hue: '', saturation: '', value: '' }, method: 'setLightStatusV2', source: 'APP' } } : { ...helpers_1.Helpers.reqBody(this.manager, 'bypass'), cid: this.cid, configModule: this.configModule, jsonCmd: { light: { brightness: brightnessLevel } } }; const [response] = await this.callApi(endpoint, 'post', body, helpers_1.Helpers.reqHeaders(this.manager)); if ((response === null || response === void 0 ? void 0 : response.code) === 0) { this.brightness = brightnessLevel; logger_1.logger.info(`[${this.deviceName}] Successfully set brightness to ${brightnessLevel}`); return true; } logger_1.logger.error(`[${this.deviceName}] Failed to set brightness: ${JSON.stringify(response)}`); return false; } /** * Get bulb brightness */ /** * Get color temperature in Kelvin */ getColorTempKelvin() { if (!this.features.includes('color_temp')) { return 0; } return ((6500 - 2700) * this.colorTemp / 100) + 2700; } /** * Get color temperature in percent */ getColorTempPercent() { if (!this.features.includes('color_temp')) { return 0; } return this.colorTemp; } /** * Get color hue */ getColorHue() { if (!this.features.includes('rgb_shift')) { return 0; } return this.colorHue; } /** * Get color saturation */ getColorSaturation() { if (!this.features.includes('rgb_shift')) { return 0; } return this.colorSaturation; } /** * Get color value */ getColorValue() { if (!this.features.includes('rgb_shift')) { return 0; } return this.colorValue; } /** * Get RGB values */ getRGBValues() { if (!this.features.includes('rgb_shift') || exports.bulbConfig[this.deviceType].colorModel !== 'rgb') { return { red: 0, green: 0, blue: 0 }; } return this.rgbValues; } /** * Set color temperature wrapper to maintain compatibility with consumers */ async setColorTemperature(colorTemp) { return this.setColorTemp(colorTemp); } /** * Set color via hue/saturation/value when supported */ async setColor(hue, saturation, value = 100) { var _a; if (!this.features.includes('rgb_shift')) { logger_1.logger.error(`[${this.deviceName}] Color control not supported`); return false; } const colorModel = (_a = exports.bulbConfig[this.deviceType]) === null || _a === void 0 ? void 0 : _a.colorModel; if (colorModel === 'rgb' && typeof this.setRgb === 'function') { const rgb = VeSyncBulb.hsvToRgb(hue, saturation, value); return this.setRgb(rgb.red, rgb.green, rgb.blue); } if (typeof this.setHsv === 'function') { return this.setHsv(hue, saturation, value); } logger_1.logger.error(`[${this.deviceName}] No compatible color handler found`); return false; } /** * Process standard bypass (v1) detail responses shared by multiple bulbs */ processStandardBypassDetails(response, statusCode) { var _a, _b, _c, _d, _e, _f, _g, _h; const success = this.checkResponse([response, statusCode], 'getDetails'); if (success && (response === null || response === void 0 ? void 0 : response.result)) { const innerResult = ((_a = response.result) === null || _a === void 0 ? void 0 : _a.result) || response.result; if (innerResult) { if (typeof innerResult.enabled === 'boolean') { this.deviceStatus = innerResult.enabled ? 'on' : 'off'; } else if (innerResult.action) { this.deviceStatus = innerResult.action === 'on' ? 'on' : 'off'; } if (innerResult.connectionStatus) { this.connectionStatus = innerResult.connectionStatus; } if (this.features.includes('dimmable') && innerResult.brightness !== undefined) { this.brightness = Number(innerResult.brightness); } if (this.features.includes('color_temp')) { if (innerResult.colorTemp !== undefined) { this.colorTemp = Number(innerResult.colorTemp); } else if (innerResult.colorTempe !== undefined) { this.colorTemp = Number(innerResult.colorTempe); } } if (this.features.includes('rgb_shift')) { if (((_b = exports.bulbConfig[this.deviceType]) === null || _b === void 0 ? void 0 : _b.colorModel) === 'rgb') { this.rgbValues = { red: Number((_c = innerResult.red) !== null && _c !== void 0 ? _c : this.rgbValues.red), green: Number((_d = innerResult.green) !== null && _d !== void 0 ? _d : this.rgbValues.green), blue: Number((_e = innerResult.blue) !== null && _e !== void 0 ? _e : this.rgbValues.blue) }; } else { this.colorHue = Number((_f = innerResult.hue) !== null && _f !== void 0 ? _f : this.colorHue); this.colorSaturation = Number((_g = innerResult.saturation) !== null && _g !== void 0 ? _g : this.colorSaturation); this.colorValue = Number((_h = innerResult.value) !== null && _h !== void 0 ? _h : this.colorValue); } } } logger_1.logger.debug(`[${this.deviceName}] Successfully retrieved bulb details`); } return success; } static clamp(value, min, max) { return Math.min(Math.max(value, min), max); } static hsvToRgb(h, s, v) { const saturation = VeSyncBulb.clamp(s, 0, 100) / 100; const value = VeSyncBulb.clamp(v, 0, 100) / 100; const hue = ((h % 360) + 360) % 360 / 60; const i = Math.floor(hue); const f = hue - i; const p = value * (1 - saturation); const q = value * (1 - saturation * f); const t = value * (1 - saturation * (1 - f)); let r = 0; let g = 0; let b = 0; switch (i) { case 0: r = value; g = t; b = p; break; case 1: r = q; g = value; b = p; break; case 2: r = p; g = value; b = t; break; case 3: r = p; g = q; b = value; break; case 4: r = t; g = p; b = value; break; default: r = value; g = p; b = q; break; } return { red: Math.round(r * 255), green: Math.round(g * 255), blue: Math.round(b * 255) }; } } exports.VeSyncBulb = VeSyncBulb;