UNPKG

homebridge-levoit-humidifiers

Version:
336 lines 15.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const CurrentState_1 = __importDefault(require("./characteristics/CurrentState")); const Humidity_1 = __importDefault(require("./characteristics/Humidity")); const Active_1 = __importDefault(require("./characteristics/Active")); const MistLevel_1 = __importDefault(require("./characteristics/MistLevel")); const TargetState_1 = __importDefault(require("./characteristics/TargetState")); const SleepState_1 = __importDefault(require("./characteristics/SleepState")); const LightBrightness_1 = __importDefault(require("./characteristics/LightBrightness")); const DisplayState_1 = __importDefault(require("./characteristics/DisplayState")); const TargetHumidity_1 = __importDefault(require("./characteristics/TargetHumidity")); const LightState_1 = __importDefault(require("./characteristics/LightState")); const WarmMistLevel_1 = __importDefault(require("./characteristics/WarmMistLevel")); const WarmActive_1 = __importDefault(require("./characteristics/WarmActive")); const AutoProState_1 = __importDefault(require("./characteristics/AutoProState")); const HumidifierName = 'Humidifier'; const HumiditySensorName = 'Humidity Sensor'; const MistName = 'Mist'; const CoolMistName = 'Cool Mist'; const WarmMistName = 'Warm Mist'; const NightLightName = 'Night Light'; const SleepModeName = 'Sleep Mode'; const DisplayName = 'Display'; const AutoProModeName = 'AutoPro Mode'; /** * VeSyncAccessory represents a single Levoit humidifier device in HomeKit. * It manages all services and characteristics for the device, including background * polling to keep device state synchronized without blocking HomeKit read requests. */ class VeSyncAccessory { get UUID() { return this.device.uuid.toString(); } get device() { return this.accessory.context.device; } constructor(platform, accessory) { this.platform = platform; this.accessory = accessory; /** * Background polling interval to keep device state fresh. * Polls every 30 seconds to balance freshness with API quota limits. * The VeSync API has daily quotas (3200 + 1500 * device count), so we * need to be conservative with polling frequency. */ this.pollingInterval = null; this.POLLING_INTERVAL_MS = 30000; // 30 seconds const config = platform.config; const accessories = config.accessories ? config.accessories : {}; this.setupAccessoryInfo(); this.humidifierService = this.setupHumidifierService(); this.mistService = this.setupMistService(accessories); this.displayService = this.setupDisplayService(accessories); this.humiditySensorService = this.setupHumiditySensorService(accessories); this.warmMistService = this.setupWarmMistService(accessories); this.sleepService = this.setupSleepService(accessories); this.lightService = this.setupLightService(accessories); this.autoProService = this.setupAutoProService(accessories); this.startPolling(); } setupAccessoryInfo() { const { manufacturer, model, mac } = this.device; this.accessory .getService(this.platform.Service.AccessoryInformation) .setCharacteristic(this.platform.Characteristic.Manufacturer, manufacturer) .setCharacteristic(this.platform.Characteristic.Model, model) .setCharacteristic(this.platform.Characteristic.SerialNumber, mac); } setupHumidifierService() { const service = this.accessory.getService(HumidifierName) || this.accessory.addService(this.platform.Service.HumidifierDehumidifier, HumidifierName, HumidifierName); service.setPrimaryService(true); service .getCharacteristic(this.platform.Characteristic.Active) .onGet(Active_1.default.get.bind(this)) .onSet(Active_1.default.set.bind(this)); service .getCharacteristic(this.platform.Characteristic.TargetHumidifierDehumidifierState) .setProps({ validValues: [1] }) .onGet(TargetState_1.default.get.bind(this)); service .getCharacteristic(this.platform.Characteristic.CurrentHumidifierDehumidifierState) .setProps({ validValues: [1, 2] }) .onGet(CurrentState_1.default.get.bind(this)); service .getCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold) .setProps({ minStep: 1, minValue: 0, maxValue: 100 }) .onGet(TargetHumidity_1.default.get.bind(this)) .onSet(TargetHumidity_1.default.set.bind(this)); service .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) .onGet(Humidity_1.default.get.bind(this)); return service; } /** * Gets or creates a service, updating its name. * Returns undefined and removes the service if the accessory config disables it. */ getOrCreateService(serviceType, name, enabled, fallbackName) { if (enabled) { let service = this.accessory.getService(name); service !== null && service !== void 0 ? service : (service = this.accessory.addService(serviceType, name, name)); service.updateCharacteristic(this.platform.Characteristic.Name, name); return service; } const existing = this.accessory.getService(name) || (fallbackName ? this.accessory.getService(fallbackName) : undefined); if (existing) { this.platform.log.info(`Removing ${name} service.`); this.accessory.removeService(existing); } return undefined; } setupMistService(accessories) { const enabled = accessories.mist !== false && accessories.cool_mist !== false; const service = this.getOrCreateService(this.platform.Service.Fan, MistName, enabled, CoolMistName); if (!service) { return undefined; } service .getCharacteristic(this.platform.Characteristic.On) .onGet(Active_1.default.get.bind(this)) .onSet(Active_1.default.set.bind(this)); service .getCharacteristic(this.platform.Characteristic.RotationSpeed) .setProps({ minStep: 1, minValue: 0, maxValue: 100 }) .onGet(MistLevel_1.default.get.bind(this)) .onSet(MistLevel_1.default.set.bind(this)); this.humidifierService.addLinkedService(service); return service; } setupDisplayService(accessories) { const service = this.getOrCreateService(this.platform.Service.Switch, DisplayName, accessories.display !== false); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.On) .onGet(DisplayState_1.default.get.bind(this)) .onSet(DisplayState_1.default.set.bind(this)); return service; } setupHumiditySensorService(accessories) { const service = this.getOrCreateService(this.platform.Service.HumiditySensor, HumiditySensorName, accessories.humidity_sensor !== false); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) .onGet(Humidity_1.default.get.bind(this)); return service; } setupWarmMistService(accessories) { const enabled = this.device.deviceType.hasWarmMode && accessories.warm_mist !== false; const service = this.getOrCreateService(this.platform.Service.Fan, WarmMistName, enabled); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.On) .onGet(WarmActive_1.default.get.bind(this)) .onSet(WarmActive_1.default.set.bind(this)); service .getCharacteristic(this.platform.Characteristic.RotationSpeed) .setProps({ minStep: 1, minValue: 0, maxValue: 100 }) .onGet(WarmMistLevel_1.default.get.bind(this)) .onSet(WarmMistLevel_1.default.set.bind(this)); return service; } setupSleepService(accessories) { const enabled = this.device.deviceType.hasSleepMode && accessories.sleep_mode !== false; const service = this.getOrCreateService(this.platform.Service.Switch, SleepModeName, enabled); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.On) .onGet(SleepState_1.default.get.bind(this)) .onSet(SleepState_1.default.set.bind(this)); return service; } setupLightService(accessories) { const enabled = this.device.deviceType.hasLight && accessories.night_light !== false; const service = this.getOrCreateService(this.platform.Service.Lightbulb, NightLightName, enabled); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.On) .onGet(LightState_1.default.get.bind(this)) .onSet(LightState_1.default.set.bind(this)); const props = this.device.deviceType.hasColorMode ? { minValue: 39, maxValue: 100 } : { minStep: 25, minValue: 0, maxValue: 100, validValues: [0, 25, 50, 75, 100], }; service .getCharacteristic(this.platform.Characteristic.Brightness) .setProps(props) .onGet(LightBrightness_1.default.get.bind(this)) .onSet(LightBrightness_1.default.set.bind(this)); return service; } setupAutoProService(accessories) { const enabled = !!this.device.deviceType.hasAutoProMode && accessories.auto_pro !== false; const service = this.getOrCreateService(this.platform.Service.Switch, AutoProModeName, enabled); if (!service) { return undefined; } this.humidifierService.addLinkedService(service); service .getCharacteristic(this.platform.Characteristic.On) .onGet(AutoProState_1.default.get.bind(this)) .onSet(AutoProState_1.default.set.bind(this)); return service; } /** * Starts background polling to periodically update device state. * This ensures characteristics always have fresh data without blocking * HomeKit read requests, which prevents "slow to respond" warnings. */ startPolling() { this.device.updateInfo().catch((err) => { this.platform.log.debug(`[${this.device.name}] Initial device update failed:`, err instanceof Error ? err.message : String(err)); }); this.pollingInterval = setInterval(() => { this.device.updateInfo().catch((err) => { this.platform.log.debug(`[${this.device.name}] Background polling update failed:`, err instanceof Error ? err.message : String(err)); }); }, this.POLLING_INTERVAL_MS); } /** * Stops background polling. * Should be called when the accessory is removed to clean up resources. */ stopPolling() { if (this.pollingInterval) { clearInterval(this.pollingInterval); this.pollingInterval = null; } } /** * Updates all HomeKit characteristics to match current device state. * Call this after any device command to immediately reflect changes in HomeKit. */ updateAllCharacteristics() { const { device } = this; this.updateHumidifierCharacteristics(device); this.updateOptionalServiceCharacteristics(device); } updateHumidifierCharacteristics(device) { this.humidifierService .getCharacteristic(this.platform.Characteristic.Active) .updateValue(device.isOn ? 1 : 0); this.humidifierService .getCharacteristic(this.platform.Characteristic.CurrentHumidifierDehumidifierState) .updateValue(device.isOn ? this.platform.Characteristic.CurrentHumidifierDehumidifierState .HUMIDIFYING : this.platform.Characteristic.CurrentHumidifierDehumidifierState .IDLE); this.humidifierService .getCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold) .updateValue(device.isOn ? device.targetHumidity : 0); this.humidifierService .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) .updateValue(device.humidityLevel); } updateOptionalServiceCharacteristics(device) { var _a; if (this.humiditySensorService) { this.humiditySensorService .getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity) .updateValue(device.humidityLevel); } if (this.mistService) { this.mistService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.isOn); const mistPct = device.isOn ? Math.round((device.mistLevel / device.deviceType.mistLevels) * 100) : 0; this.mistService .getCharacteristic(this.platform.Characteristic.RotationSpeed) .updateValue(mistPct); } if (this.warmMistService) { this.warmMistService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.warmEnabled); const maxWarm = (_a = device.deviceType.warmMistLevels) !== null && _a !== void 0 ? _a : 0; const warmPct = device.isOn && maxWarm > 0 ? Math.round((device.warmLevel / maxWarm) * 100) : 0; this.warmMistService .getCharacteristic(this.platform.Characteristic.RotationSpeed) .updateValue(warmPct); } if (this.lightService) { this.lightService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.lightOn === 'on'); this.lightService .getCharacteristic(this.platform.Characteristic.Brightness) .updateValue(device.brightnessLevel); } if (this.displayService) { this.displayService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.isOn && device.displayOn); } if (this.sleepService) { this.sleepService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.isOn && device.mode === 'sleep'); } if (this.autoProService) { this.autoProService .getCharacteristic(this.platform.Characteristic.On) .updateValue(device.isOn && device.mode === 'autoPro'); } } } exports.default = VeSyncAccessory; //# sourceMappingURL=VeSyncAccessory.js.map