homebridge-melcloud-control 3.9.0-beta.17 → 3.9.0-beta.19
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/homebridge-ui/public/index.html +94 -184
- package/package.json +1 -1
|
@@ -37,11 +37,12 @@
|
|
|
37
37
|
|
|
38
38
|
<div class="mb-3 position-relative">
|
|
39
39
|
<label for="passwd" class="form-label">Password</label>
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
<div class="input-group">
|
|
41
|
+
<input id="passwd" type="password" class="form-control" autocomplete="off" required>
|
|
42
|
+
<button type="button" id="togglePasswd" class="btn btn-outline-secondary">
|
|
43
|
+
<i class="fas fa-eye"></i>
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
45
46
|
</div>
|
|
46
47
|
|
|
47
48
|
<div class="mb-3">
|
|
@@ -94,60 +95,56 @@
|
|
|
94
95
|
homebridge.showSchemaForm();
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
97
|
-
this.configButtonState = false;
|
|
98
98
|
|
|
99
|
-
const accountsCount = pluginConfig[0].accounts.length;
|
|
100
99
|
this.deviceIndex = 0;
|
|
100
|
+
|
|
101
|
+
const accountsCount = pluginConfig[0].accounts.length;
|
|
101
102
|
for (let i = 0; i < accountsCount; i++) {
|
|
102
103
|
const button = document.createElement("button");
|
|
103
|
-
button.
|
|
104
|
-
button.
|
|
105
|
-
button.
|
|
104
|
+
button.type = "button";
|
|
105
|
+
button.id = `button${i}`;
|
|
106
|
+
button.className = "btn btn-primary";
|
|
106
107
|
button.style.textTransform = 'none';
|
|
107
108
|
button.innerText = pluginConfig[0].accounts[i].name;
|
|
108
109
|
document.getElementById("accountButton").appendChild(button);
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
button.addEventListener('click', async () => {
|
|
111
112
|
for (let j = 0; j < accountsCount; j++) {
|
|
112
|
-
j === i
|
|
113
|
-
? document.getElementById(`button${j}`).setAttribute("class", "btn btn-secondary")
|
|
114
|
-
: document.getElementById(`button${j}`).setAttribute("class", "btn btn-primary");
|
|
113
|
+
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
|
|
118
|
-
document.getElementById('
|
|
119
|
-
document.getElementById('
|
|
120
|
-
document.getElementById('
|
|
121
|
-
document.getElementById('
|
|
116
|
+
const acc = pluginConfig[0].accounts[i];
|
|
117
|
+
document.getElementById('accountName').innerText = acc.name || '';
|
|
118
|
+
document.getElementById('name').value = acc.name || '';
|
|
119
|
+
document.getElementById('user').value = acc.user || '';
|
|
120
|
+
document.getElementById('passwd').value = acc.passwd || '';
|
|
121
|
+
document.getElementById('language').value = acc.language || '';
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
document.getElementById('logIn').disabled = !accountConfigured;
|
|
123
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
125
124
|
|
|
126
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
127
125
|
this.deviceIndex = i;
|
|
128
|
-
});
|
|
129
|
-
if (i === accountsCount - 1) {
|
|
130
|
-
document.getElementById(`button0`).click();
|
|
131
126
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
132
|
-
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (i === accountsCount - 1) document.getElementById(`button0`).click();
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
document.getElementById('melCloudAccount').style.display = 'block';
|
|
136
133
|
|
|
137
134
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
138
|
-
pluginConfig[0].accounts[this.deviceIndex]
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
const acc = pluginConfig[0].accounts[this.deviceIndex];
|
|
136
|
+
acc.name = document.querySelector('#name').value;
|
|
137
|
+
acc.user = document.querySelector('#user').value;
|
|
138
|
+
acc.passwd = document.querySelector('#passwd').value;
|
|
139
|
+
acc.language = document.querySelector('#language').value;
|
|
142
140
|
|
|
143
|
-
|
|
144
|
-
document.getElementById('logIn').disabled = !accountConfigured;
|
|
141
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
145
142
|
|
|
146
143
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
147
144
|
await homebridge.savePluginConfig(pluginConfig);
|
|
148
145
|
});
|
|
149
146
|
|
|
150
|
-
// Config
|
|
147
|
+
// --- Config Button Toggle ---
|
|
151
148
|
const configButton = document.getElementById('configButton');
|
|
152
149
|
let configButtonState = false;
|
|
153
150
|
configButton.addEventListener('click', () => {
|
|
@@ -156,28 +153,30 @@
|
|
|
156
153
|
configButton.className = configButtonState ? 'btn btn-primary' : 'btn btn-secondary';
|
|
157
154
|
});
|
|
158
155
|
|
|
159
|
-
//
|
|
156
|
+
// --- Improved Show/Hide Password ---
|
|
160
157
|
const passwdInput = document.getElementById('passwd');
|
|
161
158
|
const togglePassword = document.getElementById('togglePassword');
|
|
162
159
|
|
|
163
160
|
togglePassword.addEventListener('click', () => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
const hidden = passwdInput.type === 'password';
|
|
162
|
+
passwdInput.type = hidden ? 'text' : 'password';
|
|
163
|
+
togglePassword.innerHTML = `<i class="fas fa-${hidden ? 'eye-slash' : 'eye'}"></i>`;
|
|
164
|
+
togglePassword.dataset.clicked = hidden; // remember permanent toggle
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
togglePassword.addEventListener('mouseenter', () => {
|
|
168
|
+
passwdInput.type = 'text';
|
|
169
|
+
togglePassword.innerHTML = `<i class="fas fa-eye-slash"></i>`;
|
|
171
170
|
});
|
|
172
171
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
togglePassword.click();
|
|
172
|
+
togglePassword.addEventListener('mouseleave', () => {
|
|
173
|
+
if (!togglePassword.dataset.clicked || togglePassword.dataset.clicked === 'false') {
|
|
174
|
+
passwdInput.type = 'password';
|
|
175
|
+
togglePassword.innerHTML = `<i class="fas fa-eye"></i>`;
|
|
178
176
|
}
|
|
179
177
|
});
|
|
180
178
|
|
|
179
|
+
// --- Device Handling & Login Logic ---
|
|
181
180
|
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
182
181
|
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
183
182
|
const removedDevices = [];
|
|
@@ -194,181 +193,92 @@
|
|
|
194
193
|
function updateInfo(id, text, color) {
|
|
195
194
|
const el = document.getElementById(id);
|
|
196
195
|
if (el) {
|
|
197
|
-
el.
|
|
196
|
+
el.innerText = text;
|
|
198
197
|
el.style.color = color;
|
|
199
198
|
}
|
|
200
199
|
}
|
|
201
200
|
|
|
202
201
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
203
202
|
homebridge.showSpinner();
|
|
204
|
-
document.getElementById(`logIn`).
|
|
203
|
+
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
205
204
|
updateInfo('info', 'Connecting...', 'yellow');
|
|
206
205
|
|
|
207
206
|
try {
|
|
208
|
-
const
|
|
209
|
-
const {
|
|
210
|
-
const payload = { accountName, user, passwd, language };
|
|
207
|
+
const acc = pluginConfig[0].accounts[this.deviceIndex];
|
|
208
|
+
const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language };
|
|
211
209
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
212
210
|
|
|
211
|
+
// Initialize devices arrays
|
|
213
212
|
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
for (const deviceInMelcloud of devicesInMelCloud) {
|
|
217
|
-
switch (deviceInMelcloud.Type) {
|
|
218
|
-
case 0: devicesByTypeInMelCloud.ata.push(deviceInMelcloud); break;
|
|
219
|
-
case 1: devicesByTypeInMelCloud.atw.push(deviceInMelcloud); break;
|
|
220
|
-
case 3: devicesByTypeInMelCloud.erv.push(deviceInMelcloud); break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
213
|
+
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
223
214
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const removedAta = removeStaleDevices(ataDevicesInConfig, devicesByTypeInMelCloud.ata);
|
|
229
|
-
const removedAtw = removeStaleDevices(atwDevicesInConfig, devicesByTypeInMelCloud.atw);
|
|
230
|
-
const removedErv = removeStaleDevices(ervDevicesInConfig, devicesByTypeInMelCloud.erv);
|
|
231
|
-
|
|
232
|
-
// === Handle ATA devices ===
|
|
233
|
-
devicesByTypeInMelCloud.ata.forEach(device => {
|
|
234
|
-
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
235
|
-
const deviceAta = {
|
|
236
|
-
id: deviceId,
|
|
237
|
-
type: deviceType,
|
|
238
|
-
typeString: "Air Conditioner",
|
|
239
|
-
name: deviceName,
|
|
240
|
-
displayMode: 1,
|
|
241
|
-
heatDryFanMode: 1,
|
|
242
|
-
coolDryFanMode: 1,
|
|
243
|
-
autoDryFanMode: 1,
|
|
244
|
-
temperatureSensor: false,
|
|
245
|
-
temperatureSensorOutdoor: false,
|
|
246
|
-
presets: [],
|
|
247
|
-
buttonsSensors: []
|
|
248
|
-
};
|
|
249
|
-
if (!ataDevicesInConfig.some(d => d.id === deviceId)) {
|
|
250
|
-
ataDevicesInConfig.push(deviceAta);
|
|
251
|
-
newDevices.ata.push(deviceAta);
|
|
252
|
-
}
|
|
253
|
-
devicePresets.forEach(preset => {
|
|
254
|
-
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
255
|
-
preset.id = presetId;
|
|
256
|
-
preset.name = presetName;
|
|
257
|
-
preset.displayType = 0;
|
|
258
|
-
preset.namePrefix = false;
|
|
259
|
-
const ataDevice = ataDevicesInConfig.find(d => d.id === deviceId);
|
|
260
|
-
if (ataDevice && !ataDevice.presets.some(p => p.id === presetId)) {
|
|
261
|
-
ataDevice.presets.push(preset);
|
|
262
|
-
newDevices.ataPresets.push(preset);
|
|
263
|
-
}
|
|
264
|
-
});
|
|
215
|
+
devicesInMelCloud.forEach(d => {
|
|
216
|
+
if (d.Type === 0) devicesByType.ata.push(d);
|
|
217
|
+
if (d.Type === 1) devicesByType.atw.push(d);
|
|
218
|
+
if (d.Type === 3) devicesByType.erv.push(d);
|
|
265
219
|
});
|
|
266
220
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
buttonsSensors: []
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
newDevices.atw.push(deviceAtw);
|
|
284
|
-
}
|
|
285
|
-
devicePresets.forEach(preset => {
|
|
286
|
-
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
287
|
-
preset.id = presetId;
|
|
288
|
-
preset.name = presetName;
|
|
289
|
-
preset.displayType = 0;
|
|
290
|
-
preset.namePrefix = false;
|
|
291
|
-
const atwDevice = atwDevicesInConfig.find(d => d.id === deviceId);
|
|
292
|
-
if (atwDevice && !atwDevice.presets.some(p => p.id === presetId)) {
|
|
293
|
-
atwDevice.presets.push(preset);
|
|
294
|
-
newDevices.atwPresets.push(preset);
|
|
221
|
+
acc.ataDevices ??= [];
|
|
222
|
+
acc.atwDevices ??= [];
|
|
223
|
+
acc.ervDevices ??= [];
|
|
224
|
+
|
|
225
|
+
const removedAta = removeStaleDevices(acc.ataDevices, devicesByType.ata);
|
|
226
|
+
const removedAtw = removeStaleDevices(acc.atwDevices, devicesByType.atw);
|
|
227
|
+
const removedErv = removeStaleDevices(acc.ervDevices, devicesByType.erv);
|
|
228
|
+
|
|
229
|
+
// Function to handle device & presets
|
|
230
|
+
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
231
|
+
devicesInCloud.forEach(device => {
|
|
232
|
+
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
233
|
+
const devObj = { id, type, typeString, name, displayMode: 1, presets: [], buttonsSensors: [] };
|
|
234
|
+
if (!devicesInConfig.some(d => d.id === id)) {
|
|
235
|
+
devicesInConfig.push(devObj);
|
|
236
|
+
newArr.push(devObj);
|
|
295
237
|
}
|
|
238
|
+
presets.forEach(p => {
|
|
239
|
+
p.id = p.ID;
|
|
240
|
+
p.name = p.NumberDescription;
|
|
241
|
+
p.displayType = 0;
|
|
242
|
+
p.namePrefix = false;
|
|
243
|
+
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
244
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.ID)) {
|
|
245
|
+
devConfig.presets.push(p);
|
|
246
|
+
newPresets.push(p);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
296
249
|
});
|
|
297
|
-
}
|
|
250
|
+
};
|
|
298
251
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const deviceErv = {
|
|
303
|
-
id: deviceId,
|
|
304
|
-
type: deviceType,
|
|
305
|
-
typeString: "ERV",
|
|
306
|
-
name: deviceName,
|
|
307
|
-
displayMode: 1,
|
|
308
|
-
temperatureSensor: false,
|
|
309
|
-
presets: [],
|
|
310
|
-
buttonsSensors: []
|
|
311
|
-
};
|
|
312
|
-
if (!ervDevicesInConfig.some(d => d.id === deviceId)) {
|
|
313
|
-
ervDevicesInConfig.push(deviceErv);
|
|
314
|
-
newDevices.erv.push(deviceErv);
|
|
315
|
-
}
|
|
316
|
-
devicePresets.forEach(preset => {
|
|
317
|
-
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
318
|
-
preset.id = presetId;
|
|
319
|
-
preset.name = presetName;
|
|
320
|
-
preset.displayType = 0;
|
|
321
|
-
preset.namePrefix = false;
|
|
322
|
-
const ervDevice = ervDevicesInConfig.find(d => d.id === deviceId);
|
|
323
|
-
if (ervDevice && !ervDevice.presets.some(p => p.id === presetId)) {
|
|
324
|
-
ervDevice.presets.push(preset);
|
|
325
|
-
newDevices.ervPresets.push(preset);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// --- Display Info Section ---
|
|
331
|
-
const textAta = `ATA: ${newDevices.ata.length}`;
|
|
332
|
-
const textAtw = `ATW: ${newDevices.atw.length}`;
|
|
333
|
-
const textErv = `ERV: ${newDevices.erv.length}`;
|
|
334
|
-
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
|
|
335
|
-
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
|
|
336
|
-
const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`;
|
|
337
|
-
const textRemovedAta = `ATA: ${removedAta.length}`;
|
|
338
|
-
const textRemovedAtw = `ATW: ${removedAtw.length}`;
|
|
339
|
-
const textRemovedErv = `ERV: ${removedErv.length}`;
|
|
252
|
+
handleDevices(devicesByType.ata, acc.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
253
|
+
handleDevices(devicesByType.atw, acc.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
254
|
+
handleDevices(devicesByType.erv, acc.ervDevices, "ERV", newDevices.erv, newDevices.ervPresets);
|
|
340
255
|
|
|
256
|
+
// Display summary
|
|
341
257
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
342
258
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
343
259
|
const removedCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
344
260
|
|
|
345
|
-
if (newDevicesCount
|
|
261
|
+
if (!newDevicesCount && !newPresetsCount && !removedCount) {
|
|
346
262
|
updateInfo('info', 'No changes detected.', 'white');
|
|
347
263
|
} else {
|
|
348
|
-
if (newDevicesCount
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
if (newPresetsCount > 0) {
|
|
352
|
-
updateInfo('info1', `Found new presets: ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
353
|
-
}
|
|
354
|
-
if (removedCount > 0) {
|
|
355
|
-
updateInfo('info2', `Removed devices: ${textRemovedAta}, ${textRemovedAtw}, ${textRemovedErv}.`, 'orange');
|
|
356
|
-
}
|
|
264
|
+
if (newDevicesCount) updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
265
|
+
if (newPresetsCount) updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
266
|
+
if (removedCount) updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
357
267
|
}
|
|
358
268
|
|
|
359
269
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
360
270
|
await homebridge.savePluginConfig(pluginConfig);
|
|
361
|
-
document.getElementById('logIn').
|
|
362
|
-
homebridge.hideSpinner();
|
|
271
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
363
272
|
|
|
364
273
|
} catch (error) {
|
|
365
274
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
366
275
|
updateInfo('info1', `Error: ${error}`, 'red');
|
|
367
|
-
document.getElementById('logIn').
|
|
276
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
368
277
|
} finally {
|
|
369
278
|
homebridge.hideSpinner();
|
|
370
279
|
}
|
|
371
280
|
});
|
|
281
|
+
|
|
372
282
|
})();
|
|
373
283
|
</script>
|
|
374
284
|
</body>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "3.9.0-beta.
|
|
4
|
+
"version": "3.9.0-beta.19",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|