UNPKG

homebridge-purpleair-sensor

Version:
190 lines 8.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SensorReading = void 0; exports.parsePurpleAirJson = parsePurpleAirJson; function parsePurpleAirJson(data, averages, conversion, usesLocalSensor = false) { if (usesLocalSensor) { return parseLocalPurpleAirJson(data, averages, conversion); } else { return parseRemotePurpleAirJson(data, averages, conversion); } } function parseRemotePurpleAirJson(data, averages, conversion) { const sensor_data = data.sensor; const sensor_stats = sensor_data.stats; const conv = conversion !== null && conversion !== void 0 ? conversion : 'None'; const pm25 = getPM25(sensor_data, sensor_stats, averages); const pm25alt = parseFloat(sensor_data['pm2.5_alt']); const pm25Cf1 = parseFloat(sensor_data['pm2.5_cf_1']); const humidity = parseFloat(sensor_data.humidity) + 4; const sensor = sensor_data.sensor_index; const voc = sensor_data.voc ? parseFloat(sensor_data.voc) : null; const temperature = convertTemperatureToCelcius(parseFloat(sensor_data.temperature)); return new SensorReading(sensor, pm25, pm25Cf1, humidity, temperature, voc, conv, pm25alt); } function parseLocalPurpleAirJson(data, averages, conversion) { const conv = conversion !== null && conversion !== void 0 ? conversion : 'None'; const pm25 = parseFloat(data.pm2_5_atm); const pm25alt = pm25; // local sensors don't have pm2.5_alt (yet?) // TODO: calculate it https://github.com/jmkk/homebridge-purpleair-sensor/issues/58 const pm25Cf1 = parseFloat(data.pm2_5_cf_1); const humidity = parseFloat(data.current_humidity) + 4; const sensor = data.Id; const temperature = convertTemperatureToCelcius(parseFloat(data.current_temp_f)); const voc = data.gas_680 ? parseFloat(data.gas_680) : null; return new SensorReading(sensor, pm25, pm25Cf1, humidity, temperature, voc, conv, pm25alt); } function getPM25(sensor_data, sensor_stats, averages) { switch (averages) { case '10m': return sensor_stats['pm2.5_10minute']; case '30m': return sensor_stats['pm2.5_30minute']; case '60m': return sensor_stats['pm2.5_60minute']; default: return parseFloat(sensor_data['pm2.5']); } } function convertTemperatureToCelcius(temperature) { // Correct temperature by 8 degrees F before conversion // https://community.purpleair.com/t/purpleair-sensors-functional-overview/150 return Math.round(((temperature - 8) - 32) * 5 / 9); } class SensorReading { /** * Constructor * @param sensor sensor station number (digits) * @param pm25 sensor pm 2.5 value (PM2_5Value) * @param pm25Cf1 sensor pm 2.5 value from CF1 / standard particles (pm2_5_cf_1) * @param humidity sensor humidity value * @param temperature sensor temperature value * @param voc sensor Voc value * @param conversion conversion ("None", "AQandU", "LRAPA", "EPA", or "WOODSMOKE"). Default to None. * @param pm25alt sensor pm 2.5 value from alt (pm2_5_alt) */ constructor(sensor, pm25, pm25Cf1, humidity, temperature, voc, conversion, pm25alt) { this.sensor = sensor; this.pm25 = pm25; this.pm25Cf1 = pm25Cf1; this.humidity = humidity; this.temperature = temperature; this.voc = voc; this.conversion = conversion; this.pm25alt = pm25alt; this.toString = () => { // eslint-disable-next-line max-len return `(AQI=${this.aqi.toFixed(0)}, PM25=${this.pm25}u/m3, PM25_CF1=${this.pm25Cf1}u/m3, Humidity=${this.humidity}, temperature=${this.temperature}, VOC=${this.voc}, PM25ALT=${this.pm25alt})`; }; this.updateTimeMs = Date.now(); } get aqi() { switch (this.conversion) { case 'AQandU': { return SensorReading.pmToAQandU(this.pm25); } case 'LRAPA': { return SensorReading.pmToLRAPA(this.pm25Cf1); } case 'EPA': { return SensorReading.pmToEPA(this.pm25Cf1, this.humidity); } case 'WOODSMOKE': { return SensorReading.pmToWoodsmoke(this.pm25Cf1); } case 'ALT-CF3': { return SensorReading.pmToAQI(this.pm25alt); } default: { return SensorReading.pmToAQI(this.pm25); } } } get airQualityHomekitReading() { return SensorReading.aqiToHomekit(this.aqi); } static aqiToHomekit(aqi) { // This calculation was lifted from https://github.com/SANdood/homebridge-purpleair. if (aqi === undefined) { return 0; // Error or unknown response } else if (aqi <= 50) { return 1; // Return EXCELLENT } else if (aqi <= 100) { return 2; // Return GOOD } else if (aqi <= 150) { return 3; // Return FAIR } else if (aqi <= 200) { return 4; // Return INFERIOR } else if (aqi > 200) { return 5; // Return POOR (Homekit only goes to cat 5, so combined the last two AQI cats of Very Unhealty and Hazardous. } return 0; } static pmToAQandU(pm) { // formula found on https://www.purpleair.com/map, shown when you hover on the `?` next to `Conversion` // PM2.5 (µg/m³) = 0.778 x PA + 2.65 return this.pmToAQI(0.778 * pm + 2.65); } static pmToLRAPA(paCf1) { // formula found on https://www.purpleair.com/map, shown when you hover on the `?` next to `Conversion` // 0 - 65 µg/m³ range: // LRAPA PM2.5 (µg/m³) = 0.5 x PA (PM2.5 CF=ATM) – 0.66 // note that this calculation at PurpleAir seems wrong, their PM2.5 values are from CF=ATM (atmo) rather than CF=1 (standard particles) return this.pmToAQI(0.5 * paCf1 - 0.66); } static pmToEPA(paCf1, humidity) { // formula found on https://www.purpleair.com/map, shown when you hover on the `?` next to `Conversion` // 0-250 ug/m3 range (>250 may underestimate true PM2.5): // PM2.5 (µg/m³) = 0.534 x PA(cf_1) - 0.0844 x RH + 5.604 // eslint-disable-next-line max-len // more at https://cfpub.epa.gov/si/si_public_record_report.cfm?dirEntryId=349513&Lab=CEMM&simplesearch=0&showcriteria=2&sortby=pubDate&timstype=&datebeginpublishedpresented=08/25/2018 return this.pmToAQI(0.534 * paCf1 - 0.0844 * humidity + 5.604); } static pmToWoodsmoke(paCf1) { // formula found on https://www.purpleair.com/map, shown when you hover on the `?` next to `Conversion` // eslint-disable-next-line max-len // From a study in Australia comparing Purple Air with NSW Government TEOM PM2.5 and Armidale Regional Council's DustTrak measurements - see published peer-reviewed study - https://www.mdpi.com/2073-4433/11/8/856/htm. // Woodsmoke PM2.5 (µg/m³) = 0.55 x PA (PM2.5 CF=1) + 0.53 return this.pmToAQI(0.55 * paCf1 + 0.53); } static pmToAQI(pm) { let aqi; if (pm > 350.5) { aqi = this.calcAQI(pm, 500, 401, 500, 350.5); } else if (pm > 250.5) { aqi = this.calcAQI(pm, 400, 301, 350.4, 250.5); } else if (pm > 150.5) { aqi = this.calcAQI(pm, 300, 201, 250.4, 150.5); } else if (pm > 55.5) { aqi = this.calcAQI(pm, 200, 151, 150.4, 55.5); } else if (pm > 35.5) { aqi = this.calcAQI(pm, 150, 101, 55.4, 35.5); } else if (pm > 12.1) { aqi = this.calcAQI(pm, 100, 51, 35.4, 12.1); } else if (pm >= 0) { aqi = this.calcAQI(pm, 50, 0, 12, 0); } else { aqi = 0; } return aqi; } static calcAQI(Cp, Ih, Il, BPh, BPl) { // The AQI equation https://forum.airnowtech.org/t/the-aqi-equation/169 // c = concentration, I = AQI, and BP = breakpoint const a = Ih - Il; const b = BPh - BPl; const c = Cp - BPl; return Math.round((a / b) * c + Il); } } exports.SensorReading = SensorReading; //# sourceMappingURL=SensorReading.js.map