iobroker.melcloud
Version:
595 lines (557 loc) • 17.1 kB
JavaScript
"use strict";
const commonDefines = require("./commonDefines");
const HttpStatus = require("http-status-codes");
const MelcloudBaseDevice = require("./melcloudBaseDevice");
const Axios = require("axios").default;
class MelcloudErvDevice extends MelcloudBaseDevice {
constructor(adapter, platform) {
super(adapter, platform, commonDefines.DeviceTypes.EnergyRecoveryVentilation);
// Info
this.minTempCoolDry = 0;
this.maxTempCoolDry = 0;
this.minTempHeat = 0;
this.maxTempHeat = 0;
this.minTempAuto = 0;
this.maxTempAuto = 0;
this.roomTemp = 0;
this.outdoorTemp = 0;
this.actualSupplyFanSpeed = 0;
this.actualExhaustFanSpeed = 0;
this.numberOfFanSpeeds = 0;
// Control
this.operationMode = commonDefines.ErvDeviceOperationModes.UNDEF.value;
this.fanSpeed = 0;
}
// Creates all necessary states and channels and writes the values into the DB
async CreateAndSave() {
// check if object has already been created
if (this.hasBeenCreated) {
return;
}
await super.createCommonStates();
const devicePrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}`;
//#region INFO
const infoPrefix = `${devicePrefix}.${commonDefines.AdapterDatapointIDs.Info}`;
await this.createStates(infoPrefix, [
{
id: commonDefines.ErvDeviceStateIDs.MinTempCoolDry,
definition: {
type: "state",
common: {
name: "Minimal temperature (Cool/Dry)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.minTempCoolDry,
desc: "Minimal temperature in cool/dry-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.MaxTempCoolDry,
definition: {
type: "state",
common: {
name: "Maximal temperature (Cool/Dry)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.maxTempCoolDry,
desc: "Maximal temperature in cool/dry-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.MinTempHeat,
definition: {
type: "state",
common: {
name: "Minimal temperature (Heat)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.minTempHeat,
desc: "Minimal temperature in heat-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.MaxTempHeat,
definition: {
type: "state",
common: {
name: "Maximal temperature (Heat)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.maxTempHeat,
desc: "Maximal temperature in heat-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.MinTempAuto,
definition: {
type: "state",
common: {
name: "Minimal Temperature (Auto)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.minTempAuto,
desc: "Minimal temperature in auto-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.MaxTempAuto,
definition: {
type: "state",
common: {
name: "Maximal Temperature (Auto)",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.maxTempAuto,
desc: "Maximal temperature in auto-mode",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.RoomTemp,
definition: {
type: "state",
common: {
name: "Room temperature",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.roomTemp,
desc: "Current room temperature",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.OutdoorTemp,
definition: {
type: "state",
common: {
name: "Outdoor temperature",
type: "number",
role: "value.temperature",
unit: this.platform.UseFahrenheit ? "°F" : "°C",
read: true,
write: false,
def: this.outdoorTemp,
desc: "Current outdoor temperature",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.SupplyFanSpeed,
definition: {
type: "state",
common: {
name: "Supply fan speed",
type: "number",
role: "value",
read: true,
write: false,
def: this.actualSupplyFanSpeed,
desc: "Actual supply fan speed",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.ExhaustFanSpeed,
definition: {
type: "state",
common: {
name: "Exhaust fan speed",
type: "number",
role: "value",
read: true,
write: false,
def: this.actualExhaustFanSpeed,
desc: "Actual exhaust fan speed",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.NumberOfFanSpeeds,
definition: {
type: "state",
common: {
name: "Number of fan speeds",
type: "number",
role: "value",
read: true,
write: false,
def: this.numberOfFanSpeeds,
desc: "Number of available fan speeds",
},
native: {},
},
},
]);
//#endregion
//#region CONTROL
const controlPrefix = `${devicePrefix}.${commonDefines.AdapterDatapointIDs.Control}`;
await this.createStates(controlPrefix, [
{
id: commonDefines.ErvDeviceStateIDs.Power,
definition: {
type: "state",
common: {
name: "Power",
type: "boolean",
role: "switch.power",
read: true,
write: true,
def: this.power,
desc: "Power switch",
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.Mode,
definition: {
type: "state",
common: {
name: "Operation mode",
type: "number",
role: "value",
read: true,
write: true,
def: this.operationMode,
desc: "Operation mode of the device",
states: {
0: "Recovery",
1: "Bypass",
2: "Auto",
},
},
native: {},
},
},
{
id: commonDefines.ErvDeviceStateIDs.FanSpeed,
definition: {
type: "state",
common: {
name: "Fan speed",
type: "number",
role: "value",
min: 0,
max: 5,
states: {
0: "Auto",
1: "Silent",
2: "1",
3: "2",
4: "3",
5: "4",
},
read: true,
write: true,
def: this.fanSpeed,
desc: "Current fan speed of the device",
},
native: {},
},
},
]);
//#endregion
this.adapter.log.debug(`Created and saved ERV device ${this.id} (${this.name})`);
this.hasBeenCreated = true;
}
// Only writes changed device data into the DB
async UpdateDeviceData(deviceOption) {
super.UpdateCommonDeviceData();
//#region INFO
const infoPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}.${commonDefines.AdapterDatapointIDs.Info}.`;
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MinTempCoolDry,
this.minTempCoolDry,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MaxTempCoolDry,
this.maxTempCoolDry,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MinTempHeat,
this.minTempHeat,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MaxTempHeat,
this.maxTempHeat,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MinTempAuto,
this.minTempAuto,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.MaxTempAuto,
this.maxTempAuto,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.RoomTemp,
this.roomTemp,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.SupplyFanSpeed,
this.actualSupplyFanSpeed,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.ExhaustFanSpeed,
this.actualExhaustFanSpeed,
true,
);
await this.adapter.setStateChangedAsync(
infoPrefix + commonDefines.ErvDeviceStateIDs.NumberOfFanSpeeds,
this.numberOfFanSpeeds,
true,
);
//#endregion
//#region CONTROL
const controlPrefix = `${commonDefines.AdapterDatapointIDs.Devices}.${this.id}.${commonDefines.AdapterDatapointIDs.Control}.`;
switch (deviceOption) {
case commonDefines.ErvDeviceOptions.PowerState:
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.Power,
this.power,
true,
);
break;
case commonDefines.ErvDeviceOptions.OperationMode:
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.Mode,
this.operationMode,
true,
);
break;
case commonDefines.ErvDeviceOptions.FanSpeed:
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.FanSpeed,
this.fanSpeed,
true,
);
break;
case "ALL":
default:
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.Power,
this.power,
true,
);
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.Mode,
this.operationMode,
true,
);
await this.adapter.setStateChangedAsync(
controlPrefix + commonDefines.ErvDeviceStateIDs.FanSpeed,
this.fanSpeed,
true,
);
break;
}
//#endregion
this.adapter.log.debug(`Updated device data for ERV device ${this.id} (${this.name})`);
}
setDevice(deviceOption, value) {
if (this.currentDeviceSetRequests < 1) {
this.currentDeviceSetRequests++;
this.adapter.log.debug(
`Changing device option '${deviceOption.id}' to '${value.value != undefined ? value.value : value}'...`,
);
const modifiedAirInfo = this.airInfo;
if (modifiedAirInfo == null) {
this.adapter.log.error(
`setDevice(): modifiedAirInfo is not filled - please report this to the developer!`,
);
this.currentDeviceSetRequests--;
return;
}
value = this.verifyDeviceOptionValue(deviceOption, value);
if (deviceOption == commonDefines.ErvDeviceOptions.PowerState) {
switch (value) {
case commonDefines.DevicePowerStates.OFF:
modifiedAirInfo.Power = commonDefines.DevicePowerStates.OFF.value;
modifiedAirInfo.EffectiveFlags = commonDefines.DevicePowerStates.OFF.effectiveFlags;
break;
case commonDefines.DevicePowerStates.ON:
modifiedAirInfo.Power = commonDefines.DevicePowerStates.ON.value;
modifiedAirInfo.EffectiveFlags = commonDefines.DevicePowerStates.ON.effectiveFlags;
break;
default:
this.adapter.log.error(
"setDevice(): Unsupported value for device option - please report this to the developer!",
);
this.currentDeviceSetRequests--;
return;
}
} else if (deviceOption == commonDefines.ErvDeviceOptions.OperationMode) {
switch (value) {
case commonDefines.ErvDeviceOperationModes.RECOVERY:
modifiedAirInfo.OperationMode = commonDefines.ErvDeviceOperationModes.RECOVERY.value;
modifiedAirInfo.EffectiveFlags = commonDefines.ErvDeviceOperationModes.RECOVERY.effectiveFlags;
break;
case commonDefines.ErvDeviceOperationModes.BYPASS:
modifiedAirInfo.OperationMode = commonDefines.ErvDeviceOperationModes.BYPASS.value;
modifiedAirInfo.EffectiveFlags = commonDefines.ErvDeviceOperationModes.BYPASS.effectiveFlags;
break;
case commonDefines.ErvDeviceOperationModes.AUTO:
modifiedAirInfo.OperationMode = commonDefines.ErvDeviceOperationModes.AUTO.value;
modifiedAirInfo.EffectiveFlags = commonDefines.ErvDeviceOperationModes.AUTO.effectiveFlags;
break;
default:
this.adapter.log.error(
"setDevice(): Unsupported value for device option - please report this to the developer!",
);
this.currentDeviceSetRequests--;
return;
}
} else if (deviceOption == commonDefines.ErvDeviceOptions.FanSpeed) {
modifiedAirInfo.SetFanSpeed = value;
modifiedAirInfo.EffectiveFlags = commonDefines.ErvDeviceOptions.FanSpeed.effectiveFlags;
} else {
this.adapter.log.error("setDevice(): Unsupported device option - please report this to the developer!");
this.currentDeviceSetRequests--;
return;
}
modifiedAirInfo.HasPendingCommand = true;
const url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetErv";
const body = JSON.stringify(modifiedAirInfo);
this.adapter.log.silly(`Request body: ${body}`);
Axios.post(url, body, {
httpsAgent: this.platform.customHttpsAgent,
headers: {
Host: "app.melcloud.com",
"X-MitsContextKey": this.platform.contextKey,
"Content-Type": "application/json; charset=utf-8",
},
})
.then(response => {
if (!response) {
this.adapter.log.error(`There was a problem receiving the response from: ${url}`);
this.airInfo = null;
} else {
const statusCode = response.status;
const statusText = response.statusText;
this.adapter.log.debug(
`Received response from: ${url} (status code: ${statusCode} - ${statusText})`,
);
if (statusCode != HttpStatus.StatusCodes.OK) {
this.airInfo = null;
this.adapter.log.error(
`Invalid HTTP status code (${statusCode} - ${statusText}). Changing device option failed!`,
);
this.currentDeviceSetRequests--;
return;
}
const responseData = response.data;
this.adapter.log.debug(`Response from cloud: ${JSON.stringify(responseData)}`);
this.lastCommunication = responseData.LastCommunication;
this.nextCommunication = responseData.NextCommunication;
this.roomTemp = responseData.RoomTemperature;
this.deviceOnline = !responseData.Offline;
this.errorCode = responseData.ErrorCode;
this.errorMessages = responseData.ErrorMessage;
switch (deviceOption) {
case commonDefines.ErvDeviceOptions.PowerState:
this.power = responseData.Power;
break;
case commonDefines.ErvDeviceOptions.OperationMode:
this.operationMode = responseData.OperationMode;
break;
case commonDefines.ErvDeviceOptions.FanSpeed:
this.fanSpeed = responseData.SetFanSpeed;
break;
default:
break;
}
this.UpdateDeviceData(deviceOption); // write updated values
this.currentDeviceSetRequests--;
if (this.deviceSetRequestQueue.length) {
const args = this.deviceSetRequestQueue.shift();
this.adapter.log.debug(
`Dequeueing setDevice remote request for device option '${args[0].id}' with value '${args[1].value != undefined ? args[1].value : args[1]}'`,
);
this.setDevice.apply(this, args);
}
}
})
.catch(error => {
this.adapter.log.error(`There was a problem setting info to: ${url}`);
this.adapter.log.error(error);
this.currentDeviceSetRequests--;
if (error.response && error.response.status && error.response.status == 429) {
this.adapter.log.error(
"You have probably been rate limited by the MELCloud servers because of too much requests. Stop the adapter for a few hours, increase the polling interval in the settings and try again later.",
);
}
if (this.deviceSetRequestQueue.length) {
const args = this.deviceSetRequestQueue.shift();
this.adapter.log.debug(
`Dequeueing setDevice remote request for device option '${args[0].id}' with value '${args[1].value != undefined ? args[1].value : args[1]}'`,
);
this.setDevice.apply(this, args);
}
});
} else {
this.adapter.log.debug(
`Queueing setDevice remote request for '${deviceOption.id}' with value '${value.value != undefined ? value.value : value}'...`,
);
this.deviceSetRequestQueue.push([deviceOption, value]);
}
}
verifyDeviceOptionValue(deviceOption, value) {
switch (deviceOption) {
case commonDefines.ErvDeviceOptions.FanSpeed:
if (value > this.numberOfFanSpeeds) {
this.adapter.log.warn(
`Fan speed limited to ${this.numberOfFanSpeeds} because device can't handle more than that!`,
);
return this.numberOfFanSpeeds;
}
return value;
default:
return value;
}
}
}
exports.MelCloudDevice = MelcloudErvDevice;