homebridge-miot
Version:
Homebridge plugin for devices supporting the miot protocol
864 lines (721 loc) • 36.8 kB
JavaScript
let Service, Characteristic, Accessory, HapStatusError, HAPStatus;
const AbstractAccessory = require('./AbstractAccessory.js');
const Constants = require('../constants/Constants.js');
const DevTypes = require('../constants/DevTypes.js');
class BaseAccessory extends AbstractAccessory {
constructor(name, device, uuid, config, api, logger) {
if (new.target === BaseAccessory) {
throw new Error("Cannot instantiate BaseAccessory directly!")
}
Service = api.hap.Service;
Characteristic = api.hap.Characteristic;
Accessory = api.platformAccessory;
HapStatusError = api.hap.HapStatusError;
HAPStatus = api.hap.HAPStatus;
super(name, device, uuid, config, api, logger);
}
/*----------========== INIT ==========----------*/
initAccessoryObject() {
this.buzzerControl = this.getConfigValue('buzzerControl', true);
this.ledControl = this.getConfigValue('ledControl', true);
this.childLockControl = this.getConfigValue('childLockControl', true);
this.modeControl = this.getConfigValue('modeControl', true);
this.suppressAutoServiceCreation = this.getConfigValue('suppressAutoServiceCreation', []);
this.actionButtons = this.getConfigValue('actionButtons', false);
this.methodButtons = this.getConfigValue('methodButtons', []);
this.propertyControl = this.getConfigValue('propertyControl', []);
this.propertyMonitor = this.getConfigValue('propertyMonitor', []);
super.initAccessoryObject();
}
/*----------========== ACCESSORY INFO ==========----------*/
/*----------========== INIT ACCESSORIES ==========----------*/
/*----------========== SETUP SERVICES ==========----------*/
setupAdditionalAccessoryServices() {
if (this.buzzerControl) this.prepareBuzzerControlService();
if (this.ledControl) this.prepareLedControlService();
if (this.modeControl) this.prepareModeControlServices();
if (!this.isServiceSupressed("temperature")) this.prepareTemperatureService();
if (!this.isServiceSupressed("relativeHumidity")) this.prepareRelativeHumidityService();
if (!this.isServiceSupressed("illumination")) this.prepareIlluminationService();
this.prepareBatteryService();
this.prepareFilterMaintenanceService();
super.setupAdditionalAccessoryServices();
}
setupCustomizationServices() {
if (this.actionButtons) this.prepareActionButtonServices(this.actionButtons);
if (this.methodButtons) this.prepareMethodButtonServices(this.methodButtons);
if (this.propertyControl) this.preparePropertyControlervices(this.propertyControl);
if (this.propertyMonitor) this.preparePropertyMonitorServices(this.propertyMonitor);
}
/*----------========== CREATE ADDITIONAL SERVICES ==========----------*/
prepareBuzzerControlService() {
if (this.getDevice().supportsBuzzerControl()) {
this.addPropWrapper('Buzzer', this.getDevice().alarmProp(), null, null, null, null);
}
}
prepareLedControlService() {
if (this.getDevice().supportsIndicatorLightOnControl()) {
this.addPropWrapper('Led', this.getDevice().indicatorLightOnProp(), null, null, null, null);
} else if (this.getDevice().supportsIndicatorLightBrightnessControl()) {
this.addPropWrapper('Led', this.getDevice().indicatorLightBrightnessProp(), null, null, null, null);
} else if (this.getDevice().supportsIndicatorLightIndicatorLightControl()) {
this.addPropWrapper('Led', this.getDevice().indicatorLightIndicatorLightProp(), null, null, null, null);
}
}
prepareScreenControlService() {
if (this.getDevice().supportsScreenOnControl()) {
this.addPropWrapper('Screen', this.getDevice().screenOnProp(), null, null, null, null);
} else if (this.getDevice().supportsScreenBrightnessControl()) {
this.addPropWrapper('Screen', this.getDevice().screenBrightnessProp(), null, null, null, null);
}
}
prepareOffDelayService() {
if (this.getDevice().supportsOffDelay()) {
this.addOffDelayWrapper('Off delay', this.getDevice().offDelayProp(), this.getDevice().onProp());
}
}
prepareModeControlServices() {
if (this.getDevice().supportsModes()) {
this.addPropWrapper('Mode', this.getDevice().modeProp(), this.getDevice().onProp(), null, null, null, null);
}
}
prepareFanLevelControlServices() {
if (this.getDevice().supportsFanLevel()) {
this.addPropWrapper('Fan Level', this.getDevice().fanLevelProp(), this.getDevice().onProp(), null, null, null);
}
}
prepareIoniserControlService() {
if (this.getDevice().supportsIoniser()) {
this.addPropWrapper('Ioniser', this.getDevice().anionProp(), this.getDevice().onProp(), null, null);
}
}
prepareTemperatureService(customName) {
if (this.getDevice().supportsTemperatureReporting() && !this.temperatureService) {
const serviceName = customName || 'Temperature';
this.temperatureService = new Service.TemperatureSensor(serviceName, 'temperatureService');
this.temperatureService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.temperatureService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
this.temperatureService
.setCharacteristic(Characteristic.StatusFault, Characteristic.StatusFault.NO_FAULT)
.setCharacteristic(Characteristic.StatusTampered, Characteristic.StatusTampered.NOT_TAMPERED);
this.temperatureService
.getCharacteristic(Characteristic.StatusActive)
.onGet(this.getCurrentTemperatureStatusActive.bind(this));
this.temperatureService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
this.temperatureService
.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(this.getCurrentTemperature.bind(this));
this.addAccessoryService(this.temperatureService);
// add property for monitoring
this.getDevice().monitorTemperatureProp();
}
}
prepareRelativeHumidityService(customName) {
if (this.getDevice().supportsRelativeHumidityReporting() && !this.relativeHumidityService) {
const serviceName = customName || 'Humidity';
this.relativeHumidityService = new Service.HumiditySensor(serviceName, 'relativeHumidityService');
this.relativeHumidityService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.relativeHumidityService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
this.relativeHumidityService
.setCharacteristic(Characteristic.StatusFault, Characteristic.StatusFault.NO_FAULT)
.setCharacteristic(Characteristic.StatusTampered, Characteristic.StatusTampered.NOT_TAMPERED);
this.relativeHumidityService
.getCharacteristic(Characteristic.StatusActive)
.onGet(this.getCurrentRelativeHumidityStatusActive.bind(this));
this.relativeHumidityService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
this.relativeHumidityService
.getCharacteristic(Characteristic.CurrentRelativeHumidity)
.onGet(this.getCurrentRelativeHumidity.bind(this));
this.addAccessoryService(this.relativeHumidityService);
// add property for monitoring
this.getDevice().monitorRelativeHumidityProp();
}
}
prepareIlluminationService(customName) {
if (this.getDevice().supportsIlluminationReporting() && !this.illuminationService) {
const serviceName = customName || 'Illumination';
this.illuminationService = new Service.LightSensor(serviceName, 'illuminationService');
this.illuminationService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.illuminationService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
this.illuminationService
.setCharacteristic(Characteristic.StatusFault, Characteristic.StatusFault.NO_FAULT)
.setCharacteristic(Characteristic.StatusTampered, Characteristic.StatusTampered.NOT_TAMPERED);
this.illuminationService
.getCharacteristic(Characteristic.StatusActive)
.onGet(this.getLightSensorStatusActive.bind(this));
this.illuminationService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
this.illuminationService
.getCharacteristic(Characteristic.CurrentAmbientLightLevel)
.onGet(this.getCurrentAmbientLightLevel.bind(this))
.setProps({
minValue: 0
});
this.addAccessoryService(this.illuminationService);
// add property for monitoring
this.getDevice().monitorIlluminationProp();
}
}
prepareBatteryService(customName) {
if ((this.getDevice().supportsBatteryLevelReporting() || this.getDevice().supportsChargingReporting()) && !this.batteryService) {
const serviceName = customName || 'Battery';
this.batteryService = new Service.BatteryService(serviceName, 'batteryService');
this.batteryService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.batteryService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
this.batteryService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
if (this.getDevice().supportsChargingReporting()) {
this.batteryService
.getCharacteristic(Characteristic.ChargingState)
.onGet(this.getBatteryChargingState.bind(this));
}
if (this.getDevice().supportsBatteryLevelReporting()) {
this.batteryService
.getCharacteristic(Characteristic.BatteryLevel)
.onGet(this.getBatteryLevel.bind(this));
}
this.addAccessoryService(this.batteryService);
}
}
prepareFilterMaintenanceService(customName) {
if (this.getDevice().supportsFilterLifeLevelReporting() && !this.filterMaintenanceService) {
const serviceName = customName || 'Filter Maintenance';
this.filterMaintenanceService = new Service.FilterMaintenance(serviceName, 'filterMaintenanceService');
this.filterMaintenanceService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.filterMaintenanceService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
this.filterMaintenanceService
.getCharacteristic(Characteristic.FilterChangeIndication)
.onGet(this.getFilterChangeIndicationState.bind(this));
this.filterMaintenanceService
.addCharacteristic(Characteristic.FilterLifeLevel)
.onGet(this.getFilterLifeLevel.bind(this));
if (this.getDevice().supportsFilterLifeResetAction()) {
this.filterMaintenanceService
.getCharacteristic(Characteristic.ResetFilterIndication)
.onSet(this.resetFilterIndication.bind(this));
}
this.addAccessoryService(this.filterMaintenanceService);
}
}
prepareAirQualityService(pm25Breakpoints) {
if (this.getDevice().supportsPm25DensityReporting() && !this.airQualityService) {
// setup the breakpoints
this.preparePm25Breakpoints(pm25Breakpoints);
this.airQualityService = new Service.AirQualitySensor('Air Quality', 'airQualityService');
this.airQualityService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.airQualityService.setCharacteristic(Characteristic.ConfiguredName, 'Air Quality');
this.airQualityService
.setCharacteristic(Characteristic.StatusFault, Characteristic.StatusFault.NO_FAULT)
.setCharacteristic(Characteristic.StatusTampered, Characteristic.StatusTampered.NOT_TAMPERED);
this.airQualityService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
this.airQualityService
.getCharacteristic(Characteristic.StatusActive)
.onGet(this.getAirQualityStatusActive.bind(this));
this.airQualityService
.getCharacteristic(Characteristic.AirQuality)
.onGet(this.getAirQuality.bind(this));
this.airQualityService
.getCharacteristic(Characteristic.PM2_5Density)
.onGet(this.getPM25Density.bind(this));
if (this.getDevice().supportsVOCDenstiy()) {
let vocDensityRange = this.getDevice().getVOCDenstiyRange();
this.airQualityService
.getCharacteristic(Characteristic.VOCDensity)
.onGet(this.getVOCDensity.bind(this))
.setProps({
minValue: vocDensityRange[0],
maxValue: vocDensityRange[1],
minStep: vocDensityRange[2]
});
}
if (this.getDevice().supportsPm10DensityReporting()) {
this.airQualityService
.getCharacteristic(Characteristic.PM10Density)
.onGet(this.getPM10DensityChar.bind(this));
}
this.addAccessoryService(this.airQualityService);
}
}
prepareCarbonDioxideService(co2AbnormalThreshold) {
if (this.getDevice().supportsCo2DensityReporting() && !this.carbonDioxideService) {
// setup the threshold
this.prepareCo2AbnormalThreshold(co2AbnormalThreshold);
this.carbonDioxideService = new Service.CarbonDioxideSensor('Carbon Dioxide', 'carbonDioxideService');
this.carbonDioxideService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.carbonDioxideService.setCharacteristic(Characteristic.ConfiguredName, 'Carbon Dioxide');
this.carbonDioxideService
.setCharacteristic(Characteristic.StatusFault, Characteristic.StatusFault.NO_FAULT)
.setCharacteristic(Characteristic.StatusTampered, Characteristic.StatusTampered.NOT_TAMPERED);
this.carbonDioxideService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getStatusLowBatteryState.bind(this));
this.carbonDioxideService
.getCharacteristic(Characteristic.StatusActive)
.onGet(this.getCarbonDioxideStatusActive.bind(this));
this.carbonDioxideService
.getCharacteristic(Characteristic.CarbonDioxideDetected)
.onGet(this.getCarbonDioxideDetected.bind(this));
this.carbonDioxideService
.getCharacteristic(Characteristic.CarbonDioxideLevel)
.onGet(this.getCarbonDioxideLevel.bind(this));
this.addAccessoryService(this.carbonDioxideService);
}
}
// ----- characteristics
addLockPhysicalControlsCharacteristic(service) {
if (this.childLockControl && this.getDevice().supportsChildLock() && !this.childLockCharacteristic && service) {
this.childLockCharacteristic = service.addCharacteristic(Characteristic.LockPhysicalControls);
this.childLockCharacteristic
.onGet(this.getLockPhysicalControlsState.bind(this))
.onSet(this.setLockPhysicalControlsState.bind(this));
}
}
addRotationSpeedCharacteristic(service) {
if (this.getDevice().supportsRotationSpeed() && !this.rotationSpeedCharacteristic && service) {
this.rotationSpeedCharacteristic = service.addCharacteristic(Characteristic.RotationSpeed);
this.rotationSpeedCharacteristic
.onGet(this.getRotationSpeed.bind(this))
.onSet(this.setRotationSpeed.bind(this));
}
}
addCurrentTemperatureCharacteristic(service) {
if (this.getDevice().supportsTemperatureReporting() && !this.currentTemperatureCharacteristic && service) {
this.currentTemperatureCharacteristic = service.getCharacteristic(Characteristic.CurrentTemperature);
if (!this.currentTemperatureCharacteristic) {
this.currentTemperatureCharacteristic = service.addCharacteristic(Characteristic.CurrentTemperature);
}
this.currentTemperatureCharacteristic
.onGet(this.getCurrentTemperature.bind(this));
}
}
addCurrentRelativeHumidityCharacteristic(service) {
if (this.getDevice().supportsRelativeHumidityReporting() && !this.currentRelativeHumidityCharacteristic && service) {
this.currentRelativeHumidityCharacteristic = service.getCharacteristic(Characteristic.CurrentRelativeHumidity);
if (!this.currentRelativeHumidityCharacteristic) {
this.currentRelativeHumidityCharacteristic = service.addCharacteristic(Characteristic.CurrentRelativeHumidity);
}
this.currentRelativeHumidityCharacteristic
.onGet(this.getCurrentRelativeHumidity.bind(this));
}
}
addFilterLifeLevelCharacteristic(service) {
if (this.getDevice().supportsFilterLifeLevelReporting() && !this.filterChangeIndicationCharacteristic && service) {
this.filterChangeIndicationCharacteristic = service.addCharacteristic(Characteristic.FilterChangeIndication);
this.filterChangeIndicationCharacteristic
.onGet(this.getFilterChangeIndicationState.bind(this));
this.filterLifeLevelCharacteristic = service.addCharacteristic(Characteristic.FilterLifeLevel);
this.filterLifeLevelCharacteristic
.onGet(this.getFilterLifeLevel.bind(this));
}
}
// ----- actions
prepareActionButtonServices(actionButtonsUserConfig) {
if (this.getDevice().hasActions()) {
let actionNames = actionButtonsUserConfig;
// if user specified true then use all actions
if (actionNames && actionNames === true) {
actionNames = this.getDevice().getAllActionNames();
}
// at this point we should have an array of actions, if not then something went wrong
if (!actionNames || !Array.isArray(actionNames)) {
this.getLogger().debug(`=AB= Failed to create actions buttons...`);
return;
}
this.getLogger().debug(`=AB= Creating user specified action buttons!`);
// create the action buttons
actionNames.forEach((item, i) => {
let actionName = item.action || item; // get the action name, if array of objects then get "action" else use the item
let actionDisplayName = item.name || this.getDevice().getActionFriendlyName(actionName); // get name from "name" else get friendly name
let actionId = actionName + 'ActionService' + i; // generate action id
let paramValues = item.params || []; // get params from "params" or leave empty
const action = this.getDevice().getAction(actionName);
//only if action exists
if (action) {
const tmpActionSwitch = this.createStatlessSwitch(actionDisplayName, actionId, (value) => {
this.setActionSwitchOn(value, actionName, paramValues);
});
this.addAccessoryService(tmpActionSwitch);
this.getLogger().deepDebug(`=AB= Successfully created action button for action: ${actionName}!`);
} else {
this.getLogger().warn(`=AB= Action ${actionName} does not exist on this device. Skipping action button!`);
}
});
}
}
// ----- methods
prepareMethodButtonServices(methodButtonsUserConfig) {
if (methodButtonsUserConfig && Array.isArray(methodButtonsUserConfig) && methodButtonsUserConfig.length > 0) {
this.getLogger().debug(`=MB= Creating user specified method buttons!`);
// create the method button items
methodButtonsUserConfig.forEach((userMethod, i) => {
let methodName = userMethod.method || userMethod; // get the method name, if array of objects then get "method" else use the item
let methodDisplayName = userMethod.name || methodName; // get name from "name" else get method
let methodId = methodName + 'MethodService' + i; // generate method id
let paramValues = userMethod.params || []; // get params from "params" or leave empty
if (methodName && methodName.length > 0) {
const tmpMethodSwitch = this.createStatlessSwitch(methodDisplayName, methodId, (value) => {
this.setMethodSwitchOn(value, methodName, paramValues);
});
this.addAccessoryService(tmpMethodSwitch);
this.getLogger().deepDebug(`=MB= Successfully created method button for method: ${methodName}!`);
} else {
this.getLogger().warn(`=MB= Missing method name! Skiping method button!`);
}
});
}
}
// ----- property buttons
preparePropertyControlervices(propertyControlUserConfig) {
if (propertyControlUserConfig && Array.isArray(propertyControlUserConfig) && propertyControlUserConfig.length > 0) {
this.getLogger().debug(`=PC= Creating user specified property wrappers!`);
// create the property buttons
propertyControlUserConfig.forEach((userProp, i) => {
let propName = userProp.property || userProp; // get the property name or id, if array of objects then get "property" else use the item
let propDisplayName = userProp.name || this.getDevice().getPropertyFriendlyName(propName); // get name from "name" else get friendly name
let propValue = userProp.value; // get the prop value to set
let linkedPropName = userProp.linkedProperty; // get the linked prop
let linkedPropValue = userProp.linkedPropertyValue; // get the linked prop value
let config = userProp.config;
//if value specified then add the value to the display name
if (propValue) {
propDisplayName = `${propDisplayName} - ${propValue}`;
}
// get the actual properties
let tmpProp = this.getDevice().getProperty(propName);
let linkedProp = this.getDevice().getProperty(linkedPropName);
// only if property exists
if (tmpProp) {
const tmpWrapper = this.addPropWrapper(propDisplayName, tmpProp, linkedProp, propValue, linkedPropValue, config);
if (tmpWrapper) {
this.getLogger().deepDebug(`=PC= Successfully created property control for property: ${propName}!`);
}
} else {
this.getLogger().warn(`=PC= Property ${propName} does not exist on this device. Skipping property control!`);
}
});
}
}
// ----- property monitor
preparePropertyMonitorServices(propertyMonitorUserConfig) {
if (propertyMonitorUserConfig && Array.isArray(propertyMonitorUserConfig) && propertyMonitorUserConfig.length > 0) {
this.getLogger().debug(`=PM= Creating user specified property monitor wrappers!`);
// create the property monitor items
propertyMonitorUserConfig.forEach((userProp, i) => {
let propName = userProp.property || userProp; // get the property name or id, if array of objects then get "property" else use the item
let propDisplayName = userProp.name || this.getDevice().getPropertyFriendlyName(propName); // get name from "name" else get friendly name
let propValue = userProp.value; // get the prop value to set
let propValueOperator = userProp.valueOperator; // get the prop value operator
let linkedPropName = userProp.linkedProperty; // get the linked prop
let linkedPropValue = userProp.linkedPropertyValue; // get the linked prop value
// get the actual properties
let tmpProp = this.getDevice().getProperty(propName);
let linkedProp = this.getDevice().getProperty(linkedPropName);
// only if property exists
if (tmpProp) {
const tmpWrapper = this.addPropMonitorWrapper(propDisplayName, tmpProp, linkedProp, propValue, linkedPropValue, propValueOperator);
if (tmpWrapper) {
this.getLogger().deepDebug(`=PM= Successfully created property monitor for property: ${propName}!`);
}
} else {
this.getLogger().warn(`=PM= Property ${propName} does not exist on this device. Skipping property monitor!`);
}
});
}
}
/*----------========== HOMEBRIDGE STATE SETTERS/GETTERS ==========----------*/
// temperature
getCurrentTemperatureStatusActive() {
if (this.isMiotDeviceConnected()) {
return true
}
return false;
}
getCurrentTemperature() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getTemperature();
}
return 0;
}
// humidity
getCurrentRelativeHumidityStatusActive() {
if (this.isMiotDeviceConnected()) {
return true
}
return false;
}
getCurrentRelativeHumidity() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getRelativeHumidity();
}
return 0;
}
// illumination
getLightSensorStatusActive() {
if (this.isMiotDeviceConnected()) {
return true
}
return false;
}
getCurrentAmbientLightLevel() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getIllumination();
}
return 0;
}
// battery
getStatusLowBatteryState() {
if (this.getDevice().supportsBatteryLevelReporting()) {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getBatteryLevel() > Constants.BATTERY_LOW_THRESHOLD ? Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL : Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
}
}
return Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
getBatteryChargingState() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().isDeviceCharging() ? Characteristic.ChargingState.CHARGING : Characteristic.ChargingState.NOT_CHARGING;
}
return Characteristic.ChargingState.NOT_CHARGEABLE;
}
getBatteryLevel() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getBatteryLevel();
}
return 0;
}
// filter maintenance
getFilterChangeIndicationState() {
if (this.isMiotDeviceConnected()) {
let lifeLevel = this.getDevice().getFilterLifeLevel();
if (lifeLevel <= Constants.FILTER_CHANGE_INDICATION_THRESHOLD) {
return Characteristic.FilterChangeIndication.CHANGE_FILTER;
}
}
return Characteristic.FilterChangeIndication.FILTER_OK;
}
getFilterLifeLevel() {
if (this.isMiotDeviceConnected()) {
let safeLifeLevel = Math.min(100, this.getDevice().getFilterLifeLevel()); // some device return a value greater than 100
return safeLifeLevel;
}
return 0;
}
resetFilterIndication(value) {
if (this.isMiotDeviceConnected()) {
this.getDevice().resetFilterLife();
} else {
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
// air quality
getAirQualityStatusActive() {
if (this.isMiotDeviceConnected()) {
return true
}
return false;
}
getAirQuality() {
if (this.isMiotDeviceConnected()) {
let pm25Density = this.getDevice().getPm25Density();
if (pm25Density <= this.excellentBreakpoint) {
return Characteristic.AirQuality.EXCELLENT;
} else if (pm25Density > this.excellentBreakpoint && pm25Density <= this.goodBreakpoint) {
return Characteristic.AirQuality.GOOD;
} else if (pm25Density > this.goodBreakpoint && pm25Density <= this.fairBreakpoint) {
return Characteristic.AirQuality.FAIR;
} else if (pm25Density > this.fairBreakpoint && pm25Density <= this.inferiorBreakpoint) {
return Characteristic.AirQuality.INFERIOR;
} else if (pm25Density > this.inferiorBreakpoint) {
return Characteristic.AirQuality.POOR;
}
}
return Characteristic.AirQuality.UNKNOWN;
}
getPM25Density() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getPm25Density();
}
return 0;
}
getVOCDensity() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getVOCDenstiyValue();
}
return 0;
}
getPM10DensityChar() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getPm10Density();
}
return 0;
}
// carbon dioxide
getCarbonDioxideStatusActive() {
if (this.isMiotDeviceConnected()) {
return true
}
return false;
}
getCarbonDioxideDetected() {
if (this.isMiotDeviceConnected()) {
let co2Density = this.getDevice().getCo2Density();
if (co2Density > this.co2AbnormalThreshold) {
return Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL;
} else {
return Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL;
}
}
return Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL;
}
getCarbonDioxideLevel() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getCo2Density();
}
return 0;
}
// ----- characteristics
getLockPhysicalControlsState() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().isChildLockActive() ? Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED : Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED;
}
return Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED;
}
setLockPhysicalControlsState(state) {
if (this.isMiotDeviceConnected()) {
let isChildLockActive = state === Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED;
this.getDevice().setChildLock(isChildLockActive);
} else {
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
getRotationSpeed() {
if (this.isMiotDeviceConnected()) {
return this.getDevice().getRotationSpeedPercentage();
}
return 0;
}
setRotationSpeed(value) {
if (this.isMiotDeviceConnected()) {
// use debounce to limit the number of calls when the user slides the rotation slider
if (this.rotationSpeedTimeout) clearTimeout(this.rotationSpeedTimeout);
this.rotationSpeedTimeout = setTimeout(() => this.getDevice().setRotationSpeedPercentage(value), Constants.SLIDER_DEBOUNCE);
} else {
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
// ----- actions
setActionSwitchOn(state, actionName, paramValues = []) {
if (this.isMiotDeviceConnected()) {
this.getDevice().fireAction(actionName, paramValues);
} else {
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
// ----- methods
setMethodSwitchOn(state, methodName, paramValues = []) {
if (this.isMiotDeviceConnected()) {
this.getDevice().executeMethod(methodName, paramValues).then((result) => {
this.getLogger().info(`=MB= Method ${methodName} with params ${JSON.stringify(paramValues)} successfully executed! Result: ${result}!`);
}).catch((err) => {
this.getLogger().warn(`=MB= ${err.message}!`);
});
} else {
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
}
}
/*----------========== SERVICES STATUS ==========----------*/
updateAccessoryStatus() {
if (this.temperatureService) {
this.temperatureService.getCharacteristic(Characteristic.StatusActive).updateValue(this.getCurrentTemperatureStatusActive());
this.temperatureService.getCharacteristic(Characteristic.CurrentTemperature).updateValue(this.getCurrentTemperature());
this.temperatureService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
}
if (this.relativeHumidityService) {
this.relativeHumidityService.getCharacteristic(Characteristic.StatusActive).updateValue(this.getCurrentRelativeHumidityStatusActive());
this.relativeHumidityService.getCharacteristic(Characteristic.CurrentRelativeHumidity).updateValue(this.getCurrentRelativeHumidity());
this.relativeHumidityService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
}
if (this.illuminationService) {
this.illuminationService.getCharacteristic(Characteristic.StatusActive).updateValue(this.getCurrentRelativeHumidityStatusActive());
this.illuminationService.getCharacteristic(Characteristic.CurrentAmbientLightLevel).updateValue(this.getCurrentAmbientLightLevel());
this.illuminationService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
}
if (this.batteryService) {
this.batteryService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
if (this.getDevice().supportsChargingReporting()) this.batteryService.getCharacteristic(Characteristic.ChargingState).updateValue(this.getBatteryChargingState());
if (this.getDevice().supportsBatteryLevelReporting()) this.batteryService.getCharacteristic(Characteristic.BatteryLevel).updateValue(this.getBatteryLevel());
}
if (this.filterMaintenanceService && this.getDevice().supportsFilterLifeLevelReporting()) {
this.filterMaintenanceService.getCharacteristic(Characteristic.FilterChangeIndication).updateValue(this.getFilterChangeIndicationState());
this.filterMaintenanceService.getCharacteristic(Characteristic.FilterLifeLevel).updateValue(this.getFilterLifeLevel());
}
if (this.airQualityService) {
this.airQualityService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
this.airQualityService.getCharacteristic(Characteristic.StatusActive).updateValue(this.getAirQualityStatusActive());
this.airQualityService.getCharacteristic(Characteristic.AirQuality).updateValue(this.getAirQuality());
this.airQualityService.getCharacteristic(Characteristic.PM2_5Density).updateValue(this.getPM25Density());
if (this.getDevice().supportsVOCDenstiy()) this.airQualityService.getCharacteristic(Characteristic.VOCDensity).updateValue(this.getVOCDensity());
if (this.getDevice().supportsPm10DensityReporting()) this.airQualityService.getCharacteristic(Characteristic.PM10Density).updateValue(this.getPM10DensityChar());
}
if (this.carbonDioxideService) {
this.carbonDioxideService.getCharacteristic(Characteristic.StatusLowBattery).updateValue(this.getStatusLowBatteryState());
this.carbonDioxideService.getCharacteristic(Characteristic.StatusActive).updateValue(this.getCarbonDioxideStatusActive());
this.carbonDioxideService.getCharacteristic(Characteristic.CarbonDioxideDetected).updateValue(this.getCarbonDioxideDetected());
this.carbonDioxideService.getCharacteristic(Characteristic.CarbonDioxideLevel).updateValue(this.getCarbonDioxideLevel());
}
if (this.childLockCharacteristic) this.childLockCharacteristic.updateValue(this.getLockPhysicalControlsState());
if (this.rotationSpeedCharacteristic) this.rotationSpeedCharacteristic.updateValue(this.getRotationSpeed());
if (this.currentTemperatureCharacteristic) this.currentTemperatureCharacteristic.updateValue(this.getCurrentTemperature());
if (this.currentRelativeHumidityCharacteristic) this.currentRelativeHumidityCharacteristic.updateValue(this.getCurrentRelativeHumidity());
if (this.filterChangeIndicationCharacteristic) this.filterChangeIndicationCharacteristic.updateValue(this.getFilterChangeIndicationState());
if (this.filterLifeLevelCharacteristic) this.filterLifeLevelCharacteristic.updateValue(this.getFilterLifeLevel());
super.updateAccessoryStatus();
}
/*----------========== MULTI-SWITCH SERVICE HELPERS ==========----------*/
/*----------========== SWITCH SERVICE HELPERS ==========----------*/
/*----------========== STATELESS SWITCH SERVICES HELPERS ==========----------*/
/*----------========== GETTERS ==========----------*/
/*----------========== PROPERTY HELPERS ==========----------*/
preparePm25Breakpoints(pm25Breakpoints) {
// make sure that the provided array is valid
if (pm25Breakpoints) {
if (!Array.isArray(pm25Breakpoints) || pm25Breakpoints.length != 4 ||
pm25Breakpoints[0] >= pm25Breakpoints[1] || pm25Breakpoints[1] >= pm25Breakpoints[2] || pm25Breakpoints[2] >= pm25Breakpoints[3]) {
this.getLogger().warn(`The value specified for the 'pm25Breakpoints' property is invalid! Reverting to default!`);
pm25Breakpoints = [7, 15, 30, 55];
}
} else {
pm25Breakpoints = [7, 15, 30, 55];
}
this.excellentBreakpoint = parseInt(pm25Breakpoints[0]);
this.goodBreakpoint = parseInt(pm25Breakpoints[1]);
this.fairBreakpoint = parseInt(pm25Breakpoints[2]);
this.inferiorBreakpoint = parseInt(pm25Breakpoints[3]);
}
prepareCo2AbnormalThreshold(co2AbnormalThreshold) {
// make sure that the value is valid
if (!Number.isFinite(co2AbnormalThreshold)) {
this.getLogger().warn(`The value specified for the 'co2AbnormalThreshold' property is invalid! Reverting to default!`);
co2AbnormalThreshold = 1000;
}
this.co2AbnormalThreshold = co2AbnormalThreshold;
}
/*----------========== HELPERS ==========----------*/
isServiceSupressed(serviceName) {
if (this.suppressAutoServiceCreation && this.suppressAutoServiceCreation.length > 0) {
if (this.suppressAutoServiceCreation.includes(serviceName)) {
return true;
}
}
return false;
}
}
module.exports = BaseAccessory;