homebridge-melcloud-control 4.3.16-beta.9 → 4.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/src/deviceatw.js +89 -18
- package/src/functions.js +42 -0
- package/src/melcloudata.js +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
22
|
|
|
23
23
|
- Do not use Homebridge UI > v5.5.0 because of break config.json
|
|
24
24
|
|
|
25
|
+
# [4.4.0] - (10.12.2025)
|
|
26
|
+
|
|
27
|
+
## Changes
|
|
28
|
+
|
|
29
|
+
- added possibility to set frost protection min/max temperaure (ATA, ATW)
|
|
30
|
+
- added possibility to set overheat protection min/max temperture (ATA)
|
|
31
|
+
- bump dependencies
|
|
32
|
+
- cleanup
|
|
33
|
+
|
|
25
34
|
# [4.3.16] - (09.12.2025)
|
|
26
35
|
|
|
27
36
|
## Changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.4.0-beta.0",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
package/src/deviceatw.js
CHANGED
|
@@ -1054,27 +1054,95 @@ class DeviceAtw extends EventEmitter {
|
|
|
1054
1054
|
}
|
|
1055
1055
|
|
|
1056
1056
|
//frost protection
|
|
1057
|
-
if (this.frostProtectionSupport && this.accessory.
|
|
1057
|
+
if (this.frostProtectionSupport && this.accessory.frostProtection.Enabled !== null) {
|
|
1058
1058
|
//control
|
|
1059
1059
|
if (this.logDebug) this.emit('debug', `Prepare frost protection control service`);
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1060
|
+
const frostProtectionControlService = new Service.HeaterCooler(`${serviceName} Frost Protection`, `frostProtectionControlService${deviceId}`);
|
|
1061
|
+
frostProtectionControlService.addOptionalCharacteristic(Characteristic.ConfiguredName);
|
|
1062
|
+
frostProtectionControlService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
|
|
1063
|
+
frostProtectionControlService.getCharacteristic(Characteristic.Active)
|
|
1064
1064
|
.onGet(async () => {
|
|
1065
|
-
const state = this.accessory.
|
|
1065
|
+
const state = this.accessory.frostProtection.Enabled;
|
|
1066
1066
|
return state;
|
|
1067
1067
|
})
|
|
1068
1068
|
.onSet(async (state) => {
|
|
1069
1069
|
try {
|
|
1070
|
-
deviceData.FrostProtection.Enabled = state;
|
|
1070
|
+
deviceData.FrostProtection.Enabled = state ? true : false;
|
|
1071
1071
|
if (this.logInfo) this.emit('info', `Frost protection: ${state ? 'Enabled' : 'Disabled'}`);
|
|
1072
1072
|
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
|
|
1073
1073
|
} catch (error) {
|
|
1074
1074
|
if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
|
|
1075
1075
|
};
|
|
1076
1076
|
});
|
|
1077
|
-
|
|
1077
|
+
frostProtectionControlService.getCharacteristic(Characteristic.CurrentHeaterCoolerState)
|
|
1078
|
+
.onGet(async () => {
|
|
1079
|
+
const value = this.accessory.frostProtection.Active ? 2 : 1;
|
|
1080
|
+
return value;
|
|
1081
|
+
})
|
|
1082
|
+
frostProtectionControlService.getCharacteristic(Characteristic.TargetHeaterCoolerState)
|
|
1083
|
+
.setProps({
|
|
1084
|
+
minValue: 0,
|
|
1085
|
+
maxValue: 0,
|
|
1086
|
+
validValues: [0]
|
|
1087
|
+
})
|
|
1088
|
+
.onGet(async () => {
|
|
1089
|
+
const value = 0
|
|
1090
|
+
return value;
|
|
1091
|
+
})
|
|
1092
|
+
.onSet(async (value) => {
|
|
1093
|
+
try {
|
|
1094
|
+
deviceData.FrostProtection.Enabled = true;
|
|
1095
|
+
if (this.logInfo) this.emit('info', `Frost protection: Enabled`);
|
|
1096
|
+
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
|
|
1099
|
+
};
|
|
1100
|
+
});
|
|
1101
|
+
frostProtectionControlService.getCharacteristic(Characteristic.CurrentTemperature)
|
|
1102
|
+
.onGet(async () => {
|
|
1103
|
+
const value = this.accessory.roomTemperature;
|
|
1104
|
+
return value;
|
|
1105
|
+
});
|
|
1106
|
+
frostProtectionControlService.getCharacteristic(Characteristic.CoolingThresholdTemperature) //max
|
|
1107
|
+
.setProps({
|
|
1108
|
+
minValue: 6,
|
|
1109
|
+
maxValue: 16,
|
|
1110
|
+
minStep: 1
|
|
1111
|
+
})
|
|
1112
|
+
.onGet(async () => {
|
|
1113
|
+
const value = this.accessory.frostProtection.Max;
|
|
1114
|
+
return value;
|
|
1115
|
+
})
|
|
1116
|
+
.onSet(async (value) => {
|
|
1117
|
+
try {
|
|
1118
|
+
deviceData.FrostProtection.Max = value;
|
|
1119
|
+
if (this.logInfo) this.emit('info', `Set frost protection max. temperature: ${value}${this.accessory.temperatureUnit}`);
|
|
1120
|
+
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
if (this.logWarn) this.emit('warn', `Set frost protection max. temperature error: ${error}`);
|
|
1123
|
+
};
|
|
1124
|
+
});
|
|
1125
|
+
frostProtectionControlService.getCharacteristic(Characteristic.HeatingThresholdTemperature) //min
|
|
1126
|
+
.setProps({
|
|
1127
|
+
minValue: 4,
|
|
1128
|
+
maxValue: 14,
|
|
1129
|
+
minStep: 1
|
|
1130
|
+
})
|
|
1131
|
+
.onGet(async () => {
|
|
1132
|
+
const value = this.accessory.frostProtection.Min;
|
|
1133
|
+
return value;
|
|
1134
|
+
})
|
|
1135
|
+
.onSet(async (value) => {
|
|
1136
|
+
try {
|
|
1137
|
+
deviceData.FrostProtection.Min = value;
|
|
1138
|
+
if (this.logInfo) this.emit('info', `Set frost protection min. temperature: ${value}${this.accessory.temperatureUnit}`);
|
|
1139
|
+
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
if (this.logWarn) this.emit('warn', `Set frost protection min. temperature error: ${error}`);
|
|
1142
|
+
};
|
|
1143
|
+
});
|
|
1144
|
+
this.frostProtectionControlService = frostProtectionControlService;
|
|
1145
|
+
accessory.addService(frostProtectionControlService);
|
|
1078
1146
|
|
|
1079
1147
|
if (this.logDebug) this.emit('debug', `Prepare frost protection control sensor service`);
|
|
1080
1148
|
this.frostProtectionControlSensorService = new Service.ContactSensor(`${serviceName} Frost Protection Control`, `frostProtectionControlSensorService${deviceId}`);
|
|
@@ -1082,7 +1150,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1082
1150
|
this.frostProtectionControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection Control`);
|
|
1083
1151
|
this.frostProtectionControlSensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
1084
1152
|
.onGet(async () => {
|
|
1085
|
-
const state = this.accessory.
|
|
1153
|
+
const state = this.accessory.frostProtection.Enabled;
|
|
1086
1154
|
return state;
|
|
1087
1155
|
})
|
|
1088
1156
|
accessory.addService(this.frostProtectionControlSensorService);
|
|
@@ -1094,7 +1162,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1094
1162
|
this.frostProtectionSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
|
|
1095
1163
|
this.frostProtectionSensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
1096
1164
|
.onGet(async () => {
|
|
1097
|
-
const state = this.accessory.
|
|
1165
|
+
const state = this.accessory.frostProtection.Active;
|
|
1098
1166
|
return state;
|
|
1099
1167
|
})
|
|
1100
1168
|
accessory.addService(this.frostProtectionSensorService);
|
|
@@ -1609,8 +1677,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1609
1677
|
const holidayModeActive = deviceData.HolidayMode?.Active ?? false;
|
|
1610
1678
|
|
|
1611
1679
|
//protection
|
|
1612
|
-
const
|
|
1613
|
-
const frostProtectionActive = deviceData.FrostProtection?.Active ?? false;
|
|
1680
|
+
const frostProtection = deviceData.FrostProtection ?? {};
|
|
1614
1681
|
|
|
1615
1682
|
//device info
|
|
1616
1683
|
const supportsStanbyMode = deviceData.Device[supportStandbyKey];
|
|
@@ -1719,8 +1786,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1719
1786
|
temperatureUnit: TemperatureDisplayUnits[this.accountInfo.useFahrenheit ? 1 : 0],
|
|
1720
1787
|
isConnected: isConnected,
|
|
1721
1788
|
isInError: isInError,
|
|
1722
|
-
|
|
1723
|
-
frostProtectionActive: frostProtectionActive,
|
|
1789
|
+
frostProtection: frostProtection,
|
|
1724
1790
|
scheduleEnabled: scheduleEnabled,
|
|
1725
1791
|
holidayModeEnabled: holidayModeEnabled,
|
|
1726
1792
|
holidayModeActive: holidayModeActive,
|
|
@@ -2120,10 +2186,15 @@ class DeviceAtw extends EventEmitter {
|
|
|
2120
2186
|
this.errorService?.updateCharacteristic(Characteristic.ContactSensorState, isInError);
|
|
2121
2187
|
|
|
2122
2188
|
//frost protection
|
|
2123
|
-
if (this.frostProtectionSupport &&
|
|
2124
|
-
this.frostProtectionControlService?.updateCharacteristic(Characteristic.
|
|
2125
|
-
this.
|
|
2126
|
-
this.
|
|
2189
|
+
if (this.frostProtectionSupport && frostProtection.Enabled !== null) {
|
|
2190
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.Active, frostProtection.Enabled);
|
|
2191
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.CurrentHeaterCoolerState, frostProtection.Active ? 2 : 1);
|
|
2192
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.TargetHeaterCoolerState, frostProtection.Active ? 2 : 1);
|
|
2193
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.CurrentTemperature, roomTemperature);
|
|
2194
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.CoolingThresholdTemperature, frostProtection.Max);
|
|
2195
|
+
this.frostProtectionControlService?.updateCharacteristic(Characteristic.HeatingThresholdTemperature, frostProtection.Min);
|
|
2196
|
+
this.frostProtectionControlSensorService?.updateCharacteristic(Characteristic.ContactSensorState, frostProtection.Enabled);
|
|
2197
|
+
this.frostProtectionSensorService?.updateCharacteristic(Characteristic.ContactSensorState, frostProtection.Active);
|
|
2127
2198
|
}
|
|
2128
2199
|
|
|
2129
2200
|
//holiday mode
|
package/src/functions.js
CHANGED
|
@@ -237,6 +237,48 @@ class Functions extends EventEmitter {
|
|
|
237
237
|
})
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
|
+
|
|
241
|
+
async normalizeFrostProtection(min, max) {
|
|
242
|
+
// Clamp to allowed ranges
|
|
243
|
+
min = Math.min(Math.max(min, 4), 14);
|
|
244
|
+
max = Math.min(Math.max(max, 6), 16);
|
|
245
|
+
|
|
246
|
+
// Ensure difference of at least 2 degrees
|
|
247
|
+
if (max - min < 2) {
|
|
248
|
+
// Try increasing max if possible
|
|
249
|
+
const newMax = min + 2;
|
|
250
|
+
if (newMax <= 18) {
|
|
251
|
+
max = newMax;
|
|
252
|
+
} else {
|
|
253
|
+
// Otherwise lower min
|
|
254
|
+
const newMin = max - 2;
|
|
255
|
+
min = Math.max(newMin, 4);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { min, max };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async normalizeOverheatProtection(min, max) {
|
|
263
|
+
// Clamp to allowed ranges
|
|
264
|
+
min = Math.min(Math.max(min, 31), 38);
|
|
265
|
+
max = Math.min(Math.max(max, 33), 40);
|
|
266
|
+
|
|
267
|
+
// Ensure difference of at least 2 degrees
|
|
268
|
+
if (max - min < 2) {
|
|
269
|
+
// Try increasing max if possible
|
|
270
|
+
const newMax = min + 2;
|
|
271
|
+
if (newMax <= 18) {
|
|
272
|
+
max = newMax;
|
|
273
|
+
} else {
|
|
274
|
+
// Otherwise lower min
|
|
275
|
+
const newMin = max - 2;
|
|
276
|
+
min = Math.max(newMin, 4);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { min, max };
|
|
281
|
+
}
|
|
240
282
|
}
|
|
241
283
|
|
|
242
284
|
export default Functions
|
package/src/melcloudata.js
CHANGED
|
@@ -240,20 +240,22 @@ class MelCloudAta extends EventEmitter {
|
|
|
240
240
|
case "melcloudhome":
|
|
241
241
|
switch (flag) {
|
|
242
242
|
case 'frostprotection':
|
|
243
|
+
let { frostMin, frostMax } = await this.functions.normalizeFrostProtection(deviceData.FrostProtection.Min, deviceData.FrostProtection.Max);
|
|
243
244
|
payload = {
|
|
244
245
|
enabled: deviceData.FrostProtection.Enabled,
|
|
245
|
-
min:
|
|
246
|
-
max:
|
|
246
|
+
min: frostMin,
|
|
247
|
+
max: frostMax,
|
|
247
248
|
units: { ATA: [deviceData.DeviceID] }
|
|
248
249
|
};
|
|
249
250
|
method = 'POST';
|
|
250
251
|
path = ApiUrlsHome.PostProtectionFrost;
|
|
251
252
|
break;
|
|
252
253
|
case 'overheatprotection':
|
|
254
|
+
let { overMin, overMax } = await this.functions.normalizeOverheatProtection(deviceData.OverheatProtection.Min, deviceData.OverheatProtection.Max);
|
|
253
255
|
payload = {
|
|
254
256
|
enabled: deviceData.OverheatProtection.Enabled,
|
|
255
|
-
min:
|
|
256
|
-
max:
|
|
257
|
+
min: overMin,
|
|
258
|
+
max: overMax,
|
|
257
259
|
units: { ATA: [deviceData.DeviceID] }
|
|
258
260
|
};
|
|
259
261
|
method = 'POST';
|