matterbridge-dyson-robot
Version:
A Matterbridge plugin that connects Dyson robot vacuums and air treatment devices to the Matter smart home ecosystem via their local or cloud MQTT APIs.
101 lines • 4.68 kB
JavaScript
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
// Copyright © 2025 Alexander Thoukydides
import { AirQuality, ConcentrationMeasurement } from 'matterbridge/matter/clusters';
import { assertIsDefined, formatList } from './utils.js';
// Thresholds to map different pollutants to Air Quality levels:
// co2r CO2 Common indoor air quality guidelines
// hcho Formaldehyde WHO guidelines and typical concern levels
// pm25 PM2.5 US EPA 24-hour AQI breakpoints *
// pm10 PM10 US EPA 24-hour AQI breakpoints *
// vact VOC Arbitrary mapping of unknown index values
// noxl NOx Arbitrary mapping of unknown index values
// pact Dust Arbitrary mapping of unknown index values
// * = https://aqs.epa.gov/aqsweb/documents/codetables/aqi_breakpoints.html
const AQI_COLUMNS = ['co2r', 'hcho', 'pm25', 'pm10', 'vact', 'noxl', 'pact'
];
const AQI_THRESHOLDS = [
// ppm µg/m³ µg/m³ µg/m³ 0~99 0~99 0~9
[AirQuality.AirQualityEnum.Good, [800, 10, 9.0, 54, 10, 10, 0]],
[AirQuality.AirQualityEnum.Fair, [1000, 30, 35.4, 154, 25, 25, 1]],
[AirQuality.AirQualityEnum.Moderate, [1500, 60, 55.4, 254, 50, 50, 3]],
[AirQuality.AirQualityEnum.Poor, [2500, 100, 125.4, 354, 75, 75, 5]],
[AirQuality.AirQualityEnum.VeryPoor, [5000, 200, 225.4, 424, 90, 90, 7]]
// (AirQuality.AirQualityEnum.ExtremelyPoor for anything higher)
];
// Mapping from Air Quality to Concentration Measurement levels
const LEVEL_MAP = {
[AirQuality.AirQualityEnum.Unknown]: ConcentrationMeasurement.LevelValue.Unknown,
[AirQuality.AirQualityEnum.Good]: ConcentrationMeasurement.LevelValue.Low,
[AirQuality.AirQualityEnum.Fair]: ConcentrationMeasurement.LevelValue.Low,
[AirQuality.AirQualityEnum.Moderate]: ConcentrationMeasurement.LevelValue.Medium,
[AirQuality.AirQualityEnum.Poor]: ConcentrationMeasurement.LevelValue.High,
[AirQuality.AirQualityEnum.VeryPoor]: ConcentrationMeasurement.LevelValue.High,
[AirQuality.AirQualityEnum.ExtremelyPoor]: ConcentrationMeasurement.LevelValue.Critical
};
// Map numeric sensor values to Air Quality Sensor enum values
function mapToAirQualitySensorEnum(sensors) {
const aqiValues = new Map();
AQI_COLUMNS.forEach((key, index) => {
const value = sensors[key];
if (value === undefined)
return;
if (typeof value === 'number') {
const aqiRow = AQI_THRESHOLDS.find(([, thresholds]) => value <= (thresholds[index] ?? Infinity));
const aqi = aqiRow ? aqiRow[0] : AirQuality.AirQualityEnum.ExtremelyPoor;
aqiValues.set(key, aqi);
}
else {
aqiValues.set(key, AirQuality.AirQualityEnum.Unknown);
}
});
return aqiValues;
}
// Determine the overall air quality as the worst of the available data
function worstAirQuality(log, aqiValues) {
const values = [];
let aqi = AirQuality.AirQualityEnum.Unknown;
aqiValues.forEach((aqiValue, key) => {
if (aqiValue > aqi)
aqi = aqiValue;
values.push(`${key}:${AirQuality.AirQualityEnum[aqiValue]}`);
});
log.debug(`Air quality is ${AirQuality.AirQualityEnum[aqi]} (${formatList(values)})`);
return aqi;
}
// Map Air Quality Sensor values to Concentration Measurement levels
function mapToAirQualityLevelEnum(aqiValues) {
const levelValues = new Map();
aqiValues.forEach((aqi, key) => {
levelValues.set(key, LEVEL_MAP[aqi]);
});
return levelValues;
}
export function numeric(value, factor = 1) {
if (value === undefined)
return undefined;
if (typeof value === 'string')
return null;
return Math.round(value * factor);
}
// Map the Dyson sensor data to Matter attribute values
export function mapDysonAirSensorStatus(log, status) {
const { tact, hact, co2r, hcho, pm25, pm10 } = status;
assertIsDefined(tact);
assertIsDefined(hact);
// Map the available measurements to Air Quality
const aqiValues = mapToAirQualitySensorEnum(status);
const levelValues = mapToAirQualityLevelEnum(aqiValues);
// Return the mapped sensor readings
return {
airQuality: worstAirQuality(log, aqiValues),
temperature: numeric(tact, 100), // centi-°C
humidity: numeric(hact, 100), // centi-%
voc: levelValues.get('vact'),
co2: numeric(co2r), // ppm
nox: levelValues.get('noxl'),
hcho: numeric(hcho), // µg/m³
pm25: numeric(pm25), // µg/m³
pm10: numeric(pm10) // µg/m³
};
}
//# sourceMappingURL=dyson-device-air-quality.js.map