homebridge-melcloud-control
Version:
Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.
844 lines (802 loc) • 132 kB
JavaScript
import EventEmitter from 'events';
import MelCloudAta from './melcloudata.js';
import RestFul from './restful.js';
import Mqtt from './mqtt.js';
import Functions from './functions.js';
import { TemperatureDisplayUnits, AirConditioner, DeviceType } from './constants.js';
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
class DeviceAta extends EventEmitter {
constructor(api, account, device, presets, schedules, scenes, buttons, defaultTempsFile, melCloudClass, melCloudAccountData, melCloudDeviceData) {
super();
Accessory = api.platformAccessory;
Characteristic = api.hap.Characteristic;
Service = api.hap.Service;
Categories = api.hap.Categories;
AccessoryUUID = api.hap.uuid;
//account config
this.account = account;
this.accountType = account.type;
this.accountName = account.name;
this.accountTypeMelCloud = account.type === 'melcloud';
this.logDeviceInfo = account.log?.deviceInfo || false;
this.logInfo = account.log?.info || false;
this.logWarn = account.log?.warn || false;
this.logDebug = account.log?.debug || false;
this.logError = account.log?.error || false;
//device config
this.device = device;
this.deviceId = device.id;
this.deviceName = device.name;
this.deviceTypeString = DeviceType[device.type];
this.displayType = device.displayType;
this.heatDryFanMode = device.heatDryFanMode || 1; //NONE, HEAT, DRY, FAN
this.coolDryFanMode = device.coolDryFanMode || 1; //NONE, COOL, DRY, FAN
this.autoDryFanMode = device.autoDryFanMode || 1; //NONE, AUTO, DRY, FAN
this.temperatureRoomSensor = device.temperatureRoomSensor || false;
this.temperatureOutdoorSensor = device.temperatureOutdoorSensor || false;
this.inStandbySensor = device.inStandbySensor || false;
this.connectSensor = device.connectSensor || false;
this.errorSensor = device.errorSensor || false;
this.frostProtectionSupport = device.frostProtectionSupport || false;
this.overheatProtectionSupport = device.overheatProtectionSupport || false;
this.holidayModeSupport = device.holidayModeSupport || false;
this.remoteRoomTemperatureSupport = device.remoteRoomTemperatureSupport || false;
this.presets = presets;
this.schedules = schedules;
this.scenes = scenes;
this.buttons = buttons;
//files
this.defaultTempsFile = defaultTempsFile;
//melcloud
this.melCloudClass = melCloudClass;
this.melCloudDeviceData = melCloudDeviceData;
this.melCloudAccountData = melCloudAccountData;
//external integrations
this.restFul = account.restFul ?? {};
this.restFul.port = device.restFulPort;
this.restFulConnected = false;
this.mqtt = account.mqtt ?? {};
this.mqttConnected = false;
const serviceType = [null, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor, null];
const characteristicType = [null, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState, null];
//presets configured
for (const preset of this.presets) {
preset.serviceType = serviceType[preset.displayType];
preset.characteristicType = characteristicType[preset.displayType];
preset.state = false;
preset.previousSettings = {};
}
//schedules configured
for (const schedule of this.schedules) {
schedule.serviceType = serviceType[schedule.displayType];
schedule.characteristicType = characteristicType[schedule.displayType];
schedule.state = false;
}
//scenes configured
for (const scene of this.scenes) {
scene.serviceType = serviceType[scene.displayType];
scene.characteristicType = characteristicType[scene.displayType];
scene.state = false;
}
//buttons configured
for (const button of this.buttons) {
button.serviceType = serviceType[button.displayType];
button.characteristicType = characteristicType[button.displayType];
button.state = false;
button.previousValue = null;
}
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
.on('warn', warn => this.emit('warn', warn))
.on('error', error => this.emit('error', error))
.on('debug', debug => this.emit('debug', debug));
//other variables
this.displayDeviceInfo = true;
this.deviceData = {};
this.accessory = {};
this.remoteRoomTemperature = 20;
}
async setOverExternalIntegration(integration, deviceData, key, value) {
try {
const accountTypeMelCloud = this.accountTypeMelCloud;
let payload = {};
let flag = null;
switch (key) {
case 'Power':
payload.power = value;
break;
case 'OperationMode':
payload.operationMode = value;
flag = AirConditioner.EffectiveFlags.OperationMode
break;
case 'SetTemperature':
payload.setTemperature = value;
flag = AirConditioner.EffectiveFlags.SetTemperature;
break;
case 'DefaultCoolingSetTemperature':
payload.defaultCoolingSetTemperature = value;
flag = AirConditioner.EffectiveFlags.SetTemperature;
break;
case 'DefaultHeatingSetTemperature':
payload.defaultHeatingSetTemperature = value;
flag = AirConditioner.EffectiveFlags.SetTemperature;
break;
case 'FanSpeed':
key = accountTypeMelCloud ? 'fanSpeed' : 'setFanSpeed';
payload[key] = value;
flag = AirConditioner.EffectiveFlags.SetFanSpeed;
break;
case 'VaneHorizontalDirection':
payload.vaneHorizontalDirection = value;
flag = AirConditioner.EffectiveFlags.VaneHorizontalDirection;
break;
case 'VaneVerticalDirection':
payload.vaneVerticalDirection = value;
flag = AirConditioner.EffectiveFlags.VaneVerticalDirection;
break;
case 'ProhibitSetTemperature':
if (!accountTypeMelCloud) return;
payload.prohibitSetTemperature = value;
flag = AirConditioner.EffectiveFlags.Prohibit;
break;
case 'ProhibitOperationMode':
if (!accountTypeMelCloud) return;
payload.prohibitOperationMode = value;
flag = AirConditioner.EffectiveFlags.Prohibit;
break;
case 'ProhibitPower':
if (!accountTypeMelCloud) return;
payload.prohibitPower = value;
flag = AirConditioner.EffectiveFlags.Prohibit;
break;
case 'FrostProtection':
if (accountTypeMelCloud) return;
payload.enabled = value;
flag = 'frostprotection';
break;
case 'OverheatProtection':
if (accountTypeMelCloud) return;
payload.enabled = value;
flag = 'overheatprotection';
break;
case 'Schedules':
if (accountTypeMelCloud) return;
payload.enabled = value;
flag = 'schedule';
break;
case 'HolidayMode':
if (accountTypeMelCloud) return;
payload.enabled = value;
flag = 'holidaymode';
break;
case 'RemoteRoomTemperature':
this.remoteRoomTemperature = value;
return;
default:
this.emit('warn', `${integration}, received key: ${key}, value: ${value}`);
return;
};
return await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, flag);
} catch (error) {
throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`);
};
}
async externalIntegrations() {
//RESTFul server
const restFulEnabled = this.restFul.enable || false;
if (restFulEnabled) {
try {
await new Promise((resolve) => {
const timer = setTimeout(resolve, 5000);
this.restFul1 = new RestFul({
port: this.device.restFul.port,
logWarn: this.logWarn,
logDebug: this.logDebug,
})
.once('connected', (success) => {
clearTimeout(timer);
this.restFulConnected = true;
this.emit('success', success);
resolve();
})
.on('set', async (key, value) => {
try {
await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value);
} catch (error) {
if (this.logWarn) this.emit('warn', `RESTFul set error: ${error}`);
};
})
.on('debug', (debug) => this.emit('debug', debug))
.on('warn', (warn) => this.emit('warn', warn))
.on('error', (error) => this.emit('error', error));
});
} catch (error) {
this.emit('warn', `RESTFul integration start error: ${error}`);
}
}
const mqttEnabled = this.mqtt.enable || false;
if (mqttEnabled) {
try {
await new Promise((resolve) => {
const timer = setTimeout(resolve, 10000);
this.mqtt1 = new Mqtt({
host: this.mqtt.host,
port: this.mqtt.port || 1883,
clientId: this.mqtt.clientId ? `melcloud_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `melcloud_${Math.random().toString(16).slice(3)}`,
prefix: this.mqtt.prefix ? `melcloud/${this.mqtt.prefix}/${this.deviceTypeString}/${this.deviceName}` : `melcloud/${this.deviceTypeString}/${this.deviceName}`,
user: this.mqtt.auth?.user,
passwd: this.mqtt.auth?.passwd,
logWarn: this.logWarn,
logDebug: this.logDebug
})
.once('connected', (success) => {
clearTimeout(timer);
this.mqttConnected = true;
this.emit('success', success);
resolve();
})
.on('subscribed', (success) => {
this.emit('success', success);
})
.on('set', async (key, value) => {
try {
await this.setOverExternalIntegration('MQTT', this.deviceData, key, value);
} catch (error) {
if (this.logWarn) this.emit('warn', `MQTT set, error: ${error}`);
};
})
.on('debug', (debug) => this.emit('debug', debug))
.on('warn', (warn) => this.emit('warn', warn))
.on('error', (error) => this.emit('error', error));
});
} catch (error) {
this.emit('warn', `MQTT integration start error: ${error}`);
}
};
return true;
}
//prepare accessory
async prepareAccessory() {
try {
const accountTypeMelCloud = this.accountTypeMelCloud;
const deviceData = this.deviceData;
const deviceId = this.deviceId;
const deviceTypeString = this.deviceTypeString;
const deviceName = this.deviceName;
const accountName = this.accountName;
const presetsOnServer = this.accessory.presets;
const schedulesOnServer = this.accessory.schedules;
const scenesOnServer = this.accessory.scenes;
const supportsHeat = this.accessory.supportsHeat;
const supportsDry = this.accessory.supportsDry;
const supportsCool = this.accessory.supportsCool;
const supportsAuto = this.accessory.supportsAuto;
const supportsFanSpeed = this.accessory.supportsFanSpeed;
const supportsAutomaticFanSpeed = this.accessory.supportsAutomaticFanSpeed;
const supportsOutdoorTemperature = this.accessory.supportsOutdoorTemperature;
const numberOfFanSpeeds = this.accessory.numberOfFanSpeeds;
const supportsSwingFunction = this.accessory.supportsSwingFunction;
const supportsWideVane = this.accessory.supportsWideVane;
const autoDryFanMode = [this.accessory.operationMode, 8, supportsDry ? 2 : 8, 7][this.autoDryFanMode]; //NONE, AUTO - 8, DRY - 2, FAN - 7
const heatDryFanMode = [this.accessory.operationMode, 1, supportsDry ? 2 : 1, 7][this.heatDryFanMode]; //NONE, HEAT - 1, DRY - 2, FAN - 7
const coolDryFanMode = [this.accessory.operationMode, 3, supportsDry ? 2 : 3, 7][this.coolDryFanMode]; //NONE, COOL - 3, DRY - 2, FAN - 7
//accessory
if (this.logDebug) this.emit('debug', `Prepare accessory`);
const accessoryName = deviceName;
const accessoryUUID = AccessoryUUID.generate(accountName + deviceId.toString());
const accessoryCategory = Categories.AIR_CONDITIONER;
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
//information service
if (this.logDebug) this.emit('debug', `Prepare information service`);
this.informationService = accessory.getService(Service.AccessoryInformation)
.setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(Characteristic.Model, this.model)
.setCharacteristic(Characteristic.SerialNumber, this.serialNumber)
.setCharacteristic(Characteristic.FirmwareRevision, this.firmwareRevision)
.setCharacteristic(Characteristic.ConfiguredName, accessoryName);
//melcloud services
const serviceName = `${deviceTypeString} ${accessoryName}`;
switch (this.displayType) {
case 1: //Heater Cooler
if (this.logDebug) this.emit('debug', `Prepare heater/cooler service`);
const melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId}`);
melCloudService.setPrimaryService(true);
melCloudService.getCharacteristic(Characteristic.Active)
.onGet(async () => {
const state = this.accessory.power;
return state;
})
.onSet(async (state) => {
try {
const payload = { power: state ? true : false };
if (this.logInfo) this.emit('info', `Set power: ${state ? 'On' : 'Off'}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set power error: ${error}`);
};
});
melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.onGet(async () => {
const value = this.accessory.currentOperationMode;
return value;
});
melCloudService.getCharacteristic(Characteristic.TargetHeaterCoolerState)
.setProps({
minValue: this.accessory.operationModeSetPropsMinValue,
maxValue: this.accessory.operationModeSetPropsMaxValue,
validValues: this.accessory.operationModeSetPropsValidValues
})
.onGet(async () => {
const value = this.accessory.targetOperationMode; //1 = HEAT, 2 = DRY 3 = COOL, 7 = FAN, 8 = AUTO
return value;
})
.onSet(async (value) => {
try {
switch (value) {
case 0: //AUTO - AUTO
value = autoDryFanMode;
break;
case 1: //HEAT - HEAT
value = heatDryFanMode;
break;
case 2: //COOL - COOL
value = coolDryFanMode;
break;
};
const payload = { operationMode: value };
if (this.logInfo) this.emit('info', `Set operation mode: ${AirConditioner.OperationModeMapEnumToString[value]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.OperationMode);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set operation mode error: ${error}`);
};
});
melCloudService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const value = this.accessory.roomTemperature;
return value;
});
if (supportsFanSpeed) {
melCloudService.getCharacteristic(Characteristic.RotationSpeed)
.setProps({
minValue: this.accessory.fanSpeedSetPropsMinValue,
maxValue: this.accessory.fanSpeedSetPropsMaxValue,
minStep: 1
})
.onGet(async () => {
const value = this.accessory.currentFanSpeed; //AUTO, 1, 2, 3, 4, 5, 6, OFF
return value;
})
.onSet(async (value) => {
try {
const payload = {};
const fanKeySet = accountTypeMelCloud ? 'fanSpeed' : 'setFanSpeed';
const max = numberOfFanSpeeds;
const minValue = supportsAutomaticFanSpeed ? 0 : 1;
const clampedValue = Math.min(Math.max(value, minValue), max);
payload[fanKeySet] = clampedValue;
if (this.logInfo) this.emit('info', `Set fan speed mode: ${AirConditioner.FanSpeedMapEnumToString[clampedValue]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.SetFanSpeed);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set fan speed mode error: ${error}`);
};
});
};
if (supportsSwingFunction) {
melCloudService.getCharacteristic(Characteristic.SwingMode)
.onGet(async () => {
//Vane Horizontal: Auto, 1, 2, 3, 4, 5, 6, 7 = Split, 12 = Swing //Vertical: Auto, 1, 2, 3, 4, 5, 7 = Swing
//Home Vane Horizontal: Auto, 1, 2, 3, 4, 5, 6, 7 = Swing, 8 = Split //Vertical: Auto, 1, 2, 3, 4, 5, 6 = Swing
const value = this.accessory.currentSwingMode;
return value;
})
.onSet(async (value) => {
try {
const payload = {};
if (supportsWideVane) payload.vaneHorizontalDirection = value ? 12 : 0;
payload.vaneVerticalDirection = value ? 7 : 0;
if (this.logInfo) this.emit('info', `Set air direction mode: ${AirConditioner.AirDirectionMapEnumToString[value]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.VaneVerticalVaneHorizontal);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set air direction mode error: ${error}`);
};
});
};
melCloudService.getCharacteristic(Characteristic.CoolingThresholdTemperature) // 16 - 31
.setProps({
minValue: this.accessory.minSetCoolDryAutoRoomTemperature,
maxValue: this.accessory.maxSetHeatCoolDryAutoRoomTemperature,
minStep: this.accessory.temperatureStep
})
.onGet(async () => {
const value = this.accessory.operationMode === 8 ? this.accessory.defaultCoolingSetTemperature : this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
if (this.accessory.operationMode === 8) deviceData.Device.DefaultCoolingSetTemperature = value < 16 ? 16 : value;
const payload = { setTemperature: value < 16 ? 16 : value };
if (this.logInfo) this.emit('info', `Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.SetTemperature);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set cooling threshold temperature error: ${error}`);
};
});
if (supportsHeat) {
melCloudService.getCharacteristic(Characteristic.HeatingThresholdTemperature) // 10 - 31
.setProps({
minValue: this.accessory.minSetHeatRoomTemperature,
maxValue: this.accessory.maxSetHeatCoolDryAutoRoomTemperature,
minStep: this.accessory.temperatureStep
})
.onGet(async () => {
const value = this.accessory.operationMode === 8 ? this.accessory.defaultHeatingSetTemperature : this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
if (this.accessory.operationMode === 8) deviceData.Device.DefaultHeatingSetTemperature = value;
const payload = { setTemperature: value };
if (this.logInfo) this.emit('info', `Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.SetTemperature);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set heating threshold temperature error: ${error}`);
};
});
};
melCloudService.getCharacteristic(Characteristic.LockPhysicalControls)
.onGet(async () => {
const value = this.accessory.lockPhysicalControl;
return value;
})
.onSet(async (value) => {
if (!accountTypeMelCloud) return;
try {
value = value ? true : false;
const payload = {};
payload.prohibitSetTemperature = value;
payload.prohibitOperationMode = value;
payload.prohibitPower = value;
if (this.logInfo) this.emit('info', `Set local physical controls: ${value ? 'Lock' : 'Unlock'}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.Prohibit);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set lock physical controls error: ${error}`);
};
});
melCloudService.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.onGet(async () => {
const value = this.accessory.useFahrenheit;
return value;
})
.onSet(async (value) => {
if (!accountTypeMelCloud) return;
try {
this.accessory.useFahrenheit = value ? true : false;
this.melCloudAccountData.UseFahrenheit = value ? true : false;
this.melCloudAccountData.Account.LoginData.UseFahrenheit = value ? true : false;
const payload = this.melCloudAccountData;
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'account');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
};
});
this.melCloudService = melCloudService;
break;
case 2: //Thermostat
if (this.logDebug) this.emit('debug', `Prepare thermostat service`);
const melCloudServiceT = new Service.Thermostat(serviceName, `Thermostat ${deviceId}`);
melCloudServiceT.setPrimaryService(true);
melCloudServiceT.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.onGet(async () => {
const value = this.accessory.currentOperationMode;
return value;
});
melCloudServiceT.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.setProps({
minValue: this.accessory.operationModeSetPropsMinValue,
maxValue: this.accessory.operationModeSetPropsMaxValue,
validValues: this.accessory.operationModeSetPropsValidValues
})
.onGet(async () => {
const value = this.accessory.targetOperationMode; //1 = HEAT, 2 = DRY 3 = COOL, 7 = FAN, 8 = AUTO
return value;
})
.onSet(async (value) => {
try {
let flag = null;
switch (value) {
case 0: //OFF - POWER OFF
value = deviceData.Device.OperationMode;
break;
case 1: //HEAT - HEAT
value = heatDryFanMode;
flag = AirConditioner.EffectiveFlags.OperationModeSetTemperature;
break;
case 2: //COOL - COOL
value = coolDryFanMode;
flag = AirConditioner.EffectiveFlags.OperationModeSetTemperature
break;
case 3: //AUTO - AUTO
value = autoDryFanMode;
flag = AirConditioner.EffectiveFlags.OperationModeSetTemperature;
break;
};
const payload = { operationMode: value };
if (this.logInfo) this.emit('info', `Set operation mode: ${AirConditioner.OperationModeMapEnumToString[value]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, flag);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set operation mode error: ${error}`);
};
});
melCloudServiceT.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const value = this.accessory.roomTemperature;
return value;
});
melCloudServiceT.getCharacteristic(Characteristic.TargetTemperature)
.setProps({
minValue: this.accessory.minSetCoolDryAutoRoomTemperature,
maxValue: this.accessory.maxSetHeatCoolDryAutoRoomTemperature,
minStep: this.accessory.temperatureStep
})
.onGet(async () => {
const value = this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
if (deviceData.Device.OperationMode === 1 && value < this.accessory.minSetHeatRoomTemperature) {
value = this.accessory.minSetHeatRoomTemperature;
} else if (value < 16) {
value = 16;
}
const payload = { setTemperature: value };
if (this.logInfo) this.emit('info', `Set temperature: ${value}${this.accessory.temperatureUnit}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, AirConditioner.EffectiveFlags.SetTemperature);
} catch (error) {
if (this.logWarn) this.emit('warn', `Set temperature error: ${error}`);
};
});
melCloudServiceT.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.onGet(async () => {
const value = this.accessory.useFahrenheit;
return value;
})
.onSet(async (value) => {
if (!accountTypeMelCloud) return;
try {
this.accessory.useFahrenheit = value ? true : false;
this.melCloudAccountData.UseFahrenheit = value ? true : false;
this.melCloudAccountData.Account.LoginData.UseFahrenheit = value ? true : false;
const payload = this.melCloudAccountData;
if (this.logInfo) this.emit('info', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'account');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set temperature display unit error: ${error}`);
};
});
this.melCloudService = melCloudServiceT;
break;
default:
if (this.logWarn) this.emit('warn', `Received unknown display type: ${this.displayType}`);
return;
};
//add service to accessory
accessory.addService(this.melCloudService);
//temperature sensor services
if (this.temperatureRoomSensor && this.accessory.roomTemperature !== null) {
if (this.logDebug) this.emit('debug', `Prepare room temperature sensor service`);
this.roomTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Room`, `roomTemperatureSensorService${deviceId}`);
this.roomTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.roomTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Room`);
this.roomTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const state = this.accessory.roomTemperature;
return state;
})
accessory.addService(this.roomTemperatureSensorService);
}
if (this.temperatureOutdoorSensor && supportsOutdoorTemperature) {
if (this.logDebug) this.emit('debug', `Prepare outdoor temperature sensor service`);
this.outdoorTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Outdoor`, `outdoorTemperatureSensorService${deviceId}`);
this.outdoorTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.outdoorTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Outdoor`);
this.outdoorTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const state = this.accessory.outdoorTemperature;
return state;
})
accessory.addService(this.outdoorTemperatureSensorService);
}
//in standby sensor
if (this.inStandbySensor && this.accessory.inStandbyMode !== null) {
if (this.logDebug) this.emit('debug', `Prepare in standby mode service`);
this.inStandbyService = new Service.ContactSensor(`${serviceName} In Standby`, `inStandbyService${deviceId}`);
this.inStandbyService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.inStandbyService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} In Standby`);
this.inStandbyService.getCharacteristic(Characteristic.ContactSensorState)
.onGet(async () => {
const state = this.accessory.inStandbyMode;
return state;
})
accessory.addService(this.inStandbyService);
}
//connect sensor
if (this.connectSensor && this.accessory.isConnected !== null) {
if (this.logDebug) this.emit('debug', `Prepare connect service`);
this.connectService = new Service.ContactSensor(`${serviceName} Connected`, `connectService${deviceId}`);
this.connectService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.connectService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Connected`);
this.connectService.getCharacteristic(Characteristic.ContactSensorState)
.onGet(async () => {
const state = this.accessory.isConnected;
return state;
})
accessory.addService(this.connectService);
}
//error sensor
if (this.errorSensor && this.accessory.isInError !== null) {
if (this.logDebug) this.emit('debug', `Prepare error service`);
this.errorService = new Service.ContactSensor(`${serviceName} Error`, `errorService${deviceId}`);
this.errorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.errorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Error`);
this.errorService.getCharacteristic(Characteristic.ContactSensorState)
.onGet(async () => {
const state = this.accessory.isInError;
return state;
})
accessory.addService(this.errorService);
}
//frost protection
if (this.frostProtectionSupport && this.accessory.frostProtectionEnabled !== null) {
//control
if (this.logDebug) this.emit('debug', `Prepare frost protection control service`);
const frostProtectionControlService = new Service.HeaterCooler(`${serviceName} Frost Protection`, `frostProtectionControlService${deviceId}`);
frostProtectionControlService.addOptionalCharacteristic(Characteristic.ConfiguredName);
frostProtectionControlService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
frostProtectionControlService.getCharacteristic(Characteristic.Active)
.onGet(async () => {
const state = this.accessory.frostProtectionEnabled;
return state;
})
.onSet(async (state) => {
try {
const payload = { enabled: state ? true : false, min: deviceData.FrostProtection.Min, max: deviceData.FrostProtection.Max };
if (this.logInfo) this.emit('info', `Frost protection: ${state ? 'Enabled' : 'Disabled'}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'frostprotection');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
};
});
frostProtectionControlService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.onGet(async () => {
const value = this.accessory.frostProtection.Active ? 2 : 1;
return value;
})
frostProtectionControlService.getCharacteristic(Characteristic.TargetHeaterCoolerState)
.setProps({
minValue: 0,
maxValue: 0,
validValues: [0]
})
.onGet(async () => {
const value = 0
return value;
})
.onSet(async (state) => {
try {
const payload = { enabled: true, min: deviceData.FrostProtection.Min, max: deviceData.FrostProtection.Max };
if (this.logInfo) this.emit('info', `Frost protection: Enabled`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'frostprotection');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
};
});
frostProtectionControlService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const value = this.accessory.roomTemperature;
return value;
});
frostProtectionControlService.getCharacteristic(Characteristic.CoolingThresholdTemperature) //max
.setProps({
minValue: 6,
maxValue: 16,
minStep: 1
})
.onGet(async () => {
const value = this.accessory.frostProtection.Max;
return value;
})
.onSet(async (value) => {
try {
let { min, max } = await this.functions.adjustTempProtection(deviceData.FrostProtection.Min, deviceData.FrostProtection.Max, value, 'max', 4, 14, 6, 16);
const payload = { enabled: deviceData.FrostProtection.Enabled, min: min, max: max };
if (this.logInfo) this.emit('info', `Set frost protection max. temperature: ${max}${this.accessory.temperatureUnit}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'frostprotection');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set frost protection max. temperature error: ${error}`);
};
});
frostProtectionControlService.getCharacteristic(Characteristic.HeatingThresholdTemperature) //min
.setProps({
minValue: 4,
maxValue: 14,
minStep: 1
})
.onGet(async () => {
const value = this.accessory.frostProtection.Min;
return value;
})
.onSet(async (value) => {
try {
let { min, max } = await this.functions.adjustTempProtection(deviceData.FrostProtection.Min, deviceData.FrostProtection.Max, value, 'min', 4, 14, 6, 16);
const payload = { enabled: deviceData.FrostProtection.Enabled, min: min, max: max };
if (this.logInfo) this.emit('info', `Set frost protection min. temperature: ${min}${this.accessory.temperatureUnit}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'frostprotection');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set frost protection min. temperature error: ${error}`);
};
});
this.frostProtectionControlService = frostProtectionControlService;
accessory.addService(frostProtectionControlService);
if (this.logDebug) this.emit('debug', `Prepare frost protection control sensor service`);
this.frostProtectionControlSensorService = new Service.ContactSensor(`${serviceName} Frost Protection Control`, `frostProtectionControlSensorService${deviceId}`);
this.frostProtectionControlSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.frostProtectionControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection Control`);
this.frostProtectionControlSensorService.getCharacteristic(Characteristic.ContactSensorState)
.onGet(async () => {
const state = this.accessory.frostProtectionEnabled;
return state;
})
accessory.addService(this.frostProtectionControlSensorService);
//sensor
if (this.logDebug) this.emit('debug', `Prepare frost protection service`);
this.frostProtectionSensorService = new Service.ContactSensor(`${serviceName} Frost Protection`, `frostProtectionSensorService${deviceId}`);
this.frostProtectionSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.frostProtectionSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
this.frostProtectionSensorService.getCharacteristic(Characteristic.ContactSensorState)
.onGet(async () => {
const state = this.accessory.frostProtection.Active;
return state;
})
accessory.addService(this.frostProtectionSensorService);
}
//overheat protection
if (this.overheatProtectionSupport && this.accessory.overheatProtectionEnabled !== null) {
//control
if (this.logDebug) this.emit('debug', `Prepare overheat protection control service`);
const overheatProtectionControlService = new Service.HeaterCooler(`${serviceName} Overheat Protection`, `overheatProtectionControlService${deviceId}`);
overheatProtectionControlService.addOptionalCharacteristic(Characteristic.ConfiguredName);
overheatProtectionControlService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Overheat Protection`);
overheatProtectionControlService.getCharacteristic(Characteristic.Active)
.onGet(async () => {
const state = this.accessory.overheatProtectionEnabled;
return state;
})
.onSet(async (state) => {
try {
const payload = { enabled: state ? true : false, min: deviceData.OverheatProtection.Min, max: deviceData.OverheatProtection.Max };
if (this.logInfo) this.emit('info', `Overheat protection: ${state ? 'Enabled' : 'Disabled'}`);
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, payload, 'overheatprotection');
} catch (error) {
if (this.logWarn) this.emit('warn', `Set overheat protection error: ${error}`);
};
});
overheatProtectionControlService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.onGet(async () => {
const value = this.accessory.overheatProtection.Active ? 2 : 1;
return value;
})
overheatProtectionControlService.getCharacteristic(Characteristic.TargetHeaterCoolerState)
.setProps({
minValue: 0,
maxValue: 0,
validValues: [0]
})
.onGet(async () => {
const value = 0
return value;
})
.onSet(async (value) => {
try {
const payload = { enabled: true, min: deviceData.OverheatProtection.Min, max: deviceData.OverheatProtection.Max };
if (this.logInfo) this.emit('info', `Set overheat protection: Enabled`);