homebridge-melcloud-control 3.9.0-beta.16 → 3.9.0-beta.18

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