From 46b93adff7832657fb93507d9fcd70d54e827c5e Mon Sep 17 00:00:00 2001 From: Paul Schroeder Date: Mon, 18 Dec 2023 21:44:21 -0700 Subject: [PATCH] 55: support LEH-S601S-WUS and allow disabling humidity sensors --- .vscode/launch.json | 2 +- config.schema.json | 6 +++ package.json | 2 +- src/VeSyncAccessory.ts | 19 +++++---- src/api/VeSync.ts | 5 ++- src/api/VeSyncFan.ts | 55 +++++++++++++------------- src/api/deviceTypes.ts | 21 ++++++++++ src/characteristics/LightBrightness.ts | 2 +- 8 files changed, 71 insertions(+), 41 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index dfb7b64..a49e7fa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,7 +2,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch Extron Debugging", + "name": "Launch Debugging", "program": "node_modules/homebridge/bin/homebridge", "preLaunchTask": "npm: build", "request": "launch", diff --git a/config.schema.json b/config.schema.json index d413f3f..925e9ea 100644 --- a/config.schema.json +++ b/config.schema.json @@ -55,6 +55,12 @@ "type": "boolean", "default": true, "description": "Enable brightness slider / switch for the Night Light" + }, + "humidity_sensor": { + "title": "Humidity Sensor", + "type": "boolean", + "default": true, + "description": "Expose humidity sensor from device(s) to HomeKit" } } }, diff --git a/package.json b/package.json index 4044a38..799854e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "displayName": "Levoit Humidifiers", "main": "dist/index.js", "license": "Apache-2.0", - "version": "1.9.0", + "version": "1.9.2-beta1", "private": false, "bugs": { "url": "https://github.com/pschroeder89/homebridge-levoit-humidifiers/issues" diff --git a/src/VeSyncAccessory.ts b/src/VeSyncAccessory.ts index 7a28cb8..b71c227 100644 --- a/src/VeSyncAccessory.ts +++ b/src/VeSyncAccessory.ts @@ -31,7 +31,7 @@ export type AccessoryThisType = ThisType<{ export default class VeSyncAccessory { private humidifierService: Service; - private humiditySensorService: Service; + private humiditySensorService: Service | undefined; private lightService: Service | undefined; private sleepService: Service | undefined; private displayService: Service | undefined; @@ -83,6 +83,7 @@ export default class VeSyncAccessory { const nightLightAccessory = (accessories.night_light != false); const sleepModeAccessory = (accessories.sleep_mode != false); const displayAccessory = (accessories.display != false); + const humiditySensor = (accessories.humidity_sensor != false); // Accessory info this.accessory @@ -186,15 +187,17 @@ export default class VeSyncAccessory { } // Humidity Sensor service - this.humiditySensorService = - this.accessory.getService(HumiditySensorName) || - this.accessory.addService(this.platform.Service.HumiditySensor, HumiditySensorName, HumiditySensorName); + if (humiditySensor) { + this.humiditySensorService = + this.accessory.getService(HumiditySensorName) || + this.accessory.addService(this.platform.Service.HumiditySensor, HumiditySensorName, HumiditySensorName); - this.humidifierService.addLinkedService(this.humiditySensorService); + this.humidifierService.addLinkedService(this.humiditySensorService); - this.humiditySensorService - .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) - .onGet(Humidity.get.bind(this)); + this.humiditySensorService + .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) + .onGet(Humidity.get.bind(this)); + } // Warm Mist service if (this.device.deviceType.hasWarmMode && warmMistAccessory) { diff --git a/src/api/VeSync.ts b/src/api/VeSync.ts index 71165f0..3f7f58f 100644 --- a/src/api/VeSync.ts +++ b/src/api/VeSync.ts @@ -34,7 +34,7 @@ export default class VeSync { private readonly AXIOS_OPTIONS = { baseURL: 'https://smartapi.vesync.com', - timeout: 15000 + timeout: this.config.options.apiTimeout || 15000 }; constructor( @@ -221,7 +221,8 @@ export default class VeSync { ...this.AXIOS_OPTIONS } ); - + this.debugMode.debug("Axios timeout is: " + this.config.options.apiTimeout); + if (!response?.data) { this.debugMode.debug( '[LOGIN]', diff --git a/src/api/VeSyncFan.ts b/src/api/VeSyncFan.ts index e3f89b9..ed5bc84 100644 --- a/src/api/VeSyncFan.ts +++ b/src/api/VeSyncFan.ts @@ -1,6 +1,5 @@ import AsyncLock from 'async-lock'; -import { json } from 'stream/consumers'; -import deviceTypes, { DeviceName, DeviceType } from './deviceTypes'; +import deviceTypes, { DeviceName, DeviceType, NewDevices } from './deviceTypes'; import VeSync, { BypassMethod } from './VeSync'; @@ -124,16 +123,16 @@ export default class VeSyncFan { this.client.log.info("Setting Power to " + power); // Oasis 1000 uses a different field to set power let switchJson; - if (this.model.includes("LUH-M101S")) { + if (NewDevices.includes(this.model as DeviceName)) { switchJson = { powerSwitch: power ? 1 : 0, id: 0 - } + }; } else { switchJson = { enabled: power, id: 0 - } + }; } const success = await this.client.sendCommand(this, BypassMethod.SWITCH, switchJson); @@ -169,16 +168,16 @@ export default class VeSyncFan { // Oasis 1000 uses camelcase instead of snakecase let humidityJson; - if (this.model.includes("LUH-M101S")) { + if (NewDevices.includes(this.model as DeviceName)) { humidityJson = { "targetHumidity": level, id: 0 - } + }; } else { humidityJson = { "target_humidity": level, id: 0 - } + }; } const success = await this.client.sendCommand(this, BypassMethod.HUMIDITY, humidityJson); @@ -207,14 +206,14 @@ export default class VeSyncFan { // Oasis 1000 uses camelcase instead of snakecase let modeJson; - if (this.model.includes("LUH-M101S")) { + if (NewDevices.includes(this.model as DeviceName)) { modeJson = { "workMode": mode.toString(), - } + }; } else { modeJson = { "mode": mode.toString(), - } + }; } // Don't change the mode if we are already in that mode if (this._mode == mode) { @@ -250,16 +249,16 @@ export default class VeSyncFan { // Oasis 1000 uses camelcase instead of snakecase let displayJson; - if (this.model.includes("LUH-M101S")) { + if (NewDevices.includes(this.model as DeviceName)) { displayJson = { screenSwitch: power ? 1 : 0, id: 0 - } + }; } else { displayJson = { state: power, id: 0 - } + }; } const success = await this.client.sendCommand(this, BypassMethod.DISPLAY, displayJson); @@ -282,8 +281,8 @@ export default class VeSyncFan { let coolMistJson; let method; - if (this.model.includes("LUH-M101S")) { - // We don't know the correct structure of the JSON, so this does not work. Cool Mist slider is removed from hhis model for now. + if (NewDevices.includes(this.model as DeviceName)) { + // We don't know the correct structure of the JSON, so this does not work. Cool Mist slider is removed from this model for now. // method = BypassMethod.LEVEL // coolMistJson = { // level: coolMistLevel, @@ -291,12 +290,12 @@ export default class VeSyncFan { // id: 0 // } } else { - method = BypassMethod.MIST_LEVEL + method = BypassMethod.MIST_LEVEL; coolMistJson = { level: coolMistLevel, type: 'mist', id: 0 - } + }; } const success = await this.client.sendCommand(this, method, coolMistJson); @@ -342,10 +341,10 @@ export default class VeSyncFan { public async setLightStatus(action: string, brightness: number): Promise { // Get the current RGB values and brightness % - let red = this._red; - let green = this._green; - let blue = this._blue; - let currentBrightness = this.brightnessLevel; + const red = this._red; + const green = this._green; + const blue = this._blue; + const currentBrightness = this.brightnessLevel; let newRed; let newBlue; let newGreen; @@ -373,10 +372,10 @@ export default class VeSyncFan { if (success) { this._brightnessLevel = brightness; - this._blue = newBlue || this.getBlue - this._green = newGreen || this.getGreen - this._red = newRed || this.getRed - this._lightOn = action + this._blue = newBlue || this.getBlue; + this._green = newGreen || this.getGreen; + this._red = newRed || this.getRed; + this._lightOn = action; // Not setting these for now, so don't set them // this._lightSpeed = this.getLightSpeed // this._colorMode = this.getColorMode @@ -412,8 +411,8 @@ export default class VeSyncFan { const result = data?.result?.result; this._humidityLevel = result.humidity; - // Fields are different on OasisMist 1000s - if (this.model.includes("LUH-M101S")) { + // Fields are different on newer models + if (NewDevices.includes(this.model as DeviceName)) { this._targetHumidity = result.targetHumidity; this._displayOn = result.screenSwitch; this._mode = result.workMode; diff --git a/src/api/deviceTypes.ts b/src/api/deviceTypes.ts index 31eee64..93f5912 100644 --- a/src/api/deviceTypes.ts +++ b/src/api/deviceTypes.ts @@ -20,8 +20,17 @@ export enum DeviceName { OASIS_1000S_UK = "LUH-M101S-WUK", OASIS_1000S_EU = "LUH-M101S-WEU", OASIS_1000S_JP = "LUH-M101S-WJP", + LEH_S601S_WUS = "LEH-S601S-WUS" } +export const NewDevices = [ + DeviceName.OASIS_1000S, + DeviceName.OASIS_1000S_EU, + DeviceName.OASIS_1000S_JP, + DeviceName.OASIS_1000S_UK, + DeviceName.LEH_S601S_WUS +]; + export interface DeviceType { isValid: (input: string) => boolean; hasAutoMode: boolean; @@ -295,6 +304,18 @@ const deviceTypes: DeviceType[] = [ minHumidityLevel: 40, maxHumidityLevel: 80 }, + { + isValid: (input: string) => + input.includes(DeviceName.LEH_S601S_WUS), + hasAutoMode: true, + coolMistLevels: 9, + hasLight: false, + hasColorMode: false, + hasSleepMode: true, + hasWarmMode: false, + minHumidityLevel: 40, + maxHumidityLevel: 80 + }, ]; export default deviceTypes; diff --git a/src/characteristics/LightBrightness.ts b/src/characteristics/LightBrightness.ts index 76773c0..a5b94b4 100644 --- a/src/characteristics/LightBrightness.ts +++ b/src/characteristics/LightBrightness.ts @@ -24,7 +24,7 @@ const characteristic: { // We allow 39 as a value so 40 doesn't turn off the device. // So never set the device to 39, since that's not actually supported if (value === 39) { - value + 1 + value + 1; } let action: string; if (value >= 40) {