homebridge-melcloud-control 3.9.0-beta.17 → 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.
@@ -94,60 +94,56 @@
94
94
  homebridge.showSchemaForm();
95
95
  return;
96
96
  }
97
- this.configButtonState = false;
98
97
 
99
- const accountsCount = pluginConfig[0].accounts.length;
100
98
  this.deviceIndex = 0;
99
+
100
+ const accountsCount = pluginConfig[0].accounts.length;
101
101
  for (let i = 0; i < accountsCount; i++) {
102
102
  const button = document.createElement("button");
103
- button.setAttribute("type", "button");
104
- button.setAttribute("id", `button${i}`);
105
- button.setAttribute("class", "btn btn-primary");
103
+ button.type = "button";
104
+ button.id = `button${i}`;
105
+ button.className = "btn btn-primary";
106
106
  button.style.textTransform = 'none';
107
107
  button.innerText = pluginConfig[0].accounts[i].name;
108
108
  document.getElementById("accountButton").appendChild(button);
109
109
 
110
- document.getElementById(`button${i}`).addEventListener('click', async () => {
110
+ button.addEventListener('click', async () => {
111
111
  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");
112
+ document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
115
113
  }
116
114
 
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 || '';
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 || '';
122
121
 
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;
122
+ document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
125
123
 
126
- await homebridge.updatePluginConfig(pluginConfig);
127
124
  this.deviceIndex = i;
128
- });
129
- if (i === accountsCount - 1) {
130
- document.getElementById(`button0`).click();
131
125
  await homebridge.updatePluginConfig(pluginConfig);
132
- }
126
+ });
127
+
128
+ if (i === accountsCount - 1) document.getElementById(`button0`).click();
133
129
  }
134
130
 
135
131
  document.getElementById('melCloudAccount').style.display = 'block';
136
132
 
137
133
  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;
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;
142
139
 
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;
140
+ document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
145
141
 
146
142
  await homebridge.updatePluginConfig(pluginConfig);
147
143
  await homebridge.savePluginConfig(pluginConfig);
148
144
  });
149
145
 
150
- // Config button toggle cleanup
146
+ // --- Config Button Toggle ---
151
147
  const configButton = document.getElementById('configButton');
152
148
  let configButtonState = false;
153
149
  configButton.addEventListener('click', () => {
@@ -156,28 +152,30 @@
156
152
  configButton.className = configButtonState ? 'btn btn-primary' : 'btn btn-secondary';
157
153
  });
158
154
 
159
- // Password show/hide toggle
155
+ // --- Improved Show/Hide Password ---
160
156
  const passwdInput = document.getElementById('passwd');
161
157
  const togglePassword = document.getElementById('togglePassword');
162
158
 
163
159
  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
- }
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>`;
171
169
  });
172
170
 
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();
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>`;
178
175
  }
179
176
  });
180
177
 
178
+ // --- Device Handling & Login Logic ---
181
179
  function removeStaleDevices(configDevices, melcloudDevices) {
182
180
  const melcloudIds = melcloudDevices.map(d => d.DeviceID);
183
181
  const removedDevices = [];
@@ -194,181 +192,92 @@
194
192
  function updateInfo(id, text, color) {
195
193
  const el = document.getElementById(id);
196
194
  if (el) {
197
- el.innerHTML = text;
195
+ el.innerText = text;
198
196
  el.style.color = color;
199
197
  }
200
198
  }
201
199
 
202
200
  document.getElementById('logIn').addEventListener('click', async () => {
203
201
  homebridge.showSpinner();
204
- document.getElementById(`logIn`).setAttribute("class", "btn btn-primary");
202
+ document.getElementById(`logIn`).className = "btn btn-primary";
205
203
  updateInfo('info', 'Connecting...', 'yellow');
206
204
 
207
205
  try {
208
- const account = pluginConfig[0].accounts[this.deviceIndex];
209
- const { name: accountName, user, passwd, language } = account;
210
- 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 };
211
208
  const devicesInMelCloud = await homebridge.request('/connect', payload);
212
209
 
210
+ // Initialize devices arrays
213
211
  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
- }
212
+ const devicesByType = { ata: [], atw: [], erv: [] };
223
213
 
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
- });
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);
265
218
  });
266
219
 
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);
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);
295
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
+ });
296
248
  });
297
- });
249
+ };
298
250
 
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}`;
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);
340
254
 
255
+ // Display summary
341
256
  const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
342
257
  const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
343
258
  const removedCount = removedAta.length + removedAtw.length + removedErv.length;
344
259
 
345
- if (newDevicesCount === 0 && newPresetsCount === 0 && removedCount === 0) {
260
+ if (!newDevicesCount && !newPresetsCount && !removedCount) {
346
261
  updateInfo('info', 'No changes detected.', 'white');
347
262
  } 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
- }
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');
357
266
  }
358
267
 
359
268
  await homebridge.updatePluginConfig(pluginConfig);
360
269
  await homebridge.savePluginConfig(pluginConfig);
361
- document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
362
- homebridge.hideSpinner();
270
+ document.getElementById('logIn').className = "btn btn-secondary";
363
271
 
364
272
  } catch (error) {
365
273
  updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
366
274
  updateInfo('info1', `Error: ${error}`, 'red');
367
- document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
275
+ document.getElementById('logIn').className = "btn btn-secondary";
368
276
  } finally {
369
277
  homebridge.hideSpinner();
370
278
  }
371
279
  });
280
+
372
281
  })();
373
282
  </script>
374
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.17",
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",