homebridge-melcloud-control 3.9.0-beta.17 → 3.9.0-beta.19

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