homebridge-melcloud-control
Version:
Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.
745 lines (720 loc) • 140 kB
JavaScript
import EventEmitter from 'events';
import MelCloudAtw from './melcloudatw.js';
import RestFul from './restful.js';
import Mqtt from './mqtt.js';
import { TemperatureDisplayUnits, HeatPump } from './constants.js';
let Accessory, Characteristic, Service, Categories, AccessoryUUID;
class DeviceAtw 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.temperatureSensorFlow = device.temperatureSensorFlow || false;
this.temperatureSensorReturn = device.temperatureSensorReturn || false;
this.temperatureSensorFlowZone1 = device.temperatureSensorFlowZone1 || false;
this.temperatureSensorReturnZone1 = device.temperatureSensorFlowWaterTank || false;
this.temperatureSensorFlowWaterTank = device.temperatureSensorFlowZone1 || false;
this.temperatureSensorReturnWaterTank = device.temperatureSensorReturnWaterTank || false;
this.temperatureSensorFlowZone2 = device.temperatureSensorFlowZone2 || false;
this.temperatureSensorReturnZone2 = device.temperatureSensorReturnZone2 || false;
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 = { zones: [{}, {}, {}, {}] };
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 = HeatPump.EffectiveFlags.Power;
set = await this.melCloudAtw.send(deviceData);
break;
case 'OperationMode':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationMode;
set = await this.melCloudAtw.send(deviceData);
break;
case 'OperationModeZone1':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
set = await this.melCloudAtw.send(deviceData);
break;
case 'OperationModeZone2':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetTemperatureZone1':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetTemperatureZone2':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetHeatFlowTemperatureZone1':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetHeatFlowTemperatureZone2':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetCoolFlowTemperatureZone1':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetCoolFlowTemperatureZone2':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'SetTankWaterTemperature':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature;
set = await this.melCloudAtw.send(deviceData);
break;
case 'ForcedHotWaterMode':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
set = await this.melCloudAtw.send(deviceData);
break;
case 'EcoHotWater':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.EcoHotWater;
set = await this.melCloudAtw.send(deviceData);
break;
case 'HolidayMode':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.HolidayMode;
set = await this.melCloudAtw.send(deviceData);
break;
case 'ProhibitZone1':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitZone1;
set = await this.melCloudAtw.send(deviceData);
break;
case 'ProhibitZone2':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitZone2;
set = await this.melCloudAtw.send(deviceData);
break;
case 'ProhibitHotWater':
deviceData.Device[key] = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ProhibitHotWater;
set = await this.melCloudAtw.send(deviceData);
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.melCloudAtw.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 zonesCount = this.accessory.zonesCount;
const caseHotWater = this.accessory.caseHotWater;
const caseZone2 = this.accessory.caseZone2;
const heatCoolModes = this.accessory.heatCoolModes;
const temperatureSensor = this.temperatureSensor;
const temperatureSensorFlow = this.temperatureSensorFlow;
const temperatureSensorReturn = this.temperatureSensorReturn;
const temperatureSensorFlowZone1 = this.temperatureSensorFlowZone1;
const temperatureSensorReturnZone1 = this.temperatureSensorReturnZone1;
const temperatureSensorFlowWaterTank = this.temperatureSensorFlowWaterTank;
const temperatureSensorReturnWaterTank = this.temperatureSensorReturnWaterTank;
const temperatureSensorFlowZone2 = this.temperatureSensorFlowZone2;
const temperatureSensorReturnZone2 = this.temperatureSensorReturnZone2;
//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_HEATER;
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);
//services
this.melCloudServices = [];
for (let i = 0; i < zonesCount; i++) {
const zoneName = this.accessory.zones[i].name
const serviceName = `${deviceTypeText} ${accessoryName}: ${zoneName}`;
switch (this.displayMode) {
case 1: //Heater Cooler
const debug = this.enableDebugMode ? this.emit('debug', `Prepare heather/cooler ${zoneName} service`) : false;
const melCloudService = new Service.HeaterCooler(serviceName, `HeaterCooler ${deviceId} ${i}`);
melCloudService.getCharacteristic(Characteristic.Active)
.onGet(async () => {
const state = this.accessory.power;
return state;
})
.onSet(async (state) => {
try {
switch (i) {
case 0: //Heat Pump
deviceData.Device.Power = [false, true][state];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power;
await this.melCloudAtw.send(deviceData);
const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set power: ${state ? 'ON' : 'OFF'}`);
break;
};
} catch (error) {
this.emit('warn', `Set power error: ${error}`);
};
});
melCloudService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
.onGet(async () => {
const value = this.accessory.zones[i].currentOperationMode;
return value;
});
melCloudService.getCharacteristic(Characteristic.TargetHeaterCoolerState)
.setProps({
minValue: this.accessory.zones[i].operationModesSetPropsMinValue,
maxValue: this.accessory.zones[i].operationModesSetPropsMaxValue,
validValues: this.accessory.zones[i].operationModesSetPropsValidValues
})
.onGet(async () => {
const value = this.accessory.zones[i].targetOperationMode;
return value;
})
.onSet(async (value) => {
try {
let operationModeText = '';
switch (i) {
case 0: //Heat Pump - ON, HEAT, COOL
switch (value) {
case 0: //AUTO
deviceData.Device.Power = true;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power;
break;
case 1: //HEAT
deviceData.Device.Power = true;
deviceData.Device.UnitStatus = 0;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode;
break;
case 2: //COOL
deviceData.Device.Power = true;
deviceData.Device.UnitStatus = 1;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode;
break;
};
operationModeText = [HeatPump.System[0], HeatPump.System[deviceData.Device.UnitStatus]][this.accessory.power];
break;
case 1: //Zone 1 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
switch (value) {
case 0: //AUTO - HEAT CURVE
deviceData.Device.OperationModeZone1 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT
deviceData.Device.OperationModeZone1 = [0, 3][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
case 2: //COOL - HEAT FLOW / COOL FLOW
deviceData.Device.OperationModeZone1 = [1, 4][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
};
operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone1];
break;
case caseHotWater: //Hot Water - AUTO, HEAT NOW
switch (value) {
case 0: //AUTO
deviceData.Device.ForcedHotWaterMode = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
case 1: //HEAT
deviceData.Device.ForcedHotWaterMode = true;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
case 2: //COOL
deviceData.Device.ForcedHotWaterMode = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break
};
operationModeText = deviceData.Device.OperationMode === 1 ? HeatPump.ForceDhw[1] : HeatPump.ForceDhw[deviceData.Device.ForcedHotWaterMode ? 1 : 0];
break;
case caseZone2: //Zone 2 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
switch (value) {
case 0: //AUTO
deviceData.Device.OperationModeZone2 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT
deviceData.Device.OperationModeZone2 = [0, 3][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
case 2: //COOL - HEAT FLOW / COOL FLOW
deviceData.Device.OperationModeZone2 = [1, 4][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
};
operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone2];
break;
};
await this.melCloudAtw.send(deviceData);
const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set operation mode: ${operationModeText}`);
} catch (error) {
this.emit('warn', `${zoneName}, Set operation mode error: ${error}`);
};
});
melCloudService.getCharacteristic(Characteristic.CurrentTemperature)
.setProps({
minValue: -35,
maxValue: 150,
minStep: 0.5
})
.onGet(async () => {
const value = this.accessory.zones[i].roomTemperature;
return value;
});
//only for heat/cool, only cool and not for hot water tank
if ((heatCoolModes === 0 || heatCoolModes === 2) && i !== caseHotWater) {
melCloudService.getCharacteristic(Characteristic.CoolingThresholdTemperature)
.setProps({
minValue: this.accessory.zones[i].temperaturesSetPropsMinValue,
maxValue: this.accessory.zones[i].temperaturesSetPropsMaxValue,
minStep: this.accessory.temperatureIncrement
})
.onGet(async () => {
const value = this.accessory.zones[i].setTemperature;
return value;
})
.onSet(async (value) => {
try {
switch (i) {
case 0: //Heat Pump
//deviceData.Device.SetTemperatureZone1 = value;
//deviceData.Device.EffectiveFlags = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1;
break;
case 1: //Zone 1
switch (this.accessory.zones[i].operationMode) {
case 1: //HEAT FLOW
deviceData.Device.SetHeatFlowTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1;
break;
case 4: //COOL FLOW
deviceData.Device.SetCoolFlowTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1;
break;
default:
deviceData.Device.SetTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone1;
break
};
break;
case caseHotWater: //Hot Water
deviceData.Device.SetTankWaterTemperature = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature;
break;
case caseZone2: //Zone 2
switch (this.accessory.zones[i].operationMode) {
case 1: //HEAT FLOW
deviceData.Device.SetHeatFlowTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2;
break;
case 4: //COOL FLOW
deviceData.Device.SetCoolFlowTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2;
break;
default:
deviceData.Device.SetTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2;
break
};
break;
};
const set = i > 0 ? await this.melCloudAtw.send(deviceData) : false;
const info = this.disableLogInfo || i === 0 ? false : this.emit('message', `${zoneName}, Set cooling threshold temperature: ${value}${this.accessory.temperatureUnit}`);
} catch (error) {
this.emit('warn', `${zoneName}, Set cooling threshold temperature error: ${error}`);
};
});
};
//device can heat/cool or only heat
if (heatCoolModes === 0 || heatCoolModes === 1) {
melCloudService.getCharacteristic(Characteristic.HeatingThresholdTemperature)
.setProps({
minValue: this.accessory.zones[i].temperaturesSetPropsMinValue,
maxValue: this.accessory.zones[i].temperaturesSetPropsMaxValue,
minStep: this.accessory.temperatureIncrement
})
.onGet(async () => {
const value = this.accessory.zones[i].setTemperature;
return value;
})
.onSet(async (value) => {
try {
switch (i) {
case 0: //Heat Pump
//deviceData.Device.SetTemperatureZone1 = value;
//deviceData.Device.EffectiveFlags = CONSTANTS.HeatPump.EffectiveFlags.SetTemperatureZone1;
break;
case 1: //Zone 1
switch (this.accessory.zones[i].operationMode) {
case 1: //HEAT FLOW
deviceData.Device.SetHeatFlowTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone1;
break;
case 4: //COOL FLOW
deviceData.Device.SetCoolFlowTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone1;
break;
default:
deviceData.Device.SetTemperatureZone1 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone1;
break
};
break;
case caseHotWater: //Hot Water
deviceData.Device.SetTankWaterTemperature = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTankWaterTemperature;
break;
case caseZone2: //Zone 2
switch (this.accessory.zones[i].operationMode) {
case 1: //HEAT FLOW
deviceData.Device.SetHeatFlowTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetHeatFlowTemperatureZone2;
break;
case 4: //COOL FLOW
deviceData.Device.SetCoolFlowTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetCoolFlowTemperatureZone2;
break;
default:
deviceData.Device.SetTemperatureZone2 = value;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.SetTemperatureZone2;
break
};
break;
};
const set = i > 0 ? await this.melCloudAtw.send(deviceData) : false;
const info = this.disableLogInfo || i === 0 ? false : this.emit('message', `${zoneName}, Set heating threshold temperature: ${value}${this.accessory.temperatureUnit}`);
} catch (error) {
this.emit('warn', `${zoneName}, Set heating threshold temperature error: ${error}`);
};
});
};
melCloudService.getCharacteristic(Characteristic.LockPhysicalControls)
.onGet(async () => {
const value = this.accessory.zones[i].lockPhysicalControl;
return value;
})
.onSet(async (value) => {
try {
value = value ? true : false;
switch (i) {
case 0: //Heat Pump
deviceData.Device.ProhibitZone1 = value;
deviceData.Device.ProhibitHotWater = value;
deviceData.Device.ProhibitZone2 = value;
HeatPump.EffectiveFlags.ProhibitHeatingZone1 + HeatPump.EffectiveFlags.ProhibitHotWater + HeatPump.EffectiveFlags.ProhibitHeatingZone2;
break;
case 1: //Zone 1
deviceData.Device.ProhibitZone1 = value;
HeatPump.EffectiveFlags.ProhibitHeatingZone1;
break;
case caseHotWater: //Hot Water
deviceData.Device.ProhibitHotWater = value;
HeatPump.EffectiveFlags.ProhibitHotWater;
break;
case caseZone2: //Zone 2
deviceData.Device.ProhibitZone2 = value;
HeatPump.EffectiveFlags.ProhibitHeatingZone2;
break;
};
await this.melCloudAtw.send(deviceData);
const info = this.disableLogInfo ? false : this.emit('message', `${zoneName}, Set lock physical controls: ${value ? 'LOCK' : 'UNLOCK'}`);
} catch (error) {
this.emit('warn', `${zoneName}, Set lock physical controls error: ${error}`);
};
});
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}`);
};
});
this.melCloudServices.push(melCloudService);
accessory.addService(melCloudService);
break;
case 2: //Thermostat
const debug3 = this.enableDebugMode ? this.emit('debug', `Prepare thermostat ${zoneName} service`) : false;
const melCloudServiceT = new Service.Thermostat(serviceName, `Thermostat ${deviceId} ${i}`);
melCloudServiceT.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.onGet(async () => {
const value = this.accessory.zones[i].currentOperationMode;
return value;
});
melCloudServiceT.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.setProps({
minValue: this.accessory.zones[i].operationModesSetPropsMinValue,
maxValue: this.accessory.zones[i].operationModesSetPropsMaxValue,
validValues: this.accessory.zones[i].operationModesSetPropsValidValues
})
.onGet(async () => {
const value = this.accessory.zones[i].targetOperationMode;
return value;
})
.onSet(async (value) => {
try {
let operationModeText = '';
switch (i) {
case 0: //Heat Pump - HEAT, COOL
switch (value) {
case 0: //OFF
deviceData.Device.Power = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power;
break;
case 1: //HEAT
deviceData.Device.Power = true;
deviceData.Device.UnitStatus = 0;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode;
break;
case 2: //COOL
deviceData.Device.Power = true;
deviceData.Device.UnitStatus = 1;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power + HeatPump.EffectiveFlags.OperationMode;
break;
case 3: //AUTO
deviceData.Device.Power = true;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.Power;
break;
};
operationModeText = [HeatPump.System[0], HeatPump.System[deviceData.Device.UnitStatus]][this.accessory.power];
break;
case 1: //Zone 1 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
switch (value) {
case 0: //OFF - HEAT CURVE
deviceData.Device.OperationModeZone1 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT
deviceData.Device.OperationModeZone1 = [0, 3][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
case 2: //COOL - HEAT FLOW / COOL FLOW
deviceData.Device.OperationModeZone1 = [1, 4][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
case 3: //AUTO - HEAT CURVE
deviceData.Device.OperationModeZone1 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone1;
break;
};
operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone1];
break;
case caseHotWater: //Hot Water - AUTO, HEAT NOW
switch (value) {
case 0: //OFF
deviceData.Device.ForcedHotWaterMode = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
case 1: //HEAT
deviceData.Device.ForcedHotWaterMode = true;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
case 2: //COOL
deviceData.Device.ForcedHotWaterMode = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
case 3: //AUTO
deviceData.Device.ForcedHotWaterMode = false;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.ForcedHotWaterMode;
break;
};
operationModeText = deviceData.Device.OperationMode === 1 ? HeatPump.ForceDhw[1] : HeatPump.ForceDhw[deviceData.Device.ForcedHotWaterMode ? 1 : 0];
break;
case caseZone2: //Zone 2 - HEAT THERMOSTAT, HEAT FLOW, HEAT CURVE, COOL THERMOSTAT, COOL FLOW, FLOOR DRY UP
switch (value) {
case 0: //OFF - HEAT CURVE
deviceData.Device.OperationModeZone2 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
case 1: //HEAT - HEAT THERMOSTAT / COOL THERMOSTAT
deviceData.Device.OperationModeZone2 = [0, 3][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
case 2: //COOL - HEAT FLOW / COOL FLOW
deviceData.Device.OperationModeZone2 = [1, 4][this.unitStatus];
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
case 3: //AUTO - HEAT CURVE
deviceData.Device.OperationModeZone2 = 2;
deviceData.Device.EffectiveFlags = HeatPump.EffectiveFlags.OperationModeZone2;
break;
};
operationModeText = HeatPump.ZoneOperation[deviceData.Device.OperationModeZone2];