homebridge-melcloud-control 3.9.0-beta.4 → 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 +157 -128
- 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">
|
|
@@ -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
|
|
|
@@ -84,7 +60,6 @@
|
|
|
84
60
|
<script>
|
|
85
61
|
(async () => {
|
|
86
62
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
87
|
-
|
|
88
63
|
if (!pluginConfig.length) {
|
|
89
64
|
pluginConfig.push({});
|
|
90
65
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -95,7 +70,6 @@
|
|
|
95
70
|
|
|
96
71
|
const accountsCount = pluginConfig[0].accounts.length;
|
|
97
72
|
this.deviceIndex = 0;
|
|
98
|
-
|
|
99
73
|
for (let i = 0; i < accountsCount; i++) {
|
|
100
74
|
const button = document.createElement("button");
|
|
101
75
|
button.setAttribute("type", "button");
|
|
@@ -107,10 +81,9 @@
|
|
|
107
81
|
|
|
108
82
|
document.getElementById(`button${i}`).addEventListener('click', async () => {
|
|
109
83
|
for (let j = 0; j < accountsCount; j++) {
|
|
110
|
-
|
|
111
|
-
"class",
|
|
112
|
-
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 || '';
|
|
@@ -119,19 +92,12 @@
|
|
|
119
92
|
document.getElementById('passwd').value = pluginConfig[0].accounts[i].passwd || '';
|
|
120
93
|
document.getElementById('language').value = pluginConfig[0].accounts[i].language || '';
|
|
121
94
|
|
|
122
|
-
const accountConfigured =
|
|
123
|
-
|
|
124
|
-
pluginConfig[0].accounts[i].user &&
|
|
125
|
-
pluginConfig[0].accounts[i].passwd &&
|
|
126
|
-
pluginConfig[0].accounts[i].language;
|
|
127
|
-
|
|
128
|
-
const loginBtn = document.getElementById('logIn');
|
|
129
|
-
loginBtn.disabled = !accountConfigured;
|
|
95
|
+
const accountConfigured = pluginConfig[0].accounts[i].name && pluginConfig[0].accounts[i].user && pluginConfig[0].accounts[i].passwd && pluginConfig[0].accounts[i].language;
|
|
96
|
+
document.getElementById('logIn').disabled = !accountConfigured;
|
|
130
97
|
|
|
131
98
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
132
99
|
this.deviceIndex = i;
|
|
133
100
|
});
|
|
134
|
-
|
|
135
101
|
if (i === accountsCount - 1) {
|
|
136
102
|
document.getElementById(`button0`).click();
|
|
137
103
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -146,12 +112,7 @@
|
|
|
146
112
|
pluginConfig[0].accounts[this.deviceIndex].passwd = document.querySelector('#passwd').value;
|
|
147
113
|
pluginConfig[0].accounts[this.deviceIndex].language = document.querySelector('#language').value;
|
|
148
114
|
|
|
149
|
-
const accountConfigured =
|
|
150
|
-
pluginConfig[0].accounts[this.deviceIndex].name &&
|
|
151
|
-
pluginConfig[0].accounts[this.deviceIndex].user &&
|
|
152
|
-
pluginConfig[0].accounts[this.deviceIndex].passwd &&
|
|
153
|
-
pluginConfig[0].accounts[this.deviceIndex].language;
|
|
154
|
-
|
|
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;
|
|
155
116
|
document.getElementById('logIn').disabled = !accountConfigured;
|
|
156
117
|
|
|
157
118
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -160,35 +121,52 @@
|
|
|
160
121
|
|
|
161
122
|
document.getElementById('configButton').addEventListener('click', async () => {
|
|
162
123
|
this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
|
|
163
|
-
|
|
164
|
-
.getElementById(
|
|
165
|
-
.setAttribute("class",
|
|
124
|
+
this.configButtonState
|
|
125
|
+
? document.getElementById(`configButton`).setAttribute("class", "btn btn-secondary")
|
|
126
|
+
: document.getElementById(`configButton`).setAttribute("class", "btn btn-primary");
|
|
166
127
|
this.configButtonState = !this.configButtonState;
|
|
167
128
|
});
|
|
168
129
|
|
|
130
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
131
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
132
|
+
const removedDevices = [];
|
|
133
|
+
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
134
|
+
const device = configDevices[i];
|
|
135
|
+
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
136
|
+
removedDevices.push(device);
|
|
137
|
+
configDevices.splice(i, 1);
|
|
138
|
+
}
|
|
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
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
169
151
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
170
152
|
homebridge.showSpinner();
|
|
171
|
-
|
|
172
153
|
document.getElementById(`logIn`).setAttribute("class", "btn btn-primary");
|
|
173
|
-
|
|
174
|
-
document.getElementById('info').style.color = 'yellow';
|
|
154
|
+
updateInfo('info', 'Connecting...', 'yellow');
|
|
175
155
|
|
|
176
156
|
try {
|
|
177
157
|
const account = pluginConfig[0].accounts[this.deviceIndex];
|
|
178
158
|
const { name: accountName, user, passwd, language } = account;
|
|
179
|
-
|
|
180
159
|
const payload = { accountName, user, passwd, language };
|
|
181
160
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
182
161
|
|
|
183
162
|
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
184
|
-
const removedDevices = { ata: [], atw: [], erv: [] };
|
|
185
163
|
const devicesByTypeInMelCloud = { ata: [], atw: [], erv: [] };
|
|
186
164
|
|
|
187
|
-
for (const
|
|
188
|
-
switch (
|
|
189
|
-
case 0: devicesByTypeInMelCloud.ata.push(
|
|
190
|
-
case 1: devicesByTypeInMelCloud.atw.push(
|
|
191
|
-
case 3: devicesByTypeInMelCloud.erv.push(
|
|
165
|
+
for (const deviceInMelcloud of devicesInMelCloud) {
|
|
166
|
+
switch (deviceInMelcloud.Type) {
|
|
167
|
+
case 0: devicesByTypeInMelCloud.ata.push(deviceInMelcloud); break;
|
|
168
|
+
case 1: devicesByTypeInMelCloud.atw.push(deviceInMelcloud); break;
|
|
169
|
+
case 3: devicesByTypeInMelCloud.erv.push(deviceInMelcloud); break;
|
|
192
170
|
}
|
|
193
171
|
}
|
|
194
172
|
|
|
@@ -196,93 +174,145 @@
|
|
|
196
174
|
const atwDevicesInConfig = account.atwDevices ?? (account.atwDevices = []);
|
|
197
175
|
const ervDevicesInConfig = account.ervDevices ?? (account.ervDevices = []);
|
|
198
176
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
177
|
+
const removedAta = removeStaleDevices(ataDevicesInConfig, devicesByTypeInMelCloud.ata);
|
|
178
|
+
const removedAtw = removeStaleDevices(atwDevicesInConfig, devicesByTypeInMelCloud.atw);
|
|
179
|
+
const removedErv = removeStaleDevices(ervDevicesInConfig, devicesByTypeInMelCloud.erv);
|
|
180
|
+
|
|
181
|
+
// === Handle ATA devices ===
|
|
182
|
+
devicesByTypeInMelCloud.ata.forEach(device => {
|
|
183
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
184
|
+
const deviceAta = {
|
|
185
|
+
id: deviceId,
|
|
186
|
+
type: deviceType,
|
|
187
|
+
typeString: "Air Conditioner",
|
|
188
|
+
name: deviceName,
|
|
189
|
+
displayMode: 1,
|
|
190
|
+
heatDryFanMode: 1,
|
|
191
|
+
coolDryFanMode: 1,
|
|
192
|
+
autoDryFanMode: 1,
|
|
193
|
+
temperatureSensor: false,
|
|
194
|
+
temperatureSensorOutdoor: false,
|
|
195
|
+
presets: [],
|
|
196
|
+
buttonsSensors: []
|
|
197
|
+
};
|
|
198
|
+
if (!ataDevicesInConfig.some(d => d.id === deviceId)) {
|
|
199
|
+
ataDevicesInConfig.push(deviceAta);
|
|
200
|
+
newDevices.ata.push(deviceAta);
|
|
204
201
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
removedDevices.ata.push(device);
|
|
216
|
-
ataDevicesInConfig.splice(i, 1);
|
|
202
|
+
devicePresets.forEach(preset => {
|
|
203
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
204
|
+
preset.id = presetId;
|
|
205
|
+
preset.name = presetName;
|
|
206
|
+
preset.displayType = 0;
|
|
207
|
+
preset.namePrefix = false;
|
|
208
|
+
const ataDevice = ataDevicesInConfig.find(d => d.id === deviceId);
|
|
209
|
+
if (ataDevice && !ataDevice.presets.some(p => p.id === presetId)) {
|
|
210
|
+
ataDevice.presets.push(preset);
|
|
211
|
+
newDevices.ataPresets.push(preset);
|
|
217
212
|
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// === Handle ATW devices ===
|
|
217
|
+
devicesByTypeInMelCloud.atw.forEach(device => {
|
|
218
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
219
|
+
const deviceAtw = {
|
|
220
|
+
id: deviceId,
|
|
221
|
+
type: deviceType,
|
|
222
|
+
typeString: "Heat Pump",
|
|
223
|
+
name: deviceName,
|
|
224
|
+
displayMode: 1,
|
|
225
|
+
hideZone: 0,
|
|
226
|
+
temperatureSensor: false,
|
|
227
|
+
presets: [],
|
|
228
|
+
buttonsSensors: []
|
|
229
|
+
};
|
|
230
|
+
if (!atwDevicesInConfig.some(d => d.id === deviceId)) {
|
|
231
|
+
atwDevicesInConfig.push(deviceAtw);
|
|
232
|
+
newDevices.atw.push(deviceAtw);
|
|
218
233
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
presets: [],
|
|
230
|
-
buttonsSensors: []
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
if (!configArray.some(d => d.id === deviceId)) {
|
|
234
|
-
configArray.push(deviceObj);
|
|
235
|
-
newArray.push(deviceObj);
|
|
234
|
+
devicePresets.forEach(preset => {
|
|
235
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
236
|
+
preset.id = presetId;
|
|
237
|
+
preset.name = presetName;
|
|
238
|
+
preset.displayType = 0;
|
|
239
|
+
preset.namePrefix = false;
|
|
240
|
+
const atwDevice = atwDevicesInConfig.find(d => d.id === deviceId);
|
|
241
|
+
if (atwDevice && !atwDevice.presets.some(p => p.id === presetId)) {
|
|
242
|
+
atwDevice.presets.push(preset);
|
|
243
|
+
newDevices.atwPresets.push(preset);
|
|
236
244
|
}
|
|
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
245
|
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// === Handle ERV devices ===
|
|
249
|
+
devicesByTypeInMelCloud.erv.forEach(device => {
|
|
250
|
+
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
251
|
+
const deviceErv = {
|
|
252
|
+
id: deviceId,
|
|
253
|
+
type: deviceType,
|
|
254
|
+
typeString: "ERV",
|
|
255
|
+
name: deviceName,
|
|
256
|
+
displayMode: 1,
|
|
257
|
+
temperatureSensor: false,
|
|
258
|
+
presets: [],
|
|
259
|
+
buttonsSensors: []
|
|
260
|
+
};
|
|
261
|
+
if (!ervDevicesInConfig.some(d => d.id === deviceId)) {
|
|
262
|
+
ervDevicesInConfig.push(deviceErv);
|
|
263
|
+
newDevices.erv.push(deviceErv);
|
|
264
|
+
}
|
|
265
|
+
devicePresets.forEach(preset => {
|
|
266
|
+
const { ID: presetId, NumberDescription: presetName } = preset;
|
|
267
|
+
preset.id = presetId;
|
|
268
|
+
preset.name = presetName;
|
|
269
|
+
preset.displayType = 0;
|
|
270
|
+
preset.namePrefix = false;
|
|
271
|
+
const ervDevice = ervDevicesInConfig.find(d => d.id === deviceId);
|
|
272
|
+
if (ervDevice && !ervDevice.presets.some(p => p.id === presetId)) {
|
|
273
|
+
ervDevice.presets.push(preset);
|
|
274
|
+
newDevices.ervPresets.push(preset);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// --- Display Info Section ---
|
|
280
|
+
const textAta = `ATA: ${newDevices.ata.length}`;
|
|
281
|
+
const textAtw = `ATW: ${newDevices.atw.length}`;
|
|
282
|
+
const textErv = `ERV: ${newDevices.erv.length}`;
|
|
283
|
+
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
|
|
284
|
+
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
|
|
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}`;
|
|
258
289
|
|
|
259
|
-
const formatDeviceNames = (arr) => arr.map(d => d.name).join(', ') || 'None';
|
|
260
290
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
261
291
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
262
|
-
const
|
|
292
|
+
const removedCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
263
293
|
|
|
264
|
-
if (newDevicesCount === 0 && newPresetsCount === 0 &&
|
|
265
|
-
updateInfo('info', 'No changes
|
|
294
|
+
if (newDevicesCount === 0 && newPresetsCount === 0 && removedCount === 0) {
|
|
295
|
+
updateInfo('info', 'No changes detected.', 'white');
|
|
266
296
|
} else {
|
|
267
297
|
if (newDevicesCount > 0) {
|
|
268
|
-
updateInfo('info', `
|
|
269
|
-
updateInfo('info2', 'Now You can configure it.', 'green');
|
|
298
|
+
updateInfo('info', `Found new devices: ${textAta}, ${textAtw}, ${textErv}.`, 'green');
|
|
270
299
|
}
|
|
271
300
|
if (newPresetsCount > 0) {
|
|
272
|
-
updateInfo('info1', `
|
|
301
|
+
updateInfo('info1', `Found new presets: ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
273
302
|
}
|
|
274
|
-
if (
|
|
275
|
-
updateInfo('
|
|
303
|
+
if (removedCount > 0) {
|
|
304
|
+
updateInfo('info2', `Removed devices: ${textRemovedAta}, ${textRemovedAtw}, ${textRemovedErv}.`, 'orange');
|
|
276
305
|
}
|
|
277
306
|
}
|
|
278
307
|
|
|
308
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
279
309
|
await homebridge.savePluginConfig(pluginConfig);
|
|
280
|
-
|
|
281
310
|
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
282
311
|
homebridge.hideSpinner();
|
|
312
|
+
|
|
283
313
|
} catch (error) {
|
|
284
314
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
285
|
-
updateInfo('info1', `Error: ${error
|
|
315
|
+
updateInfo('info1', `Error: ${error}`, 'red');
|
|
286
316
|
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
287
317
|
} finally {
|
|
288
318
|
homebridge.hideSpinner();
|
|
@@ -290,7 +320,6 @@
|
|
|
290
320
|
});
|
|
291
321
|
})();
|
|
292
322
|
</script>
|
|
293
|
-
|
|
294
323
|
</body>
|
|
295
324
|
|
|
296
325
|
</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.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",
|