homebridge-melcloud-control 3.9.0-beta.2 → 3.9.0-beta.20
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 +127 -145
- 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: 14px;"></label>
|
|
24
|
+
<label id="info1" class="d-block" style="font-size: 12px;"></label>
|
|
25
|
+
<label id="info2" class="d-block" style="font-size: 12px;"></label>
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<div class="mb-3">
|
|
@@ -35,9 +35,14 @@
|
|
|
35
35
|
<input id="user" type="text" class="form-control" required>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<div class="mb-3">
|
|
38
|
+
<div class="mb-3 position-relative">
|
|
39
39
|
<label for="passwd" class="form-label">Password</label>
|
|
40
|
-
<
|
|
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>
|
|
41
46
|
</div>
|
|
42
47
|
|
|
43
48
|
<div class="mb-3">
|
|
@@ -84,211 +89,188 @@
|
|
|
84
89
|
<script>
|
|
85
90
|
(async () => {
|
|
86
91
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
87
|
-
|
|
88
92
|
if (!pluginConfig.length) {
|
|
89
93
|
pluginConfig.push({});
|
|
90
94
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
91
95
|
homebridge.showSchemaForm();
|
|
92
96
|
return;
|
|
93
97
|
}
|
|
94
|
-
this.configButtonState = false;
|
|
95
98
|
|
|
96
|
-
const accountsCount = pluginConfig[0].accounts.length;
|
|
97
99
|
this.deviceIndex = 0;
|
|
98
100
|
|
|
101
|
+
const accountsCount = pluginConfig[0].accounts.length;
|
|
99
102
|
for (let i = 0; i < accountsCount; i++) {
|
|
100
103
|
const button = document.createElement("button");
|
|
101
|
-
button.
|
|
102
|
-
button.
|
|
103
|
-
button.
|
|
104
|
+
button.type = "button";
|
|
105
|
+
button.id = `button${i}`;
|
|
106
|
+
button.className = "btn btn-primary";
|
|
104
107
|
button.style.textTransform = 'none';
|
|
105
108
|
button.innerText = pluginConfig[0].accounts[i].name;
|
|
106
109
|
document.getElementById("accountButton").appendChild(button);
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
button.addEventListener('click', async () => {
|
|
109
112
|
for (let j = 0; j < accountsCount; j++) {
|
|
110
|
-
document.getElementById(`button${j}`).
|
|
111
|
-
"class",
|
|
112
|
-
j === i ? "btn btn-secondary" : "btn btn-primary"
|
|
113
|
-
);
|
|
113
|
+
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
document.getElementById('
|
|
118
|
-
document.getElementById('
|
|
119
|
-
document.getElementById('
|
|
120
|
-
document.getElementById('
|
|
121
|
-
|
|
122
|
-
const accountConfigured =
|
|
123
|
-
pluginConfig[0].accounts[i].name &&
|
|
124
|
-
pluginConfig[0].accounts[i].user &&
|
|
125
|
-
pluginConfig[0].accounts[i].passwd &&
|
|
126
|
-
pluginConfig[0].accounts[i].language;
|
|
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 || '';
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
loginBtn.disabled = !accountConfigured;
|
|
123
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
130
124
|
|
|
131
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
132
125
|
this.deviceIndex = i;
|
|
126
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
133
127
|
});
|
|
134
128
|
|
|
135
|
-
if (i === accountsCount - 1)
|
|
136
|
-
document.getElementById(`button0`).click();
|
|
137
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
138
|
-
}
|
|
129
|
+
if (i === accountsCount - 1) document.getElementById(`button0`).click();
|
|
139
130
|
}
|
|
140
131
|
|
|
141
132
|
document.getElementById('melCloudAccount').style.display = 'block';
|
|
142
133
|
|
|
143
134
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
144
|
-
pluginConfig[0].accounts[this.deviceIndex]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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;
|
|
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;
|
|
154
140
|
|
|
155
|
-
document.getElementById('logIn').disabled = !
|
|
141
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
156
142
|
|
|
157
143
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
158
144
|
await homebridge.savePluginConfig(pluginConfig);
|
|
159
145
|
});
|
|
160
146
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
147
|
+
// --- Config Button Toggle ---
|
|
148
|
+
const configButton = document.getElementById('configButton');
|
|
149
|
+
let configButtonState = false;
|
|
150
|
+
configButton.addEventListener('click', () => {
|
|
151
|
+
configButtonState = !configButtonState;
|
|
152
|
+
homebridge[configButtonState ? 'showSchemaForm' : 'hideSchemaForm']();
|
|
153
|
+
configButton.className = configButtonState ? 'btn btn-primary' : 'btn btn-secondary';
|
|
167
154
|
});
|
|
168
155
|
|
|
156
|
+
// Password toggle
|
|
157
|
+
document.getElementById('togglePasswd').addEventListener('click', () => {
|
|
158
|
+
const passwdInput = document.getElementById('passwd');
|
|
159
|
+
const icon = document.querySelector('#togglePasswd i');
|
|
160
|
+
if (passwdInput.type === 'password') {
|
|
161
|
+
passwdInput.type = 'text';
|
|
162
|
+
icon.classList.replace('fa-eye', 'fa-eye-slash');
|
|
163
|
+
} else {
|
|
164
|
+
passwdInput.type = 'password';
|
|
165
|
+
icon.classList.replace('fa-eye-slash', 'fa-eye');
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// --- Device Handling & Login Logic ---
|
|
170
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
171
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
172
|
+
const removedDevices = [];
|
|
173
|
+
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
174
|
+
const device = configDevices[i];
|
|
175
|
+
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
176
|
+
removedDevices.push(device);
|
|
177
|
+
configDevices.splice(i, 1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return removedDevices;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function updateInfo(id, text, color) {
|
|
184
|
+
const el = document.getElementById(id);
|
|
185
|
+
if (el) {
|
|
186
|
+
el.innerText = text;
|
|
187
|
+
el.style.color = color;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
170
192
|
homebridge.showSpinner();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
document.getElementById('info').innerHTML = 'Connecting...';
|
|
174
|
-
document.getElementById('info').style.color = 'yellow';
|
|
193
|
+
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
194
|
+
updateInfo('info', 'Connecting...', 'yellow');
|
|
175
195
|
|
|
176
196
|
try {
|
|
177
|
-
const
|
|
178
|
-
const {
|
|
179
|
-
|
|
180
|
-
const payload = { accountName, user, passwd, language };
|
|
197
|
+
const acc = pluginConfig[0].accounts[this.deviceIndex];
|
|
198
|
+
const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language };
|
|
181
199
|
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
182
200
|
|
|
201
|
+
// Initialize devices arrays
|
|
183
202
|
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const melcloudIds = devices.map(d => d.DeviceID);
|
|
209
|
-
|
|
210
|
-
// remove stale devices
|
|
211
|
-
for (let i = configArray.length - 1; i >= 0; i--) {
|
|
212
|
-
if (!melcloudIds.includes(configArray[i].id)) {
|
|
213
|
-
removedArray.push(configArray[i]);
|
|
214
|
-
configArray.splice(i, 1);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
devices.forEach((device) => {
|
|
219
|
-
const { DeviceID: deviceId, Type: deviceType, DeviceName: deviceName, Presets: devicePresets = [] } = device;
|
|
220
|
-
|
|
221
|
-
const deviceObj = {
|
|
222
|
-
id: deviceId,
|
|
223
|
-
type: deviceType,
|
|
224
|
-
typeString,
|
|
225
|
-
name: deviceName,
|
|
226
|
-
...template,
|
|
227
|
-
presets: [],
|
|
228
|
-
buttonsSensors: []
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
if (!configArray.some(d => d.id === deviceId)) {
|
|
232
|
-
configArray.push(deviceObj);
|
|
233
|
-
newArray.push(deviceObj);
|
|
203
|
+
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
204
|
+
|
|
205
|
+
devicesInMelCloud.forEach(d => {
|
|
206
|
+
if (d.Type === 0) devicesByType.ata.push(d);
|
|
207
|
+
if (d.Type === 1) devicesByType.atw.push(d);
|
|
208
|
+
if (d.Type === 3) devicesByType.erv.push(d);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
acc.ataDevices ??= [];
|
|
212
|
+
acc.atwDevices ??= [];
|
|
213
|
+
acc.ervDevices ??= [];
|
|
214
|
+
|
|
215
|
+
const removedAta = removeStaleDevices(acc.ataDevices, devicesByType.ata);
|
|
216
|
+
const removedAtw = removeStaleDevices(acc.atwDevices, devicesByType.atw);
|
|
217
|
+
const removedErv = removeStaleDevices(acc.ervDevices, devicesByType.erv);
|
|
218
|
+
|
|
219
|
+
// Function to handle device & presets
|
|
220
|
+
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
221
|
+
devicesInCloud.forEach(device => {
|
|
222
|
+
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
223
|
+
const devObj = { id, type, typeString, name, displayMode: 1, presets: [], buttonsSensors: [] };
|
|
224
|
+
if (!devicesInConfig.some(d => d.id === id)) {
|
|
225
|
+
devicesInConfig.push(devObj);
|
|
226
|
+
newArr.push(devObj);
|
|
234
227
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
228
|
+
presets.forEach(p => {
|
|
229
|
+
p.id = p.ID;
|
|
230
|
+
p.name = p.NumberDescription;
|
|
231
|
+
p.displayType = 0;
|
|
232
|
+
p.namePrefix = false;
|
|
233
|
+
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
234
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.ID)) {
|
|
235
|
+
devConfig.presets.push(p);
|
|
236
|
+
newPresets.push(p);
|
|
244
237
|
}
|
|
245
238
|
});
|
|
246
239
|
});
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const ataTemplate = { displayMode: 1, heatDryFanMode: 1, coolDryFanMode: 1, autoDryFanMode: 1, temperatureSensor: false, temperatureSensorOutdoor: false };
|
|
250
|
-
const atwTemplate = { displayMode: 1, hideZone: 0, temperatureSensor: false, temperatureSensorFlow: false, temperatureSensorReturn: false, temperatureSensorFlowZone1: false, temperatureSensorReturnZone1: false, temperatureSensorFlowWaterTank: false, temperatureSensorReturnWaterTank: false, temperatureSensorFlowZone2: false, temperatureSensorReturnZone2: false, temperatureSensorOutdoor: false };
|
|
251
|
-
const ervTemplate = { displayMode: 1, temperatureSensor: false, temperatureSensorOutdoor: false, temperatureSensorSupply: false };
|
|
240
|
+
};
|
|
252
241
|
|
|
253
|
-
handleDevices(
|
|
254
|
-
handleDevices(
|
|
255
|
-
handleDevices(
|
|
242
|
+
handleDevices(devicesByType.ata, acc.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
243
|
+
handleDevices(devicesByType.atw, acc.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
244
|
+
handleDevices(devicesByType.erv, acc.ervDevices, "ERV", newDevices.erv, newDevices.ervPresets);
|
|
256
245
|
|
|
257
|
-
|
|
246
|
+
// Display summary
|
|
258
247
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
259
248
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
260
|
-
const
|
|
249
|
+
const removedCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
261
250
|
|
|
262
|
-
if (newDevicesCount
|
|
263
|
-
updateInfo('info', 'No changes
|
|
251
|
+
if (!newDevicesCount && !newPresetsCount && !removedCount) {
|
|
252
|
+
updateInfo('info', 'No changes detected.', 'white');
|
|
264
253
|
} else {
|
|
265
|
-
if (newDevicesCount
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
if (newPresetsCount > 0) {
|
|
270
|
-
updateInfo('info1', `New Presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}`, 'green');
|
|
271
|
-
}
|
|
272
|
-
if (removedDevicesCount > 0) {
|
|
273
|
-
updateInfo('info', `Removed Devices: ATA: ${formatDeviceNames(removedDevices.ata)}, ATW: ${formatDeviceNames(removedDevices.atw)}, ERV: ${formatDeviceNames(removedDevices.erv)}`, 'orange');
|
|
274
|
-
}
|
|
254
|
+
if (newDevicesCount) updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
255
|
+
if (newPresetsCount) updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
256
|
+
if (removedCount) updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
275
257
|
}
|
|
276
258
|
|
|
259
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
277
260
|
await homebridge.savePluginConfig(pluginConfig);
|
|
261
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
278
262
|
|
|
279
|
-
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
280
|
-
homebridge.hideSpinner();
|
|
281
263
|
} catch (error) {
|
|
282
264
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
283
|
-
updateInfo('info1', `Error: ${error
|
|
284
|
-
document.getElementById('logIn').
|
|
265
|
+
updateInfo('info1', `Error: ${error}`, 'red');
|
|
266
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
285
267
|
} finally {
|
|
286
268
|
homebridge.hideSpinner();
|
|
287
269
|
}
|
|
288
270
|
});
|
|
271
|
+
|
|
289
272
|
})();
|
|
290
273
|
</script>
|
|
291
|
-
|
|
292
274
|
</body>
|
|
293
275
|
|
|
294
276
|
</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.20",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|