homebridge-melcloud-control 3.9.0-beta.4 → 3.9.0-beta.5
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 +187 -94
- package/package.json +1 -1
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
<form id="configForm">
|
|
21
21
|
<div class="text-center">
|
|
22
22
|
<label id="accountName" class="fw-bold" style="font-size: 23px;">Account</label><br>
|
|
23
|
-
<label id="info" class="d-block" style="font-size:
|
|
24
|
-
<label id="info1" class="d-block" style="font-size:
|
|
25
|
-
<label id="info2" class="d-block" style="font-size:
|
|
23
|
+
<label id="info" class="d-block" style="font-size: 17px;"></label>
|
|
24
|
+
<label id="info1" class="d-block" style="font-size: 15px;"></label>
|
|
25
|
+
<label id="info2" class="d-block" style="font-size: 15px;"></label>
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<div class="mb-3">
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
|
|
84
84
|
<script>
|
|
85
85
|
(async () => {
|
|
86
|
+
//get the plugin config array
|
|
86
87
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
87
88
|
|
|
88
89
|
if (!pluginConfig.length) {
|
|
@@ -93,10 +94,11 @@
|
|
|
93
94
|
}
|
|
94
95
|
this.configButtonState = false;
|
|
95
96
|
|
|
97
|
+
//get accounts count
|
|
96
98
|
const accountsCount = pluginConfig[0].accounts.length;
|
|
97
99
|
this.deviceIndex = 0;
|
|
98
|
-
|
|
99
100
|
for (let i = 0; i < accountsCount; i++) {
|
|
101
|
+
//create buttons
|
|
100
102
|
const button = document.createElement("button");
|
|
101
103
|
button.setAttribute("type", "button");
|
|
102
104
|
button.setAttribute("id", `button${i}`);
|
|
@@ -105,12 +107,10 @@
|
|
|
105
107
|
button.innerText = pluginConfig[0].accounts[i].name;
|
|
106
108
|
document.getElementById("accountButton").appendChild(button);
|
|
107
109
|
|
|
110
|
+
//get actuall value on account select
|
|
108
111
|
document.getElementById(`button${i}`).addEventListener('click', async () => {
|
|
109
112
|
for (let j = 0; j < accountsCount; j++) {
|
|
110
|
-
document.getElementById(`button${j}`).setAttribute(
|
|
111
|
-
"class",
|
|
112
|
-
j === i ? "btn btn-secondary" : "btn btn-primary"
|
|
113
|
-
);
|
|
113
|
+
const setRemoveAtribute = j === i ? document.getElementById(`button${j}`).setAttribute("class", "btn btn-secondary") : document.getElementById(`button${j}`).setAttribute("class", "btn btn-primary");
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
document.getElementById('accountName').innerHTML = pluginConfig[0].accounts[i].name || '';
|
|
@@ -119,53 +119,54 @@
|
|
|
119
119
|
document.getElementById('passwd').value = pluginConfig[0].accounts[i].passwd || '';
|
|
120
120
|
document.getElementById('language').value = pluginConfig[0].accounts[i].language || '';
|
|
121
121
|
|
|
122
|
-
const accountConfigured =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
pluginConfig[0].accounts[i].passwd &&
|
|
126
|
-
pluginConfig[0].accounts[i].language;
|
|
127
|
-
|
|
128
|
-
const loginBtn = document.getElementById('logIn');
|
|
129
|
-
loginBtn.disabled = !accountConfigured;
|
|
122
|
+
const accountConfigured = pluginConfig[0].accounts[i].name && pluginConfig[0].accounts[i].user && pluginConfig[0].accounts[i].passwd && pluginConfig[0].accounts[i].language;
|
|
123
|
+
const button = document.getElementById('logIn');
|
|
124
|
+
const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true;
|
|
130
125
|
|
|
131
126
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
132
127
|
this.deviceIndex = i;
|
|
133
128
|
});
|
|
129
|
+
const click = i === accountsCount - 1 ? document.getElementById(`button0`).click() : false;
|
|
130
|
+
const update = i === accountsCount - 1 ? await homebridge.updatePluginConfig(pluginConfig) : false;
|
|
131
|
+
};
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
document.getElementById(`button0`).click();
|
|
137
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
133
|
+
//load melCloudAccount form
|
|
141
134
|
document.getElementById('melCloudAccount').style.display = 'block';
|
|
142
135
|
|
|
136
|
+
//watch for changes to the form
|
|
143
137
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
144
138
|
pluginConfig[0].accounts[this.deviceIndex].name = document.querySelector('#name').value;
|
|
145
139
|
pluginConfig[0].accounts[this.deviceIndex].user = document.querySelector('#user').value;
|
|
146
140
|
pluginConfig[0].accounts[this.deviceIndex].passwd = document.querySelector('#passwd').value;
|
|
147
141
|
pluginConfig[0].accounts[this.deviceIndex].language = document.querySelector('#language').value;
|
|
148
142
|
|
|
149
|
-
const accountConfigured =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
pluginConfig[0].accounts[this.deviceIndex].passwd &&
|
|
153
|
-
pluginConfig[0].accounts[this.deviceIndex].language;
|
|
154
|
-
|
|
155
|
-
document.getElementById('logIn').disabled = !accountConfigured;
|
|
143
|
+
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
|
+
const button = document.getElementById('logIn');
|
|
145
|
+
const setButtonState = accountConfigured ? button.disabled = false : button.disabled = true;
|
|
156
146
|
|
|
157
147
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
158
148
|
await homebridge.savePluginConfig(pluginConfig);
|
|
159
149
|
});
|
|
160
150
|
|
|
151
|
+
//watch for changes to the config button
|
|
161
152
|
document.getElementById('configButton').addEventListener('click', async () => {
|
|
162
|
-
this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
|
|
163
|
-
document
|
|
164
|
-
.getElementById('configButton')
|
|
165
|
-
.setAttribute("class", this.configButtonState ? "btn btn-secondary" : "btn btn-primary");
|
|
153
|
+
const showHideSettings = this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
|
|
154
|
+
const setRemoveAtribute = this.configButtonState ? document.getElementById(`configButton`).setAttribute("class", "btn btn-secondary") : document.getElementById(`configButton`).setAttribute("class", "btn btn-primary");
|
|
166
155
|
this.configButtonState = !this.configButtonState;
|
|
167
156
|
});
|
|
168
157
|
|
|
158
|
+
// Helper to remove stale devices by comparing with MELCloud
|
|
159
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
160
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
161
|
+
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
162
|
+
const device = configDevices[i];
|
|
163
|
+
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
164
|
+
configDevices.splice(i, 1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
//watch for click on the login button
|
|
169
170
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
170
171
|
homebridge.showSpinner();
|
|
171
172
|
|
|
@@ -180,22 +181,37 @@
|
|
|
180
181
|
const payload = { accountName, user, passwd, language };
|
|
181
182
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
182
183
|
|
|
183
|
-
const newDevices = {
|
|
184
|
-
|
|
184
|
+
const newDevices = {
|
|
185
|
+
ata: [],
|
|
186
|
+
ataPresets: [],
|
|
187
|
+
atw: [],
|
|
188
|
+
atwPresets: [],
|
|
189
|
+
erv: [],
|
|
190
|
+
ervPresets: [],
|
|
191
|
+
};
|
|
192
|
+
|
|
185
193
|
const devicesByTypeInMelCloud = { ata: [], atw: [], erv: [] };
|
|
186
194
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
case
|
|
191
|
-
case
|
|
195
|
+
// Categorize devices by type
|
|
196
|
+
for (const deviceInMelcloud of devicesInMelCloud) {
|
|
197
|
+
switch (deviceInMelcloud.Type) {
|
|
198
|
+
case 0: devicesByTypeInMelCloud.ata.push(deviceInMelcloud); break;
|
|
199
|
+
case 1: devicesByTypeInMelCloud.atw.push(deviceInMelcloud); break;
|
|
200
|
+
case 3: devicesByTypeInMelCloud.erv.push(deviceInMelcloud); break;
|
|
192
201
|
}
|
|
193
202
|
}
|
|
194
203
|
|
|
204
|
+
// Ensure config arrays exist
|
|
195
205
|
const ataDevicesInConfig = account.ataDevices ?? (account.ataDevices = []);
|
|
196
206
|
const atwDevicesInConfig = account.atwDevices ?? (account.atwDevices = []);
|
|
197
207
|
const ervDevicesInConfig = account.ervDevices ?? (account.ervDevices = []);
|
|
198
208
|
|
|
209
|
+
// Remove stale devices
|
|
210
|
+
removeStaleDevices(ataDevicesInConfig, devicesByTypeInMelCloud.ata);
|
|
211
|
+
removeStaleDevices(atwDevicesInConfig, devicesByTypeInMelCloud.atw);
|
|
212
|
+
removeStaleDevices(ervDevicesInConfig, devicesByTypeInMelCloud.erv);
|
|
213
|
+
|
|
214
|
+
// Helper for updating UI
|
|
199
215
|
const updateInfo = (id, text, color) => {
|
|
200
216
|
const el = document.getElementById(id);
|
|
201
217
|
if (el) {
|
|
@@ -204,93 +220,170 @@
|
|
|
204
220
|
}
|
|
205
221
|
};
|
|
206
222
|
|
|
207
|
-
|
|
208
|
-
|
|
223
|
+
// Handle ATA devices
|
|
224
|
+
devicesByTypeInMelCloud.ata.forEach((device) => {
|
|
225
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
226
|
+
const deviceAta = {
|
|
227
|
+
id: deviceId,
|
|
228
|
+
type: deviceType,
|
|
229
|
+
typeString: "Air Conditioner",
|
|
230
|
+
name: deviceName,
|
|
231
|
+
displayMode: 1,
|
|
232
|
+
heatDryFanMode: 1,
|
|
233
|
+
coolDryFanMode: 1,
|
|
234
|
+
autoDryFanMode: 1,
|
|
235
|
+
temperatureSensor: false,
|
|
236
|
+
temperatureSensorOutdoor: false,
|
|
237
|
+
presets: [],
|
|
238
|
+
buttonsSensors: []
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
if (!ataDevicesInConfig.some(device => device.id === deviceId)) {
|
|
242
|
+
ataDevicesInConfig.push(deviceAta);
|
|
243
|
+
newDevices.ata.push(deviceAta);
|
|
244
|
+
}
|
|
209
245
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
246
|
+
devicePresets.forEach((preset) => {
|
|
247
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
248
|
+
preset.id = presetId;
|
|
249
|
+
preset.name = presetName;
|
|
250
|
+
preset.displayType = 0;
|
|
251
|
+
preset.namePrefix = false;
|
|
252
|
+
|
|
253
|
+
const ataDevice = ataDevicesInConfig.find((device) => device.id === deviceId);
|
|
254
|
+
if (ataDevice && !ataDevice.presets.some((p) => p.id === presetId)) {
|
|
255
|
+
ataDevice.presets.push(preset);
|
|
256
|
+
newDevices.ataPresets.push(preset);
|
|
217
257
|
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Handle ATW devices
|
|
262
|
+
devicesByTypeInMelCloud.atw.forEach((device) => {
|
|
263
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
264
|
+
const deviceAtw = {
|
|
265
|
+
id: deviceId,
|
|
266
|
+
type: deviceType,
|
|
267
|
+
typeString: "Heat Pump",
|
|
268
|
+
name: deviceName,
|
|
269
|
+
displayMode: 1,
|
|
270
|
+
hideZone: 0,
|
|
271
|
+
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
|
+
presets: [],
|
|
282
|
+
buttonsSensors: []
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
if (!atwDevicesInConfig.some(device => device.id === deviceId)) {
|
|
286
|
+
atwDevicesInConfig.push(deviceAtw);
|
|
287
|
+
newDevices.atw.push(deviceAtw);
|
|
218
288
|
}
|
|
219
289
|
|
|
220
|
-
|
|
221
|
-
const {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
presets
|
|
230
|
-
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
if (!configArray.some(d => d.id === deviceId)) {
|
|
234
|
-
configArray.push(deviceObj);
|
|
235
|
-
newArray.push(deviceObj);
|
|
290
|
+
devicePresets.forEach((preset) => {
|
|
291
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
292
|
+
preset.id = presetId;
|
|
293
|
+
preset.name = presetName;
|
|
294
|
+
preset.displayType = 0;
|
|
295
|
+
preset.namePrefix = false;
|
|
296
|
+
|
|
297
|
+
const atwDevice = atwDevicesInConfig.find((device) => device.id === deviceId);
|
|
298
|
+
if (atwDevice && !atwDevice.presets.some((p) => p.id === presetId)) {
|
|
299
|
+
atwDevice.presets.push(preset);
|
|
300
|
+
newDevices.atwPresets.push(preset);
|
|
236
301
|
}
|
|
237
|
-
|
|
238
|
-
devicePresets.forEach((preset) => {
|
|
239
|
-
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
240
|
-
Object.assign(preset, { id: presetId, name: presetName, displayType: 0, namePrefix: false });
|
|
241
|
-
|
|
242
|
-
const existingDevice = configArray.find(d => d.id === deviceId);
|
|
243
|
-
if (existingDevice && !existingDevice.presets.some(p => p.id === presetId)) {
|
|
244
|
-
existingDevice.presets.push(preset);
|
|
245
|
-
newPresetsArray.push(preset);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
302
|
});
|
|
249
|
-
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Handle ERV devices
|
|
306
|
+
devicesByTypeInMelCloud.erv.forEach((device) => {
|
|
307
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
308
|
+
const deviceErv = {
|
|
309
|
+
id: deviceId,
|
|
310
|
+
type: deviceType,
|
|
311
|
+
typeString: "Energy Recovery Ventilation",
|
|
312
|
+
name: deviceName,
|
|
313
|
+
displayMode: 1,
|
|
314
|
+
temperatureSensor: false,
|
|
315
|
+
temperatureSensorOutdoor: false,
|
|
316
|
+
temperatureSensorSupply: false,
|
|
317
|
+
presets: [],
|
|
318
|
+
buttonsSensors: []
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
if (!ervDevicesInConfig.some(device => device.id === deviceId)) {
|
|
322
|
+
ervDevicesInConfig.push(deviceErv);
|
|
323
|
+
newDevices.erv.push(deviceErv);
|
|
324
|
+
}
|
|
250
325
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
326
|
+
devicePresets.forEach((preset) => {
|
|
327
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
328
|
+
preset.id = presetId;
|
|
329
|
+
preset.name = presetName;
|
|
330
|
+
preset.displayType = 0;
|
|
331
|
+
preset.namePrefix = false;
|
|
332
|
+
|
|
333
|
+
const ervDevice = ervDevicesInConfig.find((device) => device.id === deviceId);
|
|
334
|
+
if (ervDevice && !ervDevice.presets.some((p) => p.id === presetId)) {
|
|
335
|
+
ervDevice.presets.push(preset);
|
|
336
|
+
newDevices.ervPresets.push(preset);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
254
340
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
341
|
+
// Display info
|
|
342
|
+
const textAta = `ATA: ${newDevices.ata.length}`;
|
|
343
|
+
const textAtw = `ATW: ${newDevices.atw.length}`;
|
|
344
|
+
const textErv = `ERV: ${newDevices.erv.length}`;
|
|
345
|
+
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
|
|
346
|
+
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
|
|
347
|
+
const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`;
|
|
258
348
|
|
|
259
|
-
const formatDeviceNames = (arr) => arr.map(d => d.name).join(', ') || 'None';
|
|
260
349
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
261
350
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
262
|
-
const removedDevicesCount = removedDevices.ata.length + removedDevices.atw.length + removedDevices.erv.length;
|
|
263
351
|
|
|
264
|
-
if (newDevicesCount === 0 && newPresetsCount === 0
|
|
265
|
-
updateInfo('info', 'No
|
|
352
|
+
if (newDevicesCount === 0 && newPresetsCount === 0) {
|
|
353
|
+
updateInfo('info', 'No new devices found.', 'white');
|
|
266
354
|
} else {
|
|
267
355
|
if (newDevicesCount > 0) {
|
|
268
|
-
updateInfo('info', `
|
|
356
|
+
updateInfo('info', `Found, ${textAta}, ${textAtw}, ${textErv}.`, 'green');
|
|
269
357
|
updateInfo('info2', 'Now You can configure it.', 'green');
|
|
270
358
|
}
|
|
271
359
|
if (newPresetsCount > 0) {
|
|
272
|
-
updateInfo('info1',
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
360
|
+
updateInfo('info1', `${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
361
|
+
if (newDevicesCount === 0) {
|
|
362
|
+
updateInfo('info', `Found, ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
363
|
+
updateInfo('info1', 'Now You can configure it.', 'green');
|
|
364
|
+
}
|
|
276
365
|
}
|
|
277
366
|
}
|
|
278
367
|
|
|
368
|
+
// Save updated config
|
|
369
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
279
370
|
await homebridge.savePluginConfig(pluginConfig);
|
|
280
371
|
|
|
372
|
+
// Update button
|
|
281
373
|
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
282
374
|
homebridge.hideSpinner();
|
|
375
|
+
|
|
283
376
|
} catch (error) {
|
|
284
377
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
285
|
-
updateInfo('info1', `Error: ${error
|
|
378
|
+
updateInfo('info1', `Error: ${error}`, 'red');
|
|
286
379
|
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
287
380
|
} finally {
|
|
288
381
|
homebridge.hideSpinner();
|
|
289
382
|
}
|
|
383
|
+
|
|
290
384
|
});
|
|
291
385
|
})();
|
|
292
386
|
</script>
|
|
293
|
-
|
|
294
387
|
</body>
|
|
295
388
|
|
|
296
389
|
</html>
|
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.5",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|