homebridge-melcloud-control
Version:
Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.
787 lines (751 loc) • 96.7 kB
JavaScript
import EventEmitter from 'events';
import MelCloudAta from './melcloudata.js';
import RestFul from './restful.js';
import Mqtt from './mqtt.js';
import { TemperatureDisplayUnits, AirConditioner } from './constants.js';
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
class DeviceAta extends EventEmitter {
constructor(api, account, device, melCloud, accountInfo, contextKey, accountName, deviceId, deviceName, deviceTypeText, devicesFile, refreshInterval, useFahrenheit, restFul, mqtt) {
super();
Accessory = api.platformAccessory;
Characteristic = api.hap.Characteristic;
Service = api.hap.Service;
Categories = api.hap.Categories;
AccessoryUUID = api.hap.uuid;
//account config
this.displayMode = device.displayMode;
this.temperatureSensor = device.temperatureSensor || false;
this.temperatureSensorOutdoor = device.temperatureSensorOutdoor || false;
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.presets = device.presets || [];
this.buttons = device.buttonsSensors || [];
this.disableLogInfo = account.disableLogInfo || false;
this.disableLogDeviceInfo = account.disableLogDeviceInfo || false;
this.enableDebugMode = account.enableDebugMode || false;
this.accountInfo = accountInfo;
this.contextKey = contextKey;
this.accountName = accountName;
this.deviceId = deviceId;
this.deviceName = deviceName;
this.deviceTypeText = deviceTypeText;
this.devicesFile = devicesFile;
this.refreshInterval = refreshInterval;
this.startPrepareAccessory = true;
this.displayDeviceInfo = true;
//external integrations
this.restFul = restFul;
this.restFulConnected = false;
this.mqtt = mqtt;
this.mqttConnected = false;
//function
this.melCloud = melCloud; //function
//presets configured
this.presetsConfigured = [];
for (const preset of this.presets) {
const presetName = preset.name ?? false;
const presetDisplayType = preset.displayType ?? 0;
const presetNamePrefix = preset.namePrefix ?? false;
if (presetName && presetDisplayType > 0) {
const presetyServiceType = ['', Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][presetDisplayType];
const presetCharacteristicType = ['', Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][presetDisplayType];
preset.namePrefix = presetNamePrefix;
preset.serviceType = presetyServiceType;
preset.characteristicType = presetCharacteristicType;
preset.state = false;
preset.previousSettings = {};
this.presetsConfigured.push(preset);
} else {
const log = presetDisplayType === 0 ? false : this.emit('warn', `Preset Name: ${preset ? preset : 'Missing'}`);
};
}
this.presetsConfiguredCount = this.presetsConfigured.length || 0;
//buttons configured
this.buttonsConfigured = [];
for (const button of this.buttons) {
const buttonName = button.name ?? false;
const buttonMode = button.mode ?? -1;
const buttonDisplayType = button.displayType ?? 0;
const buttonNamePrefix = button.namePrefix ?? false;
if (buttonName && buttonMode >= 0 && buttonDisplayType > 0) {
const buttonServiceType = ['', Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][buttonDisplayType];
const buttonCharacteristicType = ['', Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][buttonDisplayType];
button.namePrefix = buttonNamePrefix;
button.serviceType = buttonServiceType;
button.characteristicType = buttonCharacteristicType;
button.state = false;
button.previousValue = null;
this.buttonsConfigured.push(button);
} else {
const log = buttonDisplayType === 0 ? false : this.emit('warn', `Button Name: ${buttonName ? buttonName : 'Missing'}, Mode: ${buttonMode ? buttonMode : 'Missing'}`);
};
}
this.buttonsConfiguredCount = this.buttonsConfigured.length || 0;
//device data
this.deviceData = {};
//accessory
this.accessory = {};
this.accessory.useFahrenheit = useFahrenheit ? 1 : 0;
this.accessory.temperatureUnit = TemperatureDisplayUnits[this.accessory.useFahrenheit];
};
async externalIntegrations() {
try {
//RESTFul server
const restFulEnabled = this.restFul.enable || false;
if (restFulEnabled) {
if (!this.restFulConnected) {
this.restFul1 = new RestFul({
port: this.deviceId.toString().slice(-4).replace(/^0/, '9'),
debug: this.restFul.debug || false
});
this.restFul1.on('connected', (message) => {
this.restFulConnected = true;
this.emit('success', message);
})
.on('set', async (key, value) => {
try {
await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value);
} catch (error) {
this.emit('warn', error);
};
})
.on('debug', (debug) => {
this.emit('debug', debug);
})
.on('error', (error) => {
this.emit('warn', error);
});
}
}
//MQTT client
const mqttEnabled = this.mqtt.enable || false;
if (mqttEnabled) {
if (!this.mqttConnected) {
this.mqtt1 = new Mqtt({
host: this.mqtt.host,
port: this.mqtt.port || 1883,
clientId: `${this.mqtt.clientId}_${this.deviceId}` || `${this.deviceTypeText}_${this.deviceName}_${this.deviceId}`,
prefix: `${this.mqtt.prefix}/${this.deviceTypeText}/${this.deviceName}`,
user: this.mqtt.user,
passwd: this.mqtt.pass,
debug: this.mqtt.debug || false
});
this.mqtt1.on('connected', (message) => {
this.mqttConnected = true;
this.emit('success', message);
})
.on('subscribed', (message) => {
this.emit('success', message);
})
.on('set', async (key, value) => {
try {
await this.setOverExternalIntegration('MQTT', this.deviceData, key, value);
} catch (error) {
this.emit('warn', error);
};
})
.on('debug', (debug) => {
this.emit('debug', debug);
})
.on('error', (error) => {
this.emit('warn', error);
});
}
}
} catch (error) {
this.emit('warn', `External integration start error: ${error}`);
};
}
async setOverExternalIntegration(integration, deviceData, key, value) {
try {
let set = false
switch (key) {
case 'Power':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'OperationMode':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.OperationModeSetTemperature;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'SetTemperature':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'DefaultCoolingSetTemperature':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'DefaultHeatingSetTemperature':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'FanSpeed':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetFanSpeed;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'VaneHorizontalDirection':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneHorizontal;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'VaneVerticalDirection':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneVertical;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'HideVaneControls':
deviceData[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'HideDryModeControl':
deviceData[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'ProhibitSetTemperature':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'ProhibitOperationMode':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
await this.melCloudAta.send(deviceData, this.displayMode);
break;
case 'ProhibitPower':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
set = await this.melCloudAta.send(deviceData, this.displayMode);
break;
default:
this.emit('warn', `${integration}, received key: ${key}, value: ${value}`);
break;
};
return set;
} catch (error) {
throw new Error(`${integration} set key: ${key}, value: ${value}, error: ${error.message ?? error}`);
};
}
async startImpulseGenerator() {
try {
//start impule generator
await this.melCloudAta.impulseGenerator.start([{ name: 'checkState', sampling: this.refreshInterval }]);
return true;
} catch (error) {
throw new Error(`Impulse generator start error: ${error}`);
};
}
//prepare accessory
async prepareAccessory(accountInfo, deviceData, deviceId, deviceTypeText, deviceName, accountName) {
try {
const presetsOnServer = this.accessory.presets;
const modelSupportsHeat = this.accessory.modelSupportsHeat;
const modelSupportsDry = this.accessory.modelSupportsDry;
const modelSupportsCool = this.accessory.modelSupportsCool;
const modelSupportsAuto = this.accessory.modelSupportsAuto;
const modelSupportsFanSpeed = this.accessory.modelSupportsFanSpeed;
const hasAutomaticFanSpeed = this.accessory.hasAutomaticFanSpeed;
const hasOutdoorTemperature = this.accessory.hasOutdoorTemperature;
const numberOfFanSpeeds = this.accessory.numberOfFanSpeeds;
const swingFunction = this.accessory.swingFunction;
const autoDryFanMode = [this.accessory.operationMode, 8, modelSupportsDry ? 2 : 8, 7][this.autoDryFanMode]; //NONE, AUTO - 8, DRY - 2, FAN - 7
const heatDryFanMode = [this.accessory.operationMode, 1, modelSupportsDry ? 2 : 1, 7][this.heatDryFanMode]; //NONE, HEAT - 1, DRY - 2, FAN - 7
const coolDryFanMode = [this.accessory.operationMode, 3, modelSupportsDry ? 2 : 3, 7][this.coolDryFanMode]; //NONE, COOL - 3, DRY - 2, FAN - 7
//accessory
const debug = this.enableDebugMode ? this.emit('debug', `Prepare accessory`) : false;
const accessoryName = deviceName;
const accessoryUUID = AccessoryUUID.generate(accountName + deviceId.toString());
const accessoryCategory = Categories.AIR_CONDITIONER;
const accessory = new Accessory(accessoryName, accessoryUUID, accessoryCategory);
//information service
const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare information service`) : false;
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 = `${deviceTypeText} ${accessoryName}`;
switch (this.displayMode) {
case 1: //Heater Cooler
const debug = this.enableDebugMode ? this.emit('debug', `Prepare heater/cooler service`) : false;
this.melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId}`);
this.melCloudService.getCharacteristic(Characteristic.Active)
.onGet(async () => {
const state = this.accessory.power;
return state;
})
.onSet(async (state) => {
try {
deviceData.Device.Power = [false, true][state];
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set power: ${state ? 'ON' : 'OFF'}`);
} catch (error) {
this.emit('warn', `Set power error: ${error}`);
};
});
this.melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.onGet(async () => {
const value = this.accessory.currentOperationMode;
return value;
});
this.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
deviceData.Device.OperationMode = autoDryFanMode;
break;
case 1: //HEAT - HEAT
deviceData.Device.OperationMode = heatDryFanMode;
break;
case 2: //COOL - COOL
deviceData.Device.OperationMode = coolDryFanMode;
break;
};
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.OperationModeSetTemperature;
await this.melCloudAta.send(deviceData, this.displayMode);
const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode];
const info = this.disableLogInfo ? false : this.emit('message', `Set operation mode: ${operationModeText}`);
} catch (error) {
this.emit('warn', `Set operation mode error: ${error}`);
};
});
this.melCloudService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const value = this.accessory.roomTemperature;
return value;
});
if (modelSupportsFanSpeed) {
this.melCloudService.getCharacteristic(Characteristic.RotationSpeed)
.setProps({
minValue: 0,
maxValue: this.accessory.fanSpeedSetPropsMaxValue,
minStep: 1
})
.onGet(async () => {
const value = this.accessory.fanSpeed; //AUTO, 1, 2, 3, 4, 5, 6, OFF
return value;
})
.onSet(async (value) => {
try {
let fanSpeedModeText = '';
switch (numberOfFanSpeeds) {
case 2: //Fan speed mode 2
fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 0][value] : [7, 1, 2][value];
deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 0][value] : [1, 1, 2][value];
break;
case 3: //Fan speed mode 3
fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 0][value] : [7, 1, 2, 3][value];
deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 0][value] : [1, 1, 2, 3][value];
break;
case 4: //Fan speed mode 4
fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 0][value] : [7, 1, 2, 3, 4][value];
deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 0][value] : [1, 1, 2, 3, 4][value];
break;
case 5: //Fan speed mode 5
5; fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 0][value] : [7, 1, 2, 3, 4, 5][value];
deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 5, 0][value] : [1, 1, 2, 3, 4, 5][value];
break;
case 6: //Fan speed mode 6
fanSpeedModeText = hasAutomaticFanSpeed ? [7, 1, 2, 3, 4, 5, 6, 0][value] : [7, 1, 2, 3, 4, 5, 6][value];
deviceData.Device.FanSpeed = hasAutomaticFanSpeed ? [0, 1, 2, 3, 4, 5, 6, 0][value] : [1, 1, 2, 3, 4, 5, 6][value];
break;
};
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetFanSpeed;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set fan speed mode: ${AirConditioner.FanSpeed[fanSpeedModeText]}`);
} catch (error) {
this.emit('warn', `Set fan speed mode error: ${error}`);
};
});
};
if (swingFunction) {
this.melCloudService.getCharacteristic(Characteristic.SwingMode)
.onGet(async () => {
//Vane Horizontal: Auto, 1, 2, 3, 4, 5, 6, 12 = Swing //Vertical: Auto, 1, 2, 3, 4, 5, 7 = Swing
const value = this.accessory.swingMode;
return value;
})
.onSet(async (value) => {
try {
deviceData.Device.VaneHorizontal = value ? 12 : 0;
deviceData.Device.VaneVertical = value ? 7 : 0;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.VaneVerticalVaneHorizontal;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set air direction mode: ${AirConditioner.AirDirection[value]}`);
} catch (error) {
this.emit('warn', `Set vane swing mode error: ${error}`);
};
});
};
this.melCloudService.getCharacteristic(Characteristic.CoolingThresholdTemperature)
.setProps({
minValue: this.accessory.minTempCoolDry,
maxValue: this.accessory.maxTempCoolDry,
minStep: this.accessory.temperatureIncrement
})
.onGet(async () => {
const value = this.accessory.operationMode === 8 ? this.accessory.defaultCoolingSetTemperature : this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
deviceData.Device.DefaultCoolingSetTemperature = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`);
} catch (error) {
this.emit('warn', `Set cooling threshold temperature error: ${error}`);
};
});
if (modelSupportsHeat) {
this.melCloudService.getCharacteristic(Characteristic.HeatingThresholdTemperature)
.setProps({
minValue: this.accessory.minTempHeat,
maxValue: this.accessory.maxTempHeat,
minStep: this.accessory.temperatureIncrement
})
.onGet(async () => {
const value = this.accessory.operationMode === 8 ? this.accessory.defaultHeatingSetTemperature : this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
deviceData.Device.DefaultHeatingSetTemperature = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`);
} catch (error) {
this.emit('warn', `Set heating threshold temperature error: ${error}`);
};
});
};
this.melCloudService.getCharacteristic(Characteristic.LockPhysicalControls)
.onGet(async () => {
const value = this.accessory.lockPhysicalControl;
return value;
})
.onSet(async (value) => {
try {
value = value ? true : false;
deviceData.Device.ProhibitSetTemperature = value;
deviceData.Device.ProhibitOperationMode = value;
deviceData.Device.ProhibitPower = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Prohibit;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set local physical controls: ${value ? 'LOCK' : 'UNLOCK'}`);
} catch (error) {
this.emit('warn', `Set lock physical controls error: ${error}`);
};
});
this.melCloudService.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.onGet(async () => {
const value = this.accessory.useFahrenheit;
return value;
})
.onSet(async (value) => {
try {
accountInfo.UseFahrenheit = [false, true][value];
await this.melCloud.send(accountInfo);
this.accessory.useFahrenheit = value;
const info = this.disableLogInfo ? false : this.emit('message', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
} catch (error) {
this.emit('warn', `Set temperature display unit error: ${error}`);
};
});
accessory.addService(this.melCloudService);
break;
case 2: //Thermostat
const debug1 = this.enableDebugMode ? this.emit('debug', `Prepare thermostat service`) : false;
this.melCloudService = new Service.Thermostat(serviceName, `Thermostat ${deviceId}`);
this.melCloudService.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.onGet(async () => {
const value = this.accessory.currentOperationMode;
return value;
});
this.melCloudService.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 {
switch (value) {
case 0: //OFF - POWER OFF
deviceData.Device.Power = false;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power;
break;
case 1: //HEAT - HEAT
deviceData.Device.Power = true;
deviceData.Device.OperationMode = heatDryFanMode;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 2: //COOL - COOL
deviceData.Device.Power = true;
deviceData.Device.OperationMode = coolDryFanMode;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature
break;
case 3: //AUTO - AUTO
deviceData.Device.Power = true;
deviceData.Device.OperationMode = autoDryFanMode;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
};
await this.melCloudAta.send(deviceData, this.displayMode);
const operationModeText = AirConditioner.DriveMode[deviceData.Device.OperationMode];
const info = this.disableLogInfo ? false : this.emit('message', `Set operation mode: ${operationModeText}`);
} catch (error) {
this.emit('warn', `Set operation mode error: ${error}`);
};
});
this.melCloudService.getCharacteristic(Characteristic.CurrentTemperature)
.onGet(async () => {
const value = this.accessory.roomTemperature;
return value;
});
this.melCloudService.getCharacteristic(Characteristic.TargetTemperature)
.setProps({
minValue: this.accessory.minTempHeat,
maxValue: this.accessory.maxTempHeat,
minStep: this.accessory.temperatureIncrement
})
.onGet(async () => {
const value = this.accessory.setTemperature;
return value;
})
.onSet(async (value) => {
try {
deviceData.Device.SetTemperature = value;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.SetTemperature;
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `Set temperature: ${value}${this.accessory.temperatureUnit}`);
} catch (error) {
this.emit('warn', `Set temperature error: ${error}`);
};
});
this.melCloudService.getCharacteristic(Characteristic.TemperatureDisplayUnits)
.onGet(async () => {
const value = this.accessory.useFahrenheit;
return value;
})
.onSet(async (value) => {
try {
accountInfo.UseFahrenheit = [false, true][value];
await this.melCloud.send(accountInfo);
this.accessory.useFahrenheit = value;
const info = this.disableLogInfo ? false : this.emit('message', `Set temperature display unit: ${TemperatureDisplayUnits[value]}`);
} catch (error) {
this.emit('warn', `Set temperature display unit error: ${error}`);
};
});
accessory.addService(this.melCloudService);
break;
};
//temperature sensor services
if (this.temperatureSensor && this.accessory.roomTemperature !== null) {
const debug = this.enableDebugMode ? this.emit('debug', `Prepare room temperature sensor service`) : false;
this.roomTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Room`, `Room Temperature Sensor ${deviceId}`);
this.roomTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.roomTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Room`);
this.roomTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature)
.setProps({
minValue: -35,
maxValue: 150,
minStep: 0.5
})
.onGet(async () => {
const state = this.accessory.roomTemperature;
return state;
})
accessory.addService(this.roomTemperatureSensorService);
};
if (this.temperatureSensorOutdoor && hasOutdoorTemperature && this.accessory.outdoorTemperature !== null) {
const debug = this.enableDebugMode ? this.emit('debug', `Prepare outdoor temperature sensor service`) : false;
this.outdoorTemperatureSensorService = new Service.TemperatureSensor(`${serviceName} Outdoor`, `Outdoor Temperature Sensor ${deviceId}`);
this.outdoorTemperatureSensorService.addOptionalCharacteristic(Characteristic.ConfiguredName);
this.outdoorTemperatureSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Outdoor`);
this.outdoorTemperatureSensorService.getCharacteristic(Characteristic.CurrentTemperature)
.setProps({
minValue: -35,
maxValue: 150,
minStep: 0.5
})
.onGet(async () => {
const state = this.accessory.outdoorTemperature;
return state;
})
accessory.addService(this.outdoorTemperatureSensorService);
};
//presets services
if (this.presetsConfiguredCount > 0) {
const debug = this.enableDebugMode ? this.emit('debug', `Prepare presets services`) : false;
this.presetsServices = [];
for (let i = 0; i < this.presetsConfiguredCount; i++) {
const preset = this.presetsConfigured[i];
const presetData = presetsOnServer.find(p => p.ID === preset.Id);
//get preset name
const presetName = preset.name;
//get preset name prefix
const presetNamePrefix = preset.namePrefix;
const serviceName = presetNamePrefix ? `${accessoryName} ${presetName}` : presetName;
const serviceType = preset.serviceType;
const characteristicType = preset.characteristicType;
const presetService = new serviceType(serviceName, `Preset ${deviceId} ${i}`);
presetService.addOptionalCharacteristic(Characteristic.ConfiguredName);
presetService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
presetService.getCharacteristic(characteristicType)
.onGet(async () => {
const state = preset.state;
return state;
})
.onSet(async (state) => {
try {
switch (state) {
case true:
preset.previousSettings = deviceData.Device;
deviceData.Device.Power = presetData.Power;
deviceData.Device.OperationMode = presetData.OperationMode;
deviceData.Device.SetTemperature = presetData.SetTemperature;
deviceData.Device.VaneHorizontalDirection = presetData.VaneHorizontal;
deviceData.Device.VaneVerticalDirection = presetData.VaneVertical;
deviceData.Device.FanSpeed = presetData.FanSpeed;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Presets;
break;
case false:
deviceData.Device.Power = preset.previousSettings.Power;
deviceData.Device.OperationMode = preset.previousSettings.OperationMode;
deviceData.Device.SetTemperature = preset.previousSettings.SetTemperature;
deviceData.Device.VaneHorizontalDirection = preset.previousSettings.VaneHorizontalDirection;
deviceData.Device.VaneVerticalDirection = preset.previousSettings.VaneVerticalDirection;
deviceData.Device.FanSpeed = preset.previousSettings.FanSpeed;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Presets;
break;
};
await this.melCloudAta.send(deviceData, this.displayMode);
const info = this.disableLogInfo ? false : this.emit('message', `${state ? 'Set:' : 'Unset:'} ${presetName}`);
} catch (error) {
this.emit('warn', `Set preset error: ${error}`);
};
});
this.presetsServices.push(presetService);
accessory.addService(presetService);
};
};
//buttons services
if (this.buttonsConfiguredCount > 0) {
const debug = this.enableDebugMode ? this.emit('debug', `Prepare buttons/sensors services`) : false;
this.buttonsServices = [];
for (let i = 0; i < this.buttonsConfiguredCount; i++) {
const button = this.buttonsConfigured[i];
//get button mode
const mode = button.mode;
//get button name
const buttonName = button.name;
//get button name prefix
const buttonNamePrefix = button.namePrefix;
const serviceName = buttonNamePrefix ? `${accessoryName} ${buttonName}` : buttonName;
const serviceType = button.serviceType;
const characteristicType = button.characteristicType;
const buttonService = new serviceType(serviceName, `Button ${deviceId} ${i}`);
buttonService.addOptionalCharacteristic(Characteristic.ConfiguredName);
buttonService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
buttonService.getCharacteristic(characteristicType)
.onGet(async () => {
const state = button.state;
return state;
})
.onSet(async (state) => {
try {
switch (mode) {
case 0: //POWER ON,OFF
deviceData.Device.Power = state;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.Power;
break;
case 1: //OPERATING MODE HEAT
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 1 : button.previousValue === 9 ? 1 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 2: //OPERATING MODE DRY
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 2 : button.previousValue === 10 ? 2 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break
case 3: //OPERATING MODE COOL
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 3 : button.previousValue === 11 ? 3 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 4: //OPERATING MODE FAN
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 7 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 5: //OPERATING MODE AUTO
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 8 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 6: //OPERATING MODE PURIFY
button.previousValue = state ? deviceData.Device.OperationMode : button.previousValue ?? deviceData.Device.OperationMode;
deviceData.Device.Power = true;
deviceData.Device.OperationMode = state ? 12 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerOperationModeSetTemperature;
break;
case 7: //OPERATING MODE DRY CONTROL HIDE
deviceData.HideDryModeControl = state;
break;
case 10: //VANE H SWING MODE AUTO
button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
deviceData.Device.Power = true;
deviceData.Device.VaneHorizontalDirection = state ? 0 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
break;
case 11: //VANE H SWING MODE 1
button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
deviceData.Device.Power = true;
deviceData.Device.VaneHorizontalDirection = state ? 1 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
break;
case 12: //VANE H SWING MODE 2
button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
deviceData.Device.Power = true;
deviceData.Device.VaneHorizontalDirection = state ? 2 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal;
break;
case 13: //VANE H SWING MODE 3
button.previousValue = state ? deviceData.Device.VaneHorizontalDirection : button.previousValue ?? deviceData.Device.VaneHorizontalDirection;
deviceData.Device.Power = true;
deviceData.Device.VaneHorizontalDirection = state ? 3 : button.previousValue;
deviceData.Device.EffectiveFlags = AirConditioner.EffectiveFlags.PowerVaneHorizontal;