UNPKG

homebridge-tsvesync

Version:

Homebridge plugin for VeSync devices including Levoit air purifiers, humidifiers, and Etekcity smart outlets

277 lines 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AirQualitySensorAccessory = void 0; const base_accessory_1 = require("./base.accessory"); /** * Air Quality Sensor Accessory * This is a separate accessory that only provides air quality monitoring * Used for Core300S, Core400S, Core600S and other devices with AQ sensors */ class AirQualitySensorAccessory extends base_accessory_1.BaseAccessory { constructor(platform, accessory, device) { super(platform, accessory, device); this.parentDevice = device; // Set accessory information this.accessory.getService(this.platform.Service.AccessoryInformation) .setCharacteristic(this.platform.Characteristic.Manufacturer, 'VeSync') .setCharacteristic(this.platform.Characteristic.Model, `${device.deviceType || 'Unknown'} AQ Sensor`) .setCharacteristic(this.platform.Characteristic.SerialNumber, `${device.cid || device.uuid}-AQ`); // Get or create the Air Quality Sensor service this.service = this.accessory.getService(this.platform.Service.AirQualitySensor) || this.accessory.addService(this.platform.Service.AirQualitySensor); // Set the service name this.service.setCharacteristic(this.platform.Characteristic.Name, `${device.deviceName} Air Quality`); // Configure the Air Quality characteristic this.service.getCharacteristic(this.platform.Characteristic.AirQuality) .setProps({ validValues: [ this.platform.Characteristic.AirQuality.UNKNOWN, this.platform.Characteristic.AirQuality.EXCELLENT, this.platform.Characteristic.AirQuality.GOOD, this.platform.Characteristic.AirQuality.FAIR, this.platform.Characteristic.AirQuality.INFERIOR, ], }) .onGet(this.getAirQuality.bind(this)); // Add PM2.5 Density characteristic if device supports it if (this.deviceSupportsPM25()) { this.service.getCharacteristic(this.platform.Characteristic.PM2_5Density) .onGet(this.getPM25Density.bind(this)); } // Add PM10 Density characteristic if device supports it if (this.deviceSupportsPM10()) { this.service.getCharacteristic(this.platform.Characteristic.PM10Density) .onGet(this.getPM10Density.bind(this)); } // Polling is handled by BaseAccessory } /** * Check if device supports PM2.5 readings */ deviceSupportsPM25() { // Core300S, Core400S, Core600S all support PM2.5 const deviceType = this.parentDevice.deviceType || ''; return deviceType.includes('Core300S') || deviceType.includes('Core400S') || deviceType.includes('Core600S') || deviceType.includes('LAP-') || deviceType.includes('LV-PUR'); } /** * Check if device supports PM10 readings */ deviceSupportsPM10() { // Some newer devices support PM10 const deviceType = this.parentDevice.deviceType || ''; return deviceType.includes('Core600S') || deviceType.includes('LAP-V'); } /** * Get the current air quality level */ async getAirQuality() { try { const level = this.readNormalizedAirQualityLevel(); if (level >= 1) { const hkValue = this.mapToHomeKitAirQuality(level); this.platform.log.debug(`${this.device.deviceName} AQ: Normalized air quality level = ${level}`); return hkValue; } const pm25 = this.getDevicePm25(); this.platform.log.debug(`${this.device.deviceName} AQ: Falling back to PM2.5 heuristic (${pm25})`); return this.convertPM25ToAirQuality(pm25); } catch (error) { this.platform.log.error(`${this.device.deviceName} AQ: Failed to get air quality:`, error); return this.platform.Characteristic.AirQuality.UNKNOWN; } } /** * Get the current PM2.5 density */ async getPM25Density() { try { const pm25 = this.getDevicePm25(); // HomeKit expects PM2.5 in μg/m³, range 0-1000 return Math.min(1000, Math.max(0, pm25)); } catch (error) { this.platform.log.error(`${this.device.deviceName} AQ: Failed to get PM2.5:`, error); return 0; } } /** * Get the current PM10 density */ async getPM10Density() { var _a; try { const extendedDevice = this.parentDevice; const pm10 = ((_a = extendedDevice.details) === null || _a === void 0 ? void 0 : _a.pm10) || 0; // HomeKit expects PM10 in μg/m³, range 0-1000 return Math.min(1000, Math.max(0, pm10)); } catch (error) { this.platform.log.error(`${this.device.deviceName} AQ: Failed to get PM10:`, error); return 0; } } /** * Convert PM2.5 value to HomeKit AirQuality level * Based on EPA Air Quality Index (AQI) standards */ convertPM25ToAirQuality(pm25) { if (pm25 <= 12) { return this.platform.Characteristic.AirQuality.EXCELLENT; // Good (0-12 μg/m³) } else if (pm25 <= 35) { return this.platform.Characteristic.AirQuality.GOOD; // Moderate (12.1-35.4 μg/m³) } else if (pm25 <= 55) { return this.platform.Characteristic.AirQuality.FAIR; // Unhealthy for Sensitive (35.5-55.4 μg/m³) } return this.platform.Characteristic.AirQuality.INFERIOR; } mapToHomeKitAirQuality(level) { switch (level) { case 1: return this.platform.Characteristic.AirQuality.EXCELLENT; case 2: return this.platform.Characteristic.AirQuality.GOOD; case 3: return this.platform.Characteristic.AirQuality.FAIR; case 4: return this.platform.Characteristic.AirQuality.INFERIOR; default: return this.platform.Characteristic.AirQuality.UNKNOWN; } } readNormalizedAirQualityLevel() { var _a, _b, _c, _d, _e; const extendedDevice = this.parentDevice; if (typeof extendedDevice.getNormalizedAirQuality === 'function') { try { const normalized = extendedDevice.getNormalizedAirQuality(); if (normalized && typeof normalized.level === 'number' && normalized.level >= 1) { return normalized.level; } } catch (error) { this.platform.log.debug(`${this.device.deviceName} AQ: getNormalizedAirQuality failed`, error); } } const directLevel = (_a = extendedDevice.airQualityLevel) !== null && _a !== void 0 ? _a : (_b = extendedDevice.details) === null || _b === void 0 ? void 0 : _b.air_quality_level; if (typeof directLevel === 'number' && directLevel >= 1) { return directLevel; } const normalized = this.normalizeAirQuality((_e = (_c = extendedDevice.airQuality) !== null && _c !== void 0 ? _c : (_d = extendedDevice.details) === null || _d === void 0 ? void 0 : _d.air_quality) !== null && _e !== void 0 ? _e : extendedDevice.airQualityLabel); return normalized.level; } getDevicePm25() { var _a, _b, _c, _d; const extendedDevice = this.parentDevice; const pm25 = (_d = (_c = (_a = extendedDevice.airQualityValue) !== null && _a !== void 0 ? _a : (_b = extendedDevice.details) === null || _b === void 0 ? void 0 : _b.air_quality_value) !== null && _c !== void 0 ? _c : extendedDevice.pm25) !== null && _d !== void 0 ? _d : 0; return Number.isFinite(pm25) ? pm25 : 0; } normalizeAirQuality(value) { const stringMap = { 'excellent': 1, 'very good': 1, 'good': 2, 'moderate': 3, 'fair': 3, 'inferior': 4, 'poor': 4, 'bad': 4, }; if (typeof value === 'number') { const level = Number.isFinite(value) ? Math.trunc(value) : -1; if (level >= 1 && level <= 4) { return { level, label: level === 1 ? 'excellent' : level === 2 ? 'good' : level === 3 ? 'moderate' : 'poor', }; } } else if (typeof value === 'string') { const normalized = value.trim().toLowerCase(); const level = stringMap[normalized]; if (level) { return { level, label: level === 1 ? 'excellent' : level === 2 ? 'good' : level === 3 ? 'moderate' : 'poor', }; } } return { level: -1, label: 'unknown' }; } /** * Update characteristics from device state */ async updateCharacteristics() { try { // Make sure device data is current const extendedDevice = this.parentDevice; if (this.shouldSkipDeviceDetailsRefresh(extendedDevice)) { this.platform.log.debug(`${this.device.deviceName} AQ: Skipping refresh because parent device is offline`); } else if (!this.hasFreshDeviceDetails() && typeof extendedDevice.getDetails === 'function') { await extendedDevice.getDetails(); this.cacheDeviceDetails(extendedDevice); } // Update air quality const airQuality = await this.getAirQuality(); this.service.updateCharacteristic(this.platform.Characteristic.AirQuality, airQuality); // Update PM2.5 if supported if (this.deviceSupportsPM25()) { const pm25 = await this.getPM25Density(); this.service.updateCharacteristic(this.platform.Characteristic.PM2_5Density, pm25); } // Update PM10 if supported if (this.deviceSupportsPM10()) { const pm10 = await this.getPM10Density(); this.service.updateCharacteristic(this.platform.Characteristic.PM10Density, pm10); } this.platform.log.debug(`${this.device.deviceName} AQ: Updated air quality characteristics`); } catch (error) { this.platform.log.error(`${this.device.deviceName} AQ: Failed to update characteristics:`, error); } } /** * Get device capabilities (required by BaseAccessory) */ getDeviceCapabilities() { return { hasSpeed: false, hasAirQuality: true, hasChildLock: false, hasBrightness: false, hasHumidity: false, hasWaterLevel: false, hasSwingMode: false, hasColorTemp: false, hasColor: false, }; } /** * Setup the service (required by BaseAccessory) */ setupService() { // Service is already set up in constructor } /** * Update device specific states (required by BaseAccessory) */ async updateDeviceSpecificStates() { await this.updateCharacteristics(); } /** * Identify this accessory */ identify() { this.platform.log.info(`${this.device.deviceName} AQ Sensor: Identify!`); } } exports.AirQualitySensorAccessory = AirQualitySensorAccessory; //# sourceMappingURL=air-quality-sensor.accessory.js.map