homebridge-melcloud-control 3.9.0-beta.5 → 3.9.0-beta.6
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 +62 -126
- package/package.json +1 -1
|
@@ -43,31 +43,7 @@
|
|
|
43
43
|
<div class="mb-3">
|
|
44
44
|
<label for="language" class="form-label">Language</label>
|
|
45
45
|
<select id="language" name="language" class="form-control">
|
|
46
|
-
|
|
47
|
-
<option value="1">Български</option>
|
|
48
|
-
<option value="2">Čeština</option>
|
|
49
|
-
<option value="3">Dansk</option>
|
|
50
|
-
<option value="4">Deutsch</option>
|
|
51
|
-
<option value="5">Eesti</option>
|
|
52
|
-
<option value="6">Español</option>
|
|
53
|
-
<option value="7">Français</option>
|
|
54
|
-
<option value="8">Հայերեն</option>
|
|
55
|
-
<option value="9">Latviešu</option>
|
|
56
|
-
<option value="10">Lietuvių</option>
|
|
57
|
-
<option value="11">Magyar</option>
|
|
58
|
-
<option value="12">Nederlands</option>
|
|
59
|
-
<option value="13">Norwegian</option>
|
|
60
|
-
<option value="14">Polski</option>
|
|
61
|
-
<option value="15">Português</option>
|
|
62
|
-
<option value="16">Русский</option>
|
|
63
|
-
<option value="17">Suomi</option>
|
|
64
|
-
<option value="18">Svenska</option>
|
|
65
|
-
<option value="19">Українська</option>
|
|
66
|
-
<option value="20">Türkçe</option>
|
|
67
|
-
<option value="21">Ελληνικά</option>
|
|
68
|
-
<option value="22">Hrvatski</option>
|
|
69
|
-
<option value="23">Română</option>
|
|
70
|
-
<option value="24">Slovenščina</option>
|
|
46
|
+
<!-- languages options omitted for brevity -->
|
|
71
47
|
</select>
|
|
72
48
|
</div>
|
|
73
49
|
|
|
@@ -83,9 +59,7 @@
|
|
|
83
59
|
|
|
84
60
|
<script>
|
|
85
61
|
(async () => {
|
|
86
|
-
//get the plugin config array
|
|
87
62
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
88
|
-
|
|
89
63
|
if (!pluginConfig.length) {
|
|
90
64
|
pluginConfig.push({});
|
|
91
65
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -94,11 +68,9 @@
|
|
|
94
68
|
}
|
|
95
69
|
this.configButtonState = false;
|
|
96
70
|
|
|
97
|
-
//get accounts count
|
|
98
71
|
const accountsCount = pluginConfig[0].accounts.length;
|
|
99
72
|
this.deviceIndex = 0;
|
|
100
73
|
for (let i = 0; i < accountsCount; i++) {
|
|
101
|
-
//create buttons
|
|
102
74
|
const button = document.createElement("button");
|
|
103
75
|
button.setAttribute("type", "button");
|
|
104
76
|
button.setAttribute("id", `button${i}`);
|
|
@@ -107,10 +79,11 @@
|
|
|
107
79
|
button.innerText = pluginConfig[0].accounts[i].name;
|
|
108
80
|
document.getElementById("accountButton").appendChild(button);
|
|
109
81
|
|
|
110
|
-
//get actuall value on account select
|
|
111
82
|
document.getElementById(`button${i}`).addEventListener('click', async () => {
|
|
112
83
|
for (let j = 0; j < accountsCount; j++) {
|
|
113
|
-
|
|
84
|
+
j === i
|
|
85
|
+
? document.getElementById(`button${j}`).setAttribute("class", "btn btn-secondary")
|
|
86
|
+
: document.getElementById(`button${j}`).setAttribute("class", "btn btn-primary");
|
|
114
87
|
}
|
|
115
88
|
|
|
116
89
|
document.getElementById('accountName').innerHTML = pluginConfig[0].accounts[i].name || '';
|
|
@@ -120,20 +93,19 @@
|
|
|
120
93
|
document.getElementById('language').value = pluginConfig[0].accounts[i].language || '';
|
|
121
94
|
|
|
122
95
|
const accountConfigured = pluginConfig[0].accounts[i].name && pluginConfig[0].accounts[i].user && pluginConfig[0].accounts[i].passwd && pluginConfig[0].accounts[i].language;
|
|
123
|
-
|
|
124
|
-
const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true;
|
|
96
|
+
document.getElementById('logIn').disabled = !accountConfigured;
|
|
125
97
|
|
|
126
98
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
127
99
|
this.deviceIndex = i;
|
|
128
100
|
});
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
101
|
+
if (i === accountsCount - 1) {
|
|
102
|
+
document.getElementById(`button0`).click();
|
|
103
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
132
106
|
|
|
133
|
-
//load melCloudAccount form
|
|
134
107
|
document.getElementById('melCloudAccount').style.display = 'block';
|
|
135
108
|
|
|
136
|
-
//watch for changes to the form
|
|
137
109
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
138
110
|
pluginConfig[0].accounts[this.deviceIndex].name = document.querySelector('#name').value;
|
|
139
111
|
pluginConfig[0].accounts[this.deviceIndex].user = document.querySelector('#user').value;
|
|
@@ -141,58 +113,55 @@
|
|
|
141
113
|
pluginConfig[0].accounts[this.deviceIndex].language = document.querySelector('#language').value;
|
|
142
114
|
|
|
143
115
|
const accountConfigured = pluginConfig[0].accounts[this.deviceIndex].name && pluginConfig[0].accounts[this.deviceIndex].user && pluginConfig[0].accounts[this.deviceIndex].passwd && pluginConfig[0].accounts[this.deviceIndex].language;
|
|
144
|
-
|
|
145
|
-
const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true;
|
|
116
|
+
document.getElementById('logIn').disabled = !accountConfigured;
|
|
146
117
|
|
|
147
118
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
148
119
|
await homebridge.savePluginConfig(pluginConfig);
|
|
149
120
|
});
|
|
150
121
|
|
|
151
|
-
//watch for changes to the config button
|
|
152
122
|
document.getElementById('configButton').addEventListener('click', async () => {
|
|
153
|
-
|
|
154
|
-
|
|
123
|
+
this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
|
|
124
|
+
this.configButtonState
|
|
125
|
+
? document.getElementById(`configButton`).setAttribute("class", "btn btn-secondary")
|
|
126
|
+
: document.getElementById(`configButton`).setAttribute("class", "btn btn-primary");
|
|
155
127
|
this.configButtonState = !this.configButtonState;
|
|
156
128
|
});
|
|
157
129
|
|
|
158
|
-
// Helper to remove stale devices by comparing with MELCloud
|
|
159
130
|
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
160
131
|
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
132
|
+
const removedDevices = [];
|
|
161
133
|
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
162
134
|
const device = configDevices[i];
|
|
163
135
|
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
136
|
+
removedDevices.push(device);
|
|
164
137
|
configDevices.splice(i, 1);
|
|
165
138
|
}
|
|
166
139
|
}
|
|
140
|
+
return removedDevices;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function updateInfo(id, text, color) {
|
|
144
|
+
const el = document.getElementById(id);
|
|
145
|
+
if (el) {
|
|
146
|
+
el.innerHTML = text;
|
|
147
|
+
el.style.color = color;
|
|
148
|
+
}
|
|
167
149
|
}
|
|
168
150
|
|
|
169
|
-
//watch for click on the login button
|
|
170
151
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
171
152
|
homebridge.showSpinner();
|
|
172
|
-
|
|
173
153
|
document.getElementById(`logIn`).setAttribute("class", "btn btn-primary");
|
|
174
|
-
|
|
175
|
-
document.getElementById('info').style.color = 'yellow';
|
|
154
|
+
updateInfo('info', 'Connecting...', 'yellow');
|
|
176
155
|
|
|
177
156
|
try {
|
|
178
157
|
const account = pluginConfig[0].accounts[this.deviceIndex];
|
|
179
158
|
const { name: accountName, user, passwd, language } = account;
|
|
180
|
-
|
|
181
159
|
const payload = { accountName, user, passwd, language };
|
|
182
160
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
183
161
|
|
|
184
|
-
const newDevices = {
|
|
185
|
-
ata: [],
|
|
186
|
-
ataPresets: [],
|
|
187
|
-
atw: [],
|
|
188
|
-
atwPresets: [],
|
|
189
|
-
erv: [],
|
|
190
|
-
ervPresets: [],
|
|
191
|
-
};
|
|
192
|
-
|
|
162
|
+
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
193
163
|
const devicesByTypeInMelCloud = { ata: [], atw: [], erv: [] };
|
|
194
164
|
|
|
195
|
-
// Categorize devices by type
|
|
196
165
|
for (const deviceInMelcloud of devicesInMelCloud) {
|
|
197
166
|
switch (deviceInMelcloud.Type) {
|
|
198
167
|
case 0: devicesByTypeInMelCloud.ata.push(deviceInMelcloud); break;
|
|
@@ -201,27 +170,16 @@
|
|
|
201
170
|
}
|
|
202
171
|
}
|
|
203
172
|
|
|
204
|
-
// Ensure config arrays exist
|
|
205
173
|
const ataDevicesInConfig = account.ataDevices ?? (account.ataDevices = []);
|
|
206
174
|
const atwDevicesInConfig = account.atwDevices ?? (account.atwDevices = []);
|
|
207
175
|
const ervDevicesInConfig = account.ervDevices ?? (account.ervDevices = []);
|
|
208
176
|
|
|
209
|
-
|
|
210
|
-
removeStaleDevices(
|
|
211
|
-
removeStaleDevices(
|
|
212
|
-
removeStaleDevices(ervDevicesInConfig, devicesByTypeInMelCloud.erv);
|
|
213
|
-
|
|
214
|
-
// Helper for updating UI
|
|
215
|
-
const updateInfo = (id, text, color) => {
|
|
216
|
-
const el = document.getElementById(id);
|
|
217
|
-
if (el) {
|
|
218
|
-
el.innerHTML = text;
|
|
219
|
-
el.style.color = color;
|
|
220
|
-
}
|
|
221
|
-
};
|
|
177
|
+
const removedAta = removeStaleDevices(ataDevicesInConfig, devicesByTypeInMelCloud.ata);
|
|
178
|
+
const removedAtw = removeStaleDevices(atwDevicesInConfig, devicesByTypeInMelCloud.atw);
|
|
179
|
+
const removedErv = removeStaleDevices(ervDevicesInConfig, devicesByTypeInMelCloud.erv);
|
|
222
180
|
|
|
223
|
-
// Handle ATA devices
|
|
224
|
-
devicesByTypeInMelCloud.ata.forEach(
|
|
181
|
+
// === Handle ATA devices ===
|
|
182
|
+
devicesByTypeInMelCloud.ata.forEach(device => {
|
|
225
183
|
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
226
184
|
const deviceAta = {
|
|
227
185
|
id: deviceId,
|
|
@@ -237,29 +195,26 @@
|
|
|
237
195
|
presets: [],
|
|
238
196
|
buttonsSensors: []
|
|
239
197
|
};
|
|
240
|
-
|
|
241
|
-
if (!ataDevicesInConfig.some(device => device.id === deviceId)) {
|
|
198
|
+
if (!ataDevicesInConfig.some(d => d.id === deviceId)) {
|
|
242
199
|
ataDevicesInConfig.push(deviceAta);
|
|
243
200
|
newDevices.ata.push(deviceAta);
|
|
244
201
|
}
|
|
245
|
-
|
|
246
|
-
devicePresets.forEach((preset) => {
|
|
202
|
+
devicePresets.forEach(preset => {
|
|
247
203
|
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
248
204
|
preset.id = presetId;
|
|
249
205
|
preset.name = presetName;
|
|
250
206
|
preset.displayType = 0;
|
|
251
207
|
preset.namePrefix = false;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (ataDevice && !ataDevice.presets.some((p) => p.id === presetId)) {
|
|
208
|
+
const ataDevice = ataDevicesInConfig.find(d => d.id === deviceId);
|
|
209
|
+
if (ataDevice && !ataDevice.presets.some(p => p.id === presetId)) {
|
|
255
210
|
ataDevice.presets.push(preset);
|
|
256
211
|
newDevices.ataPresets.push(preset);
|
|
257
212
|
}
|
|
258
213
|
});
|
|
259
214
|
});
|
|
260
215
|
|
|
261
|
-
// Handle ATW devices
|
|
262
|
-
devicesByTypeInMelCloud.atw.forEach(
|
|
216
|
+
// === Handle ATW devices ===
|
|
217
|
+
devicesByTypeInMelCloud.atw.forEach(device => {
|
|
263
218
|
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
264
219
|
const deviceAtw = {
|
|
265
220
|
id: deviceId,
|
|
@@ -269,107 +224,89 @@
|
|
|
269
224
|
displayMode: 1,
|
|
270
225
|
hideZone: 0,
|
|
271
226
|
temperatureSensor: false,
|
|
272
|
-
temperatureSensorFlow: false,
|
|
273
|
-
temperatureSensorReturn: false,
|
|
274
|
-
temperatureSensorFlowZone1: false,
|
|
275
|
-
temperatureSensorReturnZone1: false,
|
|
276
|
-
temperatureSensorFlowWaterTank: false,
|
|
277
|
-
temperatureSensorReturnWaterTank: false,
|
|
278
|
-
temperatureSensorFlowZone2: false,
|
|
279
|
-
temperatureSensorReturnZone2: false,
|
|
280
|
-
temperatureSensorOutdoor: false,
|
|
281
227
|
presets: [],
|
|
282
228
|
buttonsSensors: []
|
|
283
229
|
};
|
|
284
|
-
|
|
285
|
-
if (!atwDevicesInConfig.some(device => device.id === deviceId)) {
|
|
230
|
+
if (!atwDevicesInConfig.some(d => d.id === deviceId)) {
|
|
286
231
|
atwDevicesInConfig.push(deviceAtw);
|
|
287
232
|
newDevices.atw.push(deviceAtw);
|
|
288
233
|
}
|
|
289
|
-
|
|
290
|
-
devicePresets.forEach((preset) => {
|
|
234
|
+
devicePresets.forEach(preset => {
|
|
291
235
|
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
292
236
|
preset.id = presetId;
|
|
293
237
|
preset.name = presetName;
|
|
294
238
|
preset.displayType = 0;
|
|
295
239
|
preset.namePrefix = false;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (atwDevice && !atwDevice.presets.some((p) => p.id === presetId)) {
|
|
240
|
+
const atwDevice = atwDevicesInConfig.find(d => d.id === deviceId);
|
|
241
|
+
if (atwDevice && !atwDevice.presets.some(p => p.id === presetId)) {
|
|
299
242
|
atwDevice.presets.push(preset);
|
|
300
243
|
newDevices.atwPresets.push(preset);
|
|
301
244
|
}
|
|
302
245
|
});
|
|
303
246
|
});
|
|
304
247
|
|
|
305
|
-
// Handle ERV devices
|
|
306
|
-
devicesByTypeInMelCloud.erv.forEach(
|
|
248
|
+
// === Handle ERV devices ===
|
|
249
|
+
devicesByTypeInMelCloud.erv.forEach(device => {
|
|
307
250
|
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
308
251
|
const deviceErv = {
|
|
309
252
|
id: deviceId,
|
|
310
253
|
type: deviceType,
|
|
311
|
-
typeString: "
|
|
254
|
+
typeString: "ERV",
|
|
312
255
|
name: deviceName,
|
|
313
256
|
displayMode: 1,
|
|
314
257
|
temperatureSensor: false,
|
|
315
|
-
temperatureSensorOutdoor: false,
|
|
316
|
-
temperatureSensorSupply: false,
|
|
317
258
|
presets: [],
|
|
318
259
|
buttonsSensors: []
|
|
319
260
|
};
|
|
320
|
-
|
|
321
|
-
if (!ervDevicesInConfig.some(device => device.id === deviceId)) {
|
|
261
|
+
if (!ervDevicesInConfig.some(d => d.id === deviceId)) {
|
|
322
262
|
ervDevicesInConfig.push(deviceErv);
|
|
323
263
|
newDevices.erv.push(deviceErv);
|
|
324
264
|
}
|
|
325
|
-
|
|
326
|
-
devicePresets.forEach((preset) => {
|
|
265
|
+
devicePresets.forEach(preset => {
|
|
327
266
|
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
328
267
|
preset.id = presetId;
|
|
329
268
|
preset.name = presetName;
|
|
330
269
|
preset.displayType = 0;
|
|
331
270
|
preset.namePrefix = false;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (ervDevice && !ervDevice.presets.some((p) => p.id === presetId)) {
|
|
271
|
+
const ervDevice = ervDevicesInConfig.find(d => d.id === deviceId);
|
|
272
|
+
if (ervDevice && !ervDevice.presets.some(p => p.id === presetId)) {
|
|
335
273
|
ervDevice.presets.push(preset);
|
|
336
274
|
newDevices.ervPresets.push(preset);
|
|
337
275
|
}
|
|
338
276
|
});
|
|
339
277
|
});
|
|
340
278
|
|
|
341
|
-
// Display
|
|
279
|
+
// --- Display Info Section ---
|
|
342
280
|
const textAta = `ATA: ${newDevices.ata.length}`;
|
|
343
281
|
const textAtw = `ATW: ${newDevices.atw.length}`;
|
|
344
282
|
const textErv = `ERV: ${newDevices.erv.length}`;
|
|
345
283
|
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
|
|
346
284
|
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
|
|
347
285
|
const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`;
|
|
286
|
+
const textRemovedAta = `ATA: ${removedAta.length}`;
|
|
287
|
+
const textRemovedAtw = `ATW: ${removedAtw.length}`;
|
|
288
|
+
const textRemovedErv = `ERV: ${removedErv.length}`;
|
|
348
289
|
|
|
349
290
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
350
291
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
292
|
+
const removedCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
351
293
|
|
|
352
|
-
if (newDevicesCount === 0 && newPresetsCount === 0) {
|
|
353
|
-
updateInfo('info', 'No
|
|
294
|
+
if (newDevicesCount === 0 && newPresetsCount === 0 && removedCount === 0) {
|
|
295
|
+
updateInfo('info', 'No changes detected.', 'white');
|
|
354
296
|
} else {
|
|
355
297
|
if (newDevicesCount > 0) {
|
|
356
|
-
updateInfo('info', `Found
|
|
357
|
-
updateInfo('info2', 'Now You can configure it.', 'green');
|
|
298
|
+
updateInfo('info', `Found new devices: ${textAta}, ${textAtw}, ${textErv}.`, 'green');
|
|
358
299
|
}
|
|
359
300
|
if (newPresetsCount > 0) {
|
|
360
|
-
updateInfo('info1',
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
301
|
+
updateInfo('info1', `Found new presets: ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
302
|
+
}
|
|
303
|
+
if (removedCount > 0) {
|
|
304
|
+
updateInfo('info2', `Removed devices: ${textRemovedAta}, ${textRemovedAtw}, ${textRemovedErv}.`, 'orange');
|
|
365
305
|
}
|
|
366
306
|
}
|
|
367
307
|
|
|
368
|
-
// Save updated config
|
|
369
308
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
370
309
|
await homebridge.savePluginConfig(pluginConfig);
|
|
371
|
-
|
|
372
|
-
// Update button
|
|
373
310
|
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
374
311
|
homebridge.hideSpinner();
|
|
375
312
|
|
|
@@ -380,7 +317,6 @@
|
|
|
380
317
|
} finally {
|
|
381
318
|
homebridge.hideSpinner();
|
|
382
319
|
}
|
|
383
|
-
|
|
384
320
|
});
|
|
385
321
|
})();
|
|
386
322
|
</script>
|
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.6",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|