homebridge-melcloud-control 4.3.16-beta.0 → 4.3.16-beta.10
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 +18 -0
- package/package.json +3 -3
- package/src/deviceata.js +234 -105
- package/src/deviceatw.js +145 -78
- package/src/deviceerv.js +56 -61
- package/src/functions.js +49 -7
- package/src/melcloudata.js +6 -4
- package/src/melcloudhome.js +5 -6
- package/src/mqtt.js +80 -33
package/src/deviceatw.js
CHANGED
|
@@ -118,33 +118,31 @@ class DeviceAtw extends EventEmitter {
|
|
|
118
118
|
if (restFulEnabled) {
|
|
119
119
|
try {
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
this.restFul1 = new RestFul({
|
|
122
|
+
port: this.restFul.port,
|
|
123
|
+
logWarn: this.logWarn,
|
|
124
|
+
logDebug: this.logDebug
|
|
125
|
+
})
|
|
126
|
+
.on('connected', (message) => {
|
|
127
|
+
this.restFulConnected = true;
|
|
128
|
+
this.emit('success', message);
|
|
126
129
|
})
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
})
|
|
144
|
-
.on('error', (error) => {
|
|
145
|
-
this.emit('error', error);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
130
|
+
.on('set', async (key, value) => {
|
|
131
|
+
try {
|
|
132
|
+
await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.emit('warn', error);
|
|
135
|
+
};
|
|
136
|
+
})
|
|
137
|
+
.on('debug', (debug) => {
|
|
138
|
+
this.emit('debug', debug);
|
|
139
|
+
})
|
|
140
|
+
.on('warn', (warn) => {
|
|
141
|
+
this.emit('warn', warn);
|
|
142
|
+
})
|
|
143
|
+
.on('error', (error) => {
|
|
144
|
+
this.emit('error', error);
|
|
145
|
+
});
|
|
148
146
|
} catch (error) {
|
|
149
147
|
if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
150
148
|
};
|
|
@@ -154,41 +152,39 @@ class DeviceAtw extends EventEmitter {
|
|
|
154
152
|
const mqttEnabled = this.mqtt.enable || false;
|
|
155
153
|
if (mqttEnabled) {
|
|
156
154
|
try {
|
|
157
|
-
|
|
158
|
-
this.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
155
|
+
this.mqtt1 = new Mqtt({
|
|
156
|
+
host: this.mqtt.host,
|
|
157
|
+
port: this.mqtt.port || 1883,
|
|
158
|
+
clientId: this.mqtt.clientId ? `melcloud_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `melcloud_${Math.random().toString(16).slice(3)}`,
|
|
159
|
+
prefix: this.mqtt.prefix ? `melcloud/${this.mqtt.prefix}/${this.deviceTypeString}/${this.deviceName}` : `melcloud/${this.deviceTypeString}/${this.deviceName}`,
|
|
160
|
+
user: this.mqtt.auth?.user,
|
|
161
|
+
passwd: this.mqtt.auth?.passwd,
|
|
162
|
+
logWarn: this.logWarn,
|
|
163
|
+
logDebug: this.logDebug
|
|
164
|
+
})
|
|
165
|
+
.on('connected', (message) => {
|
|
166
|
+
this.mqttConnected = true;
|
|
167
|
+
this.emit('success', message);
|
|
167
168
|
})
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
this.
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
.
|
|
186
|
-
|
|
187
|
-
})
|
|
188
|
-
.on('error', (error) => {
|
|
189
|
-
this.emit('error', error);
|
|
190
|
-
});
|
|
191
|
-
}
|
|
169
|
+
.on('subscribed', (message) => {
|
|
170
|
+
this.emit('success', message);
|
|
171
|
+
})
|
|
172
|
+
.on('set', async (key, value) => {
|
|
173
|
+
try {
|
|
174
|
+
await this.setOverExternalIntegration('MQTT', this.deviceData, key, value);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.emit('warn', error);
|
|
177
|
+
};
|
|
178
|
+
})
|
|
179
|
+
.on('debug', (debug) => {
|
|
180
|
+
this.emit('debug', debug);
|
|
181
|
+
})
|
|
182
|
+
.on('warn', (warn) => {
|
|
183
|
+
this.emit('warn', warn);
|
|
184
|
+
})
|
|
185
|
+
.on('error', (error) => {
|
|
186
|
+
this.emit('error', error);
|
|
187
|
+
});
|
|
192
188
|
} catch (error) {
|
|
193
189
|
if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
|
|
194
190
|
};
|
|
@@ -1058,27 +1054,95 @@ class DeviceAtw extends EventEmitter {
|
|
|
1058
1054
|
}
|
|
1059
1055
|
|
|
1060
1056
|
//frost protection
|
|
1061
|
-
if (this.frostProtectionSupport && this.accessory.
|
|
1057
|
+
if (this.frostProtectionSupport && this.accessory.frostProtection.Enabled !== null) {
|
|
1062
1058
|
//control
|
|
1063
1059
|
if (this.logDebug) this.emit('debug', `Prepare frost protection control service`);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
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)
|
|
1068
1064
|
.onGet(async () => {
|
|
1069
|
-
const state = this.accessory.
|
|
1065
|
+
const state = this.accessory.frostProtection.Enabled;
|
|
1070
1066
|
return state;
|
|
1071
1067
|
})
|
|
1072
1068
|
.onSet(async (state) => {
|
|
1073
1069
|
try {
|
|
1074
|
-
deviceData.FrostProtection.Enabled = state;
|
|
1070
|
+
deviceData.FrostProtection.Enabled = state ? true : false;
|
|
1075
1071
|
if (this.logInfo) this.emit('info', `Frost protection: ${state ? 'Enabled' : 'Disabled'}`);
|
|
1076
1072
|
await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'frostprotection');
|
|
1077
1073
|
} catch (error) {
|
|
1078
1074
|
if (this.logWarn) this.emit('warn', `Set frost protection error: ${error}`);
|
|
1079
1075
|
};
|
|
1080
1076
|
});
|
|
1081
|
-
|
|
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);
|
|
1082
1146
|
|
|
1083
1147
|
if (this.logDebug) this.emit('debug', `Prepare frost protection control sensor service`);
|
|
1084
1148
|
this.frostProtectionControlSensorService = new Service.ContactSensor(`${serviceName} Frost Protection Control`, `frostProtectionControlSensorService${deviceId}`);
|
|
@@ -1086,7 +1150,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1086
1150
|
this.frostProtectionControlSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection Control`);
|
|
1087
1151
|
this.frostProtectionControlSensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
1088
1152
|
.onGet(async () => {
|
|
1089
|
-
const state = this.accessory.
|
|
1153
|
+
const state = this.accessory.frostProtection.Enabled;
|
|
1090
1154
|
return state;
|
|
1091
1155
|
})
|
|
1092
1156
|
accessory.addService(this.frostProtectionControlSensorService);
|
|
@@ -1098,7 +1162,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1098
1162
|
this.frostProtectionSensorService.setCharacteristic(Characteristic.ConfiguredName, `${accessoryName} Frost Protection`);
|
|
1099
1163
|
this.frostProtectionSensorService.getCharacteristic(Characteristic.ContactSensorState)
|
|
1100
1164
|
.onGet(async () => {
|
|
1101
|
-
const state = this.accessory.
|
|
1165
|
+
const state = this.accessory.frostProtection.Active;
|
|
1102
1166
|
return state;
|
|
1103
1167
|
})
|
|
1104
1168
|
accessory.addService(this.frostProtectionSensorService);
|
|
@@ -1613,8 +1677,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1613
1677
|
const holidayModeActive = deviceData.HolidayMode?.Active ?? false;
|
|
1614
1678
|
|
|
1615
1679
|
//protection
|
|
1616
|
-
const
|
|
1617
|
-
const frostProtectionActive = deviceData.FrostProtection?.Active ?? false;
|
|
1680
|
+
const frostProtection = deviceData.FrostProtection ?? {};
|
|
1618
1681
|
|
|
1619
1682
|
//device info
|
|
1620
1683
|
const supportsStanbyMode = deviceData.Device[supportStandbyKey];
|
|
@@ -1723,8 +1786,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1723
1786
|
temperatureUnit: TemperatureDisplayUnits[this.accountInfo.useFahrenheit ? 1 : 0],
|
|
1724
1787
|
isConnected: isConnected,
|
|
1725
1788
|
isInError: isInError,
|
|
1726
|
-
|
|
1727
|
-
frostProtectionActive: frostProtectionActive,
|
|
1789
|
+
frostProtection: frostProtection,
|
|
1728
1790
|
scheduleEnabled: scheduleEnabled,
|
|
1729
1791
|
holidayModeEnabled: holidayModeEnabled,
|
|
1730
1792
|
holidayModeActive: holidayModeActive,
|
|
@@ -2124,10 +2186,15 @@ class DeviceAtw extends EventEmitter {
|
|
|
2124
2186
|
this.errorService?.updateCharacteristic(Characteristic.ContactSensorState, isInError);
|
|
2125
2187
|
|
|
2126
2188
|
//frost protection
|
|
2127
|
-
if (this.frostProtectionSupport &&
|
|
2128
|
-
this.frostProtectionControlService?.updateCharacteristic(Characteristic.
|
|
2129
|
-
this.
|
|
2130
|
-
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);
|
|
2131
2198
|
}
|
|
2132
2199
|
|
|
2133
2200
|
//holiday mode
|
package/src/deviceerv.js
CHANGED
|
@@ -109,34 +109,31 @@ class DeviceErv extends EventEmitter {
|
|
|
109
109
|
const restFulEnabled = this.restFul.enable || false;
|
|
110
110
|
if (restFulEnabled) {
|
|
111
111
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
this.restFul1 = new RestFul({
|
|
113
|
+
port: this.restFul.port,
|
|
114
|
+
logWarn: this.logWarn,
|
|
115
|
+
logDebug: this.logDebug
|
|
116
|
+
})
|
|
117
|
+
.on('connected', (message) => {
|
|
118
|
+
this.restFulConnected = true;
|
|
119
|
+
this.emit('success', message);
|
|
118
120
|
})
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
})
|
|
136
|
-
.on('error', (error) => {
|
|
137
|
-
this.emit('error', error);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
121
|
+
.on('set', async (key, value) => {
|
|
122
|
+
try {
|
|
123
|
+
await this.setOverExternalIntegration('RESTFul', this.deviceData, key, value);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
this.emit('warn', error);
|
|
126
|
+
};
|
|
127
|
+
})
|
|
128
|
+
.on('debug', (debug) => {
|
|
129
|
+
this.emit('debug', debug);
|
|
130
|
+
})
|
|
131
|
+
.on('warn', (warn) => {
|
|
132
|
+
this.emit('warn', warn);
|
|
133
|
+
})
|
|
134
|
+
.on('error', (error) => {
|
|
135
|
+
this.emit('error', error);
|
|
136
|
+
});
|
|
140
137
|
} catch (error) {
|
|
141
138
|
if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
142
139
|
};
|
|
@@ -146,41 +143,39 @@ class DeviceErv extends EventEmitter {
|
|
|
146
143
|
const mqttEnabled = this.mqtt.enable || false;
|
|
147
144
|
if (mqttEnabled) {
|
|
148
145
|
try {
|
|
149
|
-
|
|
150
|
-
this.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
this.mqtt1 = new Mqtt({
|
|
147
|
+
host: this.mqtt.host,
|
|
148
|
+
port: this.mqtt.port || 1883,
|
|
149
|
+
clientId: this.mqtt.clientId ? `melcloud_${this.mqtt.clientId}_${Math.random().toString(16).slice(3)}` : `melcloud_${Math.random().toString(16).slice(3)}`,
|
|
150
|
+
prefix: this.mqtt.prefix ? `melcloud/${this.mqtt.prefix}/${this.deviceTypeString}/${this.deviceName}` : `melcloud/${this.deviceTypeString}/${this.deviceName}`,
|
|
151
|
+
user: this.mqtt.auth?.user,
|
|
152
|
+
passwd: this.mqtt.auth?.passwd,
|
|
153
|
+
logWarn: this.logWarn,
|
|
154
|
+
logDebug: this.logDebug
|
|
155
|
+
})
|
|
156
|
+
.on('connected', (message) => {
|
|
157
|
+
this.mqttConnected = true;
|
|
158
|
+
this.emit('success', message);
|
|
159
159
|
})
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
.
|
|
178
|
-
|
|
179
|
-
})
|
|
180
|
-
.on('error', (error) => {
|
|
181
|
-
this.emit('error', error);
|
|
182
|
-
});
|
|
183
|
-
}
|
|
160
|
+
.on('subscribed', (message) => {
|
|
161
|
+
this.emit('success', message);
|
|
162
|
+
})
|
|
163
|
+
.on('set', async (key, value) => {
|
|
164
|
+
try {
|
|
165
|
+
await this.setOverExternalIntegration('MQTT', this.deviceData, key, value);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
this.emit('warn', error);
|
|
168
|
+
};
|
|
169
|
+
})
|
|
170
|
+
.on('debug', (debug) => {
|
|
171
|
+
this.emit('debug', debug);
|
|
172
|
+
})
|
|
173
|
+
.on('warn', (warn) => {
|
|
174
|
+
this.emit('warn', warn);
|
|
175
|
+
})
|
|
176
|
+
.on('error', (error) => {
|
|
177
|
+
this.emit('error', error);
|
|
178
|
+
});
|
|
184
179
|
} catch (error) {
|
|
185
180
|
if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
|
|
186
181
|
};
|
package/src/functions.js
CHANGED
|
@@ -56,7 +56,7 @@ class Functions extends EventEmitter {
|
|
|
56
56
|
|
|
57
57
|
async ensureChromiumInstalled() {
|
|
58
58
|
try {
|
|
59
|
-
//
|
|
59
|
+
// Detect OS
|
|
60
60
|
const { stdout: osOut } = await execPromise("uname -s");
|
|
61
61
|
const osName = osOut.trim();
|
|
62
62
|
const { stdout: archOut } = await execPromise("uname -m");
|
|
@@ -66,7 +66,7 @@ class Functions extends EventEmitter {
|
|
|
66
66
|
const isMac = osName === "Darwin";
|
|
67
67
|
const isLinux = osName === "Linux";
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Detect Docker
|
|
70
70
|
let isDocker = false;
|
|
71
71
|
try {
|
|
72
72
|
await access("/.dockerenv");
|
|
@@ -77,7 +77,7 @@ class Functions extends EventEmitter {
|
|
|
77
77
|
if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
|
|
78
78
|
} catch { }
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// macOS
|
|
81
81
|
if (isMac) {
|
|
82
82
|
const macCandidates = [
|
|
83
83
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
@@ -92,7 +92,7 @@ class Functions extends EventEmitter {
|
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//
|
|
95
|
+
// ARM / Raspberry Pi
|
|
96
96
|
if (isARM && isLinux) {
|
|
97
97
|
const armCandidates = [
|
|
98
98
|
"/usr/bin/chromium-browser",
|
|
@@ -132,7 +132,7 @@ class Functions extends EventEmitter {
|
|
|
132
132
|
return null;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
//
|
|
135
|
+
// QNAP / Entware
|
|
136
136
|
let entwareExists = false;
|
|
137
137
|
try {
|
|
138
138
|
await access("/opt/bin/opkg", fs.constants.X_OK);
|
|
@@ -147,7 +147,7 @@ class Functions extends EventEmitter {
|
|
|
147
147
|
} catch { }
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
//
|
|
150
|
+
// Synology DSM 7
|
|
151
151
|
const synoCandidates = [
|
|
152
152
|
"/var/packages/Chromium/target/usr/bin/chromium",
|
|
153
153
|
"/usr/local/chromium/bin/chromium"
|
|
@@ -159,7 +159,7 @@ class Functions extends EventEmitter {
|
|
|
159
159
|
} catch { }
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
//
|
|
162
|
+
// Linux x64
|
|
163
163
|
if (isLinux) {
|
|
164
164
|
const linuxCandidates = [
|
|
165
165
|
"/usr/bin/chromium",
|
|
@@ -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';
|
package/src/melcloudhome.js
CHANGED
|
@@ -228,7 +228,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
228
228
|
// Get Chromium path
|
|
229
229
|
let chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
230
230
|
|
|
231
|
-
//
|
|
231
|
+
// Fallback to Puppeteer's bundled Chromium
|
|
232
232
|
if (!chromiumPath) {
|
|
233
233
|
try {
|
|
234
234
|
const puppeteerPath = puppeteer.executablePath();
|
|
@@ -245,7 +245,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
//
|
|
248
|
+
// Verify Chromium executable
|
|
249
249
|
try {
|
|
250
250
|
const { stdout } = await execPromise(`"${chromiumPath}" --version`);
|
|
251
251
|
if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
|
|
@@ -277,7 +277,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
277
277
|
page.setDefaultTimeout(GLOBAL_TIMEOUT);
|
|
278
278
|
page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
|
|
279
279
|
|
|
280
|
-
//
|
|
280
|
+
// CDP session
|
|
281
281
|
const client = await page.createCDPSession();
|
|
282
282
|
await client.send('Network.enable')
|
|
283
283
|
client.on('Network.webSocketCreated', ({ url }) => {
|
|
@@ -311,7 +311,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
311
311
|
.on('open', () => {
|
|
312
312
|
this.socketConnected = true;
|
|
313
313
|
this.connecting = false;
|
|
314
|
-
if (this.logDebug) this.emit('debug', `Web Socket
|
|
314
|
+
if (this.logDebug) this.emit('debug', `Web Socket Connected`);
|
|
315
315
|
|
|
316
316
|
// heartbeat
|
|
317
317
|
this.heartbeat = setInterval(() => {
|
|
@@ -400,7 +400,6 @@ class MelCloudHome extends EventEmitter {
|
|
|
400
400
|
`__Secure-monitorandcontrolC2=${c2}`
|
|
401
401
|
].join('; ');
|
|
402
402
|
|
|
403
|
-
|
|
404
403
|
const userAgent = await page.evaluate(() => navigator.userAgent);
|
|
405
404
|
const headers = {
|
|
406
405
|
'Accept': '*/*',
|
|
@@ -424,7 +423,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
424
423
|
this.emit('client', this.client);
|
|
425
424
|
|
|
426
425
|
accountInfo.State = true;
|
|
427
|
-
accountInfo.Info = `Connect Success
|
|
426
|
+
accountInfo.Info = `Connect Success${this.socketConnected ? ', Web Socket Connected' : ''}`;
|
|
428
427
|
await this.functions.saveData(this.accountFile, accountInfo);
|
|
429
428
|
|
|
430
429
|
return accountInfo;
|