homebridge-melcloud-control 3.9.0-beta.9 → 3.9.1
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/CHANGELOG.md +11 -1
- package/homebridge-ui/public/index.html +223 -221
- package/package.json +1 -1
- package/src/deviceata.js +21 -21
- package/src/deviceatw.js +26 -26
- package/src/deviceerv.js +22 -22
- package/src/melcloud.js +7 -7
- package/src/melcloudata.js +2 -2
- package/src/melcloudatw.js +2 -2
- package/src/melclouderv.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
16
|
- do not configure it manually, always using Config UI X
|
|
17
17
|
- required Homebridge v2.0.0 and above
|
|
18
18
|
|
|
19
|
+
## [3.9.0] - (18.08.2025)
|
|
20
|
+
|
|
21
|
+
## Changes
|
|
22
|
+
|
|
23
|
+
- Config UI CONECT TO MELCLOUD updated
|
|
24
|
+
- automayically remove devices not existed in melcloud
|
|
25
|
+
- show/hide password
|
|
26
|
+
|
|
27
|
+
- cleanup
|
|
28
|
+
|
|
19
29
|
## [3.8.14] - (26.06.2025)
|
|
20
30
|
|
|
21
31
|
## Changes
|
|
@@ -24,7 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
24
34
|
- fix [#205](https://github.com/grzegorz914/homebridge-melcloud-control/issues/205)
|
|
25
35
|
- RESTFul code refactor
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
## [3.8.13] - (26.06.2025)
|
|
28
38
|
|
|
29
39
|
## Changes
|
|
30
40
|
|
|
@@ -6,262 +6,264 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>MELCloud Account Configuration</title>
|
|
8
8
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css">
|
|
9
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/js/all.min.js"></script>
|
|
10
10
|
</head>
|
|
11
11
|
|
|
12
12
|
<body>
|
|
13
|
+
|
|
13
14
|
<div class="container mt-4">
|
|
14
15
|
<div class="text-center">
|
|
15
16
|
<img src="homebridge-melcloud-control.png" alt="Image" height="120" />
|
|
16
17
|
</div>
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
<div id="melCloudAccount" class="card card-body mt-3">
|
|
20
|
+
<form id="configForm">
|
|
21
|
+
<div class="text-center">
|
|
22
|
+
<label id="accountName" class="fw-bold" style="font-size: 23px;">Account</label><br>
|
|
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
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="mb-3">
|
|
29
|
+
<label for="name" class="form-label">Name</label>
|
|
30
|
+
<input id="name" type="text" class="form-control" required>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="mb-3">
|
|
34
|
+
<label for="user" class="form-label">User Name</label>
|
|
35
|
+
<input id="user" type="text" class="form-control" required>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="mb-3 position-relative">
|
|
39
|
+
<label for="passwd" class="form-label">Password</label>
|
|
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>
|
|
46
|
+
</div>
|
|
20
47
|
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
<div class="mb-3">
|
|
49
|
+
<label for="language" class="form-label">Language</label>
|
|
50
|
+
<select id="language" name="language" class="form-control">
|
|
51
|
+
<option value="0">English</option>
|
|
52
|
+
<option value="1">Български</option>
|
|
53
|
+
<option value="2">Čeština</option>
|
|
54
|
+
<option value="3">Dansk</option>
|
|
55
|
+
<option value="4">Deutsch</option>
|
|
56
|
+
<option value="5">Eesti</option>
|
|
57
|
+
<option value="6">Español</option>
|
|
58
|
+
<option value="7">Français</option>
|
|
59
|
+
<option value="8">Հայերեն</option>
|
|
60
|
+
<option value="9">Latviešu</option>
|
|
61
|
+
<option value="10">Lietuvių</option>
|
|
62
|
+
<option value="11">Magyar</option>
|
|
63
|
+
<option value="12">Nederlands</option>
|
|
64
|
+
<option value="13">Norwegian</option>
|
|
65
|
+
<option value="14">Polski</option>
|
|
66
|
+
<option value="15">Português</option>
|
|
67
|
+
<option value="16">Русский</option>
|
|
68
|
+
<option value="17">Suomi</option>
|
|
69
|
+
<option value="18">Svenska</option>
|
|
70
|
+
<option value="19">Українська</option>
|
|
71
|
+
<option value="20">Türkçe</option>
|
|
72
|
+
<option value="21">Ελληνικά</option>
|
|
73
|
+
<option value="22">Hrvatski</option>
|
|
74
|
+
<option value="23">Română</option>
|
|
75
|
+
<option value="24">Slovenščina</option>
|
|
76
|
+
</select>
|
|
77
|
+
</div>
|
|
23
78
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
79
|
+
<div class="text-center">
|
|
80
|
+
<button id="logIn" type="button" class="btn btn-secondary">Connect to MELCloud</button>
|
|
81
|
+
<button id="configButton" type="button" class="btn btn-secondary"><i class="fas fa-gear"></i></button>
|
|
82
|
+
</div>
|
|
83
|
+
</form>
|
|
29
84
|
</div>
|
|
85
|
+
|
|
86
|
+
<div id="accountButton" class="mt-3"></div>
|
|
30
87
|
</div>
|
|
31
88
|
|
|
32
89
|
<script>
|
|
33
90
|
(async () => {
|
|
34
91
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
35
92
|
if (!pluginConfig.length) {
|
|
36
|
-
pluginConfig.push({
|
|
93
|
+
pluginConfig.push({});
|
|
37
94
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
38
95
|
homebridge.showSchemaForm();
|
|
39
96
|
return;
|
|
40
97
|
}
|
|
41
98
|
|
|
42
|
-
|
|
43
|
-
const tabs = document.getElementById("accountTabs");
|
|
44
|
-
const content = document.getElementById("accountTabsContent");
|
|
45
|
-
|
|
46
|
-
function createAccountForm(account, i) {
|
|
47
|
-
// Tab header
|
|
48
|
-
const li = document.createElement("li");
|
|
49
|
-
li.classList.add("nav-item");
|
|
50
|
-
li.id = `tab-li-${i}`;
|
|
51
|
-
li.innerHTML = `
|
|
52
|
-
<button class="nav-link ${i === 0 ? 'active' : ''}"
|
|
53
|
-
id="tab-${i}" data-bs-toggle="tab"
|
|
54
|
-
data-bs-target="#pane-${i}"
|
|
55
|
-
type="button" role="tab">
|
|
56
|
-
${account.name || 'Account ' + (i + 1)}
|
|
57
|
-
</button>`;
|
|
58
|
-
tabs.insertBefore(li, document.getElementById("addTab"));
|
|
59
|
-
|
|
60
|
-
// Tab content pane
|
|
61
|
-
const pane = document.createElement("div");
|
|
62
|
-
pane.classList.add("tab-pane", "fade", i === 0 ? "show" : "", i === 0 ? "active" : "");
|
|
63
|
-
pane.id = `pane-${i}`;
|
|
64
|
-
pane.role = "tabpanel";
|
|
65
|
-
pane.innerHTML = `
|
|
66
|
-
<form id="configForm-${i}">
|
|
67
|
-
<div class="mb-3">
|
|
68
|
-
<label class="form-label">Name</label>
|
|
69
|
-
<input type="text" class="form-control" id="name-${i}" value="${account.name || ''}">
|
|
70
|
-
</div>
|
|
71
|
-
<div class="mb-3">
|
|
72
|
-
<label class="form-label">User Name</label>
|
|
73
|
-
<input type="text" class="form-control" id="user-${i}" value="${account.user || ''}">
|
|
74
|
-
</div>
|
|
75
|
-
<div class="mb-3">
|
|
76
|
-
<label class="form-label">Password</label>
|
|
77
|
-
<input type="password" class="form-control" id="passwd-${i}" value="${account.passwd || ''}">
|
|
78
|
-
</div>
|
|
79
|
-
<div class="mb-3">
|
|
80
|
-
<label class="form-label">Language</label>
|
|
81
|
-
<select class="form-control" id="language-${i}">
|
|
82
|
-
<option value="0" ${account.language == 0 ? 'selected' : ''}>English</option>
|
|
83
|
-
<option value="1" ${account.language == 1 ? 'selected' : ''}>Български</option>
|
|
84
|
-
<option value="2" ${account.language == 2 ? 'selected' : ''}>Česky</option>
|
|
85
|
-
<option value="3" ${account.language == 3 ? 'selected' : ''}>Dansk</option>
|
|
86
|
-
<option value="4" ${account.language == 4 ? 'selected' : ''}>Deutsch</option>
|
|
87
|
-
<option value="5" ${account.language == 5 ? 'selected' : ''}>Eesti</option>
|
|
88
|
-
<option value="6" ${account.language == 6 ? 'selected' : ''}>Español</option>
|
|
89
|
-
<option value="7" ${account.language == 7 ? 'selected' : ''}>Français</option>
|
|
90
|
-
<option value="8" ${account.language == 8 ? 'selected' : ''}>Italiano</option>
|
|
91
|
-
<option value="9" ${account.language == 9 ? 'selected' : ''}>Latviešu</option>
|
|
92
|
-
<option value="10" ${account.language == 10 ? 'selected' : ''}>Lietuvių</option>
|
|
93
|
-
<option value="11" ${account.language == 11 ? 'selected' : ''}>Magyar</option>
|
|
94
|
-
<option value="12" ${account.language == 12 ? 'selected' : ''}>Nederlands</option>
|
|
95
|
-
<option value="13" ${account.language == 13 ? 'selected' : ''}>Norsk</option>
|
|
96
|
-
<option value="14" ${account.language == 14 ? 'selected' : ''}>Polski</option>
|
|
97
|
-
<option value="15" ${account.language == 15 ? 'selected' : ''}>Português</option>
|
|
98
|
-
<option value="16" ${account.language == 16 ? 'selected' : ''}>Русский</option>
|
|
99
|
-
<option value="17" ${account.language == 17 ? 'selected' : ''}>Suomi</option>
|
|
100
|
-
<option value="18" ${account.language == 18 ? 'selected' : ''}>Svenska</option>
|
|
101
|
-
<option value="19" ${account.language == 19 ? 'selected' : ''}>Türkçe</option>
|
|
102
|
-
</select>
|
|
103
|
-
</div>
|
|
104
|
-
<button type="button" class="btn btn-secondary" id="logIn-${i}">Connect</button>
|
|
105
|
-
<button type="button" class="btn btn-danger ms-2" id="remove-${i}">❌ Remove Account</button>
|
|
106
|
-
</form>`;
|
|
107
|
-
content.appendChild(pane);
|
|
108
|
-
|
|
109
|
-
// Handle input updates
|
|
110
|
-
pane.querySelector("form").addEventListener("input", async () => {
|
|
111
|
-
account.name = document.getElementById(`name-${i}`).value;
|
|
112
|
-
account.user = document.getElementById(`user-${i}`).value;
|
|
113
|
-
account.passwd = document.getElementById(`passwd-${i}`).value;
|
|
114
|
-
account.language = document.getElementById(`language-${i}`).value;
|
|
99
|
+
this.deviceIndex = 0;
|
|
115
100
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
101
|
+
const accountsCount = pluginConfig[0].accounts.length;
|
|
102
|
+
for (let i = 0; i < accountsCount; i++) {
|
|
103
|
+
const button = document.createElement("button");
|
|
104
|
+
button.type = "button";
|
|
105
|
+
button.id = `button${i}`;
|
|
106
|
+
button.className = "btn btn-primary";
|
|
107
|
+
button.style.textTransform = 'none';
|
|
108
|
+
button.innerText = pluginConfig[0].accounts[i].name;
|
|
109
|
+
document.getElementById("accountButton").appendChild(button);
|
|
110
|
+
|
|
111
|
+
button.addEventListener('click', async () => {
|
|
112
|
+
for (let j = 0; j < accountsCount; j++) {
|
|
113
|
+
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
|
|
114
|
+
}
|
|
115
|
+
|
|
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
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
123
|
+
this.deviceIndex = i;
|
|
119
124
|
});
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
accounts.splice(i, 1);
|
|
124
|
-
document.getElementById(`tab-li-${i}`).remove();
|
|
125
|
-
document.getElementById(`pane-${i}`).remove();
|
|
126
|
-
|
|
127
|
-
// Rebuild IDs
|
|
128
|
-
const panes = document.querySelectorAll(".tab-pane");
|
|
129
|
-
panes.forEach((p, index) => {
|
|
130
|
-
p.id = `pane-${index}`;
|
|
131
|
-
p.querySelector("form").id = `configForm-${index}`;
|
|
132
|
-
p.querySelector(`[id^="name-"]`).id = `name-${index}`;
|
|
133
|
-
p.querySelector(`[id^="user-"]`).id = `user-${index}`;
|
|
134
|
-
p.querySelector(`[id^="passwd-"]`).id = `passwd-${index}`;
|
|
135
|
-
p.querySelector(`[id^="language-"]`).id = `language-${index}`;
|
|
136
|
-
p.querySelector(`[id^="logIn-"]`).id = `logIn-${index}`;
|
|
137
|
-
p.querySelector(`[id^="remove-"]`).id = `remove-${index}`;
|
|
138
|
-
});
|
|
139
|
-
const tabButtons = document.querySelectorAll(".nav-link");
|
|
140
|
-
tabButtons.forEach((btn, idx) => {
|
|
141
|
-
btn.id = `tab-${idx}`;
|
|
142
|
-
btn.dataset.bsTarget = `#pane-${idx}`;
|
|
143
|
-
});
|
|
126
|
+
if (i === accountsCount - 1) document.getElementById(`button0`).click();
|
|
127
|
+
}
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
await homebridge.savePluginConfig(pluginConfig);
|
|
129
|
+
document.getElementById('melCloudAccount').style.display = 'block';
|
|
147
130
|
|
|
148
|
-
|
|
149
|
-
|
|
131
|
+
document.getElementById('configForm').addEventListener('input', async () => {
|
|
132
|
+
const acc = pluginConfig[0].accounts[this.deviceIndex];
|
|
133
|
+
acc.name = document.querySelector('#name').value;
|
|
134
|
+
acc.user = document.querySelector('#user').value;
|
|
135
|
+
acc.passwd = document.querySelector('#passwd').value;
|
|
136
|
+
acc.language = document.querySelector('#language').value;
|
|
150
137
|
|
|
151
|
-
|
|
152
|
-
document.getElementById(`logIn-${i}`).addEventListener("click", async () => {
|
|
153
|
-
homebridge.showSpinner();
|
|
154
|
-
try {
|
|
155
|
-
const account = accounts[i];
|
|
156
|
-
const devicesInMelCloud = await homebridge.request('/connect', {
|
|
157
|
-
accountName: account.name,
|
|
158
|
-
user: account.user,
|
|
159
|
-
passwd: account.passwd,
|
|
160
|
-
language: account.language
|
|
161
|
-
});
|
|
138
|
+
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language);
|
|
162
139
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
140
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
141
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// --- Config Button Toggle ---
|
|
145
|
+
const configButton = document.getElementById('configButton');
|
|
146
|
+
let configButtonState = false;
|
|
147
|
+
configButton.addEventListener('click', () => {
|
|
148
|
+
configButtonState = !configButtonState;
|
|
149
|
+
homebridge[configButtonState ? 'showSchemaForm' : 'hideSchemaForm']();
|
|
150
|
+
configButton.className = configButtonState ? 'btn btn-primary' : 'btn btn-secondary';
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Password toggle
|
|
154
|
+
document.getElementById('togglePasswd').addEventListener('click', () => {
|
|
155
|
+
const passwdInput = document.getElementById('passwd');
|
|
156
|
+
const icon = document.querySelector('#togglePasswd i');
|
|
157
|
+
if (passwdInput.type === 'password') {
|
|
158
|
+
passwdInput.type = 'text';
|
|
159
|
+
icon.classList.replace('fa-eye', 'fa-eye-slash');
|
|
160
|
+
} else {
|
|
161
|
+
passwdInput.type = 'password';
|
|
162
|
+
icon.classList.replace('fa-eye-slash', 'fa-eye');
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// --- Device Handling & Login Logic ---
|
|
167
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
168
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
169
|
+
const removedDevices = [];
|
|
170
|
+
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
171
|
+
const device = configDevices[i];
|
|
172
|
+
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
173
|
+
removedDevices.push(device);
|
|
174
|
+
configDevices.splice(i, 1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return removedDevices;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function updateInfo(id, text, color) {
|
|
181
|
+
const el = document.getElementById(id);
|
|
182
|
+
if (el) {
|
|
183
|
+
el.innerText = text;
|
|
184
|
+
el.style.color = color;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
document.getElementById('logIn').addEventListener('click', async () => {
|
|
189
|
+
homebridge.showSpinner();
|
|
190
|
+
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
191
|
+
updateInfo('info', 'Connecting...', 'yellow');
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const acc = pluginConfig[0].accounts[this.deviceIndex];
|
|
195
|
+
const payload = { accountName: acc.name, user: acc.user, passwd: acc.passwd, language: acc.language };
|
|
196
|
+
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
197
|
+
|
|
198
|
+
// Initialize devices arrays
|
|
199
|
+
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
200
|
+
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
201
|
+
|
|
202
|
+
devicesInMelCloud.forEach(d => {
|
|
203
|
+
if (d.Type === 0) devicesByType.ata.push(d);
|
|
204
|
+
if (d.Type === 1) devicesByType.atw.push(d);
|
|
205
|
+
if (d.Type === 3) devicesByType.erv.push(d);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
acc.ataDevices ??= [];
|
|
209
|
+
acc.atwDevices ??= [];
|
|
210
|
+
acc.ervDevices ??= [];
|
|
211
|
+
|
|
212
|
+
const removedAta = removeStaleDevices(acc.ataDevices, devicesByType.ata);
|
|
213
|
+
const removedAtw = removeStaleDevices(acc.atwDevices, devicesByType.atw);
|
|
214
|
+
const removedErv = removeStaleDevices(acc.ervDevices, devicesByType.erv);
|
|
215
|
+
|
|
216
|
+
// Function to handle device & presets
|
|
217
|
+
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
218
|
+
devicesInCloud.forEach(device => {
|
|
219
|
+
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
220
|
+
const devObj = { id, type, typeString, name, displayMode: 1, presets: [], buttonsSensors: [] };
|
|
221
|
+
if (!devicesInConfig.some(d => d.id === id)) {
|
|
222
|
+
devicesInConfig.push(devObj);
|
|
223
|
+
newArr.push(devObj);
|
|
181
224
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const removedErv = removeStaleDevices(ervDevices, devicesByType.erv);
|
|
192
|
-
|
|
193
|
-
// Add new devices & presets
|
|
194
|
-
function handleDevices(configArray, devicesArray, typeName, newDevicesArr, newPresetsArr) {
|
|
195
|
-
devicesArray.forEach(d => {
|
|
196
|
-
const existing = configArray.find(x => x.id === d.DeviceID);
|
|
197
|
-
if (!existing) {
|
|
198
|
-
const devObj = {
|
|
199
|
-
id: d.DeviceID,
|
|
200
|
-
type: d.Type,
|
|
201
|
-
typeString: typeName,
|
|
202
|
-
name: d.DeviceName,
|
|
203
|
-
displayMode: 1,
|
|
204
|
-
presets: [],
|
|
205
|
-
buttonsSensors: []
|
|
206
|
-
};
|
|
207
|
-
configArray.push(devObj);
|
|
208
|
-
newDevicesArr.push(devObj);
|
|
225
|
+
presets.forEach(p => {
|
|
226
|
+
p.id = p.ID;
|
|
227
|
+
p.name = p.NumberDescription;
|
|
228
|
+
p.displayType = 0;
|
|
229
|
+
p.namePrefix = false;
|
|
230
|
+
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
231
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.ID)) {
|
|
232
|
+
devConfig.presets.push(p);
|
|
233
|
+
newPresets.push(p);
|
|
209
234
|
}
|
|
210
|
-
(d.Presets || []).forEach(p => {
|
|
211
|
-
const preset = { ...p, id: p.ID, name: p.NumberDescription, displayType: 0, namePrefix: false };
|
|
212
|
-
if (!existing?.presets.some(pr => pr.id === p.ID)) {
|
|
213
|
-
existing?.presets.push(preset);
|
|
214
|
-
newPresetsArr.push(preset);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
235
|
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
handleDevices(ataDevices, devicesByType.ata, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
221
|
-
handleDevices(atwDevices, devicesByType.atw, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
222
|
-
handleDevices(ervDevices, devicesByType.erv, "ERV", newDevices.erv, newDevices.ervPresets);
|
|
223
|
-
|
|
224
|
-
// Info display
|
|
225
|
-
function updateInfo(id, text, color) {
|
|
226
|
-
const el = document.getElementById(id);
|
|
227
|
-
if (el) { el.innerHTML = text; el.style.color = color; }
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const textInfo = `Found devices: ATA(${newDevices.ata.length}), ATW(${newDevices.atw.length}), ERV(${newDevices.erv.length})`;
|
|
231
|
-
const textPresets = `Found presets: ATA(${newDevices.ataPresets.length}), ATW(${newDevices.atwPresets.length}), ERV(${newDevices.ervPresets.length})`;
|
|
232
|
-
const textRemoved = `Removed devices: ATA(${removedAta.length}), ATW(${removedAtw.length}), ERV(${removedErv.length})`;
|
|
233
|
-
|
|
234
|
-
updateInfo('info', textInfo, newDevices.ata.length + newDevices.atw.length + newDevices.erv.length > 0 ? 'green' : 'white');
|
|
235
|
-
updateInfo('info1', textPresets, newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length > 0 ? 'green' : 'white');
|
|
236
|
-
updateInfo('info2', textRemoved, removedAta.length + removedAtw.length + removedErv.length > 0 ? 'orange' : 'white');
|
|
237
|
-
|
|
238
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
239
|
-
await homebridge.savePluginConfig(pluginConfig);
|
|
240
|
-
} catch (err) {
|
|
241
|
-
document.getElementById('info').innerHTML = `Error: ${err.message || err}`;
|
|
242
|
-
} finally {
|
|
243
|
-
homebridge.hideSpinner();
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
236
|
+
});
|
|
237
|
+
};
|
|
247
238
|
|
|
248
|
-
|
|
249
|
-
|
|
239
|
+
handleDevices(devicesByType.ata, acc.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
240
|
+
handleDevices(devicesByType.atw, acc.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
241
|
+
handleDevices(devicesByType.erv, acc.ervDevices, "ERV", newDevices.erv, newDevices.ervPresets);
|
|
250
242
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
tabs.appendChild(addLi);
|
|
243
|
+
// Display summary
|
|
244
|
+
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
245
|
+
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
246
|
+
const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
256
247
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
248
|
+
if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
|
|
249
|
+
updateInfo('info', 'No changes detected.', 'white');
|
|
250
|
+
} else {
|
|
251
|
+
if (newDevicesCount) updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
252
|
+
if (newPresetsCount) updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
253
|
+
if (removedDevicesCount) updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
257
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
258
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
262
|
+
updateInfo('info1', `Error: ${error}`, 'red');
|
|
263
|
+
document.getElementById('logIn').className = "btn btn-secondary";
|
|
264
|
+
} finally {
|
|
265
|
+
homebridge.hideSpinner();
|
|
266
|
+
}
|
|
265
267
|
});
|
|
266
268
|
|
|
267
269
|
})();
|
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.
|
|
4
|
+
"version": "3.9.1",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|