homebridge-melcloud-control 3.9.0-beta.13 → 3.9.0-beta.15
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/config.schema.json +1 -1
- package/homebridge-ui/public/index.html +293 -186
- package/package.json +1 -1
package/config.schema.json
CHANGED
|
@@ -6,235 +6,342 @@
|
|
|
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
|
-
<img src="homebridge-melcloud-control.png" alt="Image" height="
|
|
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: 16px;"></label>
|
|
24
|
+
<label id="info1" class="d-block" style="font-size: 14px;"></label>
|
|
25
|
+
<label id="info2" class="d-block" style="font-size: 14px;"></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">
|
|
39
|
+
<label for="passwd" class="form-label">Password</label>
|
|
40
|
+
<input id="passwd" type="password" class="form-control" autocomplete="off" required>
|
|
41
|
+
</div>
|
|
20
42
|
|
|
21
|
-
|
|
22
|
-
|
|
43
|
+
<div class="mb-3">
|
|
44
|
+
<label for="language" class="form-label">Language</label>
|
|
45
|
+
<select id="language" name="language" class="form-control">
|
|
46
|
+
<option value="0">English</option>
|
|
47
|
+
<option value="1">Български</option>
|
|
48
|
+
<option value="2">Čeština</option>
|
|
49
|
+
<option value="3">Dansk</option>
|
|
50
|
+
<option value="4">Deutsch</option>
|
|
51
|
+
<option value="5">Eesti</option>
|
|
52
|
+
<option value="6">Español</option>
|
|
53
|
+
<option value="7">Français</option>
|
|
54
|
+
<option value="8">Հայերեն</option>
|
|
55
|
+
<option value="9">Latviešu</option>
|
|
56
|
+
<option value="10">Lietuvių</option>
|
|
57
|
+
<option value="11">Magyar</option>
|
|
58
|
+
<option value="12">Nederlands</option>
|
|
59
|
+
<option value="13">Norwegian</option>
|
|
60
|
+
<option value="14">Polski</option>
|
|
61
|
+
<option value="15">Português</option>
|
|
62
|
+
<option value="16">Русский</option>
|
|
63
|
+
<option value="17">Suomi</option>
|
|
64
|
+
<option value="18">Svenska</option>
|
|
65
|
+
<option value="19">Українська</option>
|
|
66
|
+
<option value="20">Türkçe</option>
|
|
67
|
+
<option value="21">Ελληνικά</option>
|
|
68
|
+
<option value="22">Hrvatski</option>
|
|
69
|
+
<option value="23">Română</option>
|
|
70
|
+
<option value="24">Slovenščina</option>
|
|
71
|
+
</select>
|
|
72
|
+
</div>
|
|
23
73
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
74
|
+
<div class="text-center">
|
|
75
|
+
<button id="logIn" type="button" class="btn btn-secondary">Connect to MELCloud</button>
|
|
76
|
+
<button id="configButton" type="button" class="btn btn-secondary"><i class="fas fa-gear"></i></button>
|
|
77
|
+
</div>
|
|
78
|
+
</form>
|
|
29
79
|
</div>
|
|
80
|
+
|
|
81
|
+
<div id="accountButton" class="mt-3"></div>
|
|
30
82
|
</div>
|
|
31
83
|
|
|
32
84
|
<script>
|
|
33
85
|
(async () => {
|
|
34
86
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
35
87
|
if (!pluginConfig.length) {
|
|
36
|
-
pluginConfig.push({
|
|
88
|
+
pluginConfig.push({});
|
|
37
89
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
38
90
|
homebridge.showSchemaForm();
|
|
39
91
|
return;
|
|
40
92
|
}
|
|
93
|
+
this.configButtonState = false;
|
|
41
94
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
95
|
+
const accountsCount = pluginConfig[0].accounts.length;
|
|
96
|
+
this.deviceIndex = 0;
|
|
97
|
+
for (let i = 0; i < accountsCount; i++) {
|
|
98
|
+
const button = document.createElement("button");
|
|
99
|
+
button.setAttribute("type", "button");
|
|
100
|
+
button.setAttribute("id", `button${i}`);
|
|
101
|
+
button.setAttribute("class", "btn btn-primary");
|
|
102
|
+
button.style.textTransform = 'none';
|
|
103
|
+
button.innerText = pluginConfig[0].accounts[i].name;
|
|
104
|
+
document.getElementById("accountButton").appendChild(button);
|
|
45
105
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
106
|
+
document.getElementById(`button${i}`).addEventListener('click', async () => {
|
|
107
|
+
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");
|
|
111
|
+
}
|
|
53
112
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
li.innerHTML = `
|
|
60
|
-
<button class="nav-link ${i === 0 ? 'active' : ''}"
|
|
61
|
-
id="tab-${i}" data-bs-toggle="tab"
|
|
62
|
-
data-bs-target="#pane-${i}"
|
|
63
|
-
type="button" role="tab">
|
|
64
|
-
${account.name || 'Account ' + (i + 1)}
|
|
65
|
-
</button>`;
|
|
66
|
-
tabs.insertBefore(li, document.getElementById("addTab"));
|
|
67
|
-
|
|
68
|
-
// Tab content pane
|
|
69
|
-
const pane = document.createElement("div");
|
|
70
|
-
pane.classList.add("tab-pane", "fade", i === 0 ? "show" : "", i === 0 ? "active" : "");
|
|
71
|
-
pane.id = `pane-${i}`;
|
|
72
|
-
pane.role = "tabpanel";
|
|
73
|
-
pane.innerHTML = `
|
|
74
|
-
<form id="configForm-${i}">
|
|
75
|
-
<div class="mb-3">
|
|
76
|
-
<label class="form-label">Name</label>
|
|
77
|
-
<input type="text" class="form-control" id="name-${i}" value="${account.name || ''}">
|
|
78
|
-
</div>
|
|
79
|
-
<div class="mb-3">
|
|
80
|
-
<label class="form-label">User Name</label>
|
|
81
|
-
<input type="text" class="form-control" id="user-${i}" value="${account.user || ''}">
|
|
82
|
-
</div>
|
|
83
|
-
<div class="mb-3">
|
|
84
|
-
<label class="form-label">Password</label>
|
|
85
|
-
<input type="password" class="form-control" id="passwd-${i}" value="${account.passwd || ''}">
|
|
86
|
-
</div>
|
|
87
|
-
<div class="mb-3">
|
|
88
|
-
<label class="form-label">Language</label>
|
|
89
|
-
<select class="form-control" id="language-${i}">
|
|
90
|
-
<option value="0" ${account.language == 0 ? 'selected' : ''}>English</option>
|
|
91
|
-
<option value="1" ${account.language == 1 ? 'selected' : ''}>Български</option>
|
|
92
|
-
<option value="2" ${account.language == 2 ? 'selected' : ''}>Česky</option>
|
|
93
|
-
<option value="3" ${account.language == 3 ? 'selected' : ''}>Dansk</option>
|
|
94
|
-
<option value="4" ${account.language == 4 ? 'selected' : ''}>Deutsch</option>
|
|
95
|
-
<option value="5" ${account.language == 5 ? 'selected' : ''}>Eesti</option>
|
|
96
|
-
<option value="6" ${account.language == 6 ? 'selected' : ''}>Español</option>
|
|
97
|
-
<option value="7" ${account.language == 7 ? 'selected' : ''}>Français</option>
|
|
98
|
-
<option value="8" ${account.language == 8 ? 'selected' : ''}>Italiano</option>
|
|
99
|
-
<option value="9" ${account.language == 9 ? 'selected' : ''}>Latviešu</option>
|
|
100
|
-
<option value="10" ${account.language == 10 ? 'selected' : ''}>Lietuvių</option>
|
|
101
|
-
<option value="11" ${account.language == 11 ? 'selected' : ''}>Magyar</option>
|
|
102
|
-
<option value="12" ${account.language == 12 ? 'selected' : ''}>Nederlands</option>
|
|
103
|
-
<option value="13" ${account.language == 13 ? 'selected' : ''}>Norsk</option>
|
|
104
|
-
<option value="14" ${account.language == 14 ? 'selected' : ''}>Polski</option>
|
|
105
|
-
<option value="15" ${account.language == 15 ? 'selected' : ''}>Português</option>
|
|
106
|
-
<option value="16" ${account.language == 16 ? 'selected' : ''}>Русский</option>
|
|
107
|
-
<option value="17" ${account.language == 17 ? 'selected' : ''}>Suomi</option>
|
|
108
|
-
<option value="18" ${account.language == 18 ? 'selected' : ''}>Svenska</option>
|
|
109
|
-
<option value="19" ${account.language == 19 ? 'selected' : ''}>Türkçe</option>
|
|
110
|
-
</select>
|
|
111
|
-
</div>
|
|
112
|
-
<div class="mb-3">
|
|
113
|
-
<button type="button" class="btn btn-secondary" id="logIn-${i}">Connect</button>
|
|
114
|
-
<button type="button" class="btn btn-danger ms-2" id="remove-${i}">❌ Remove Account</button>
|
|
115
|
-
<button type="button" class="btn btn-primary ms-2" id="config-${i}" disabled>⚙️ Config</button>
|
|
116
|
-
</div>
|
|
117
|
-
</form>`;
|
|
118
|
-
content.appendChild(pane);
|
|
119
|
-
|
|
120
|
-
const configButton = document.getElementById(`config-${i}`);
|
|
121
|
-
|
|
122
|
-
// Enable config button if devices > 1
|
|
123
|
-
updateConfigButtonState(account, configButton);
|
|
124
|
-
|
|
125
|
-
// Handle input updates
|
|
126
|
-
pane.querySelector("form").addEventListener("input", async () => {
|
|
127
|
-
account.name = document.getElementById(`name-${i}`).value;
|
|
128
|
-
account.user = document.getElementById(`user-${i}`).value;
|
|
129
|
-
account.passwd = document.getElementById(`passwd-${i}`).value;
|
|
130
|
-
account.language = document.getElementById(`language-${i}`).value;
|
|
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 || '';
|
|
131
118
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
document.querySelector(`#tab-${i}`).innerText = account.name || `Account ${i + 1}`;
|
|
135
|
-
});
|
|
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;
|
|
136
121
|
|
|
137
|
-
// Remove account
|
|
138
|
-
document.getElementById(`remove-${i}`).addEventListener("click", async () => {
|
|
139
|
-
accounts.splice(i, 1);
|
|
140
|
-
document.getElementById(`tab-li-${i}`).remove();
|
|
141
|
-
document.getElementById(`pane-${i}`).remove();
|
|
142
122
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
143
|
-
|
|
144
|
-
if (accounts.length > 0) new bootstrap.Tab(document.getElementById("tab-0")).show();
|
|
123
|
+
this.deviceIndex = i;
|
|
145
124
|
});
|
|
125
|
+
if (i === accountsCount - 1) {
|
|
126
|
+
document.getElementById(`button0`).click();
|
|
127
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
146
130
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
131
|
+
document.getElementById('melCloudAccount').style.display = 'block';
|
|
132
|
+
|
|
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;
|
|
138
|
+
|
|
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;
|
|
141
|
+
|
|
142
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
143
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
144
|
+
});
|
|
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;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
155
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
156
|
+
const removedDevices = [];
|
|
157
|
+
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
158
|
+
const device = configDevices[i];
|
|
159
|
+
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
160
|
+
removedDevices.push(device);
|
|
161
|
+
configDevices.splice(i, 1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return removedDevices;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function updateInfo(id, text, color) {
|
|
168
|
+
const el = document.getElementById(id);
|
|
169
|
+
if (el) {
|
|
170
|
+
el.innerHTML = text;
|
|
171
|
+
el.style.color = color;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
158
174
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
document.getElementById('logIn').addEventListener('click', async () => {
|
|
176
|
+
homebridge.showSpinner();
|
|
177
|
+
document.getElementById(`logIn`).setAttribute("class", "btn btn-primary");
|
|
178
|
+
updateInfo('info', 'Connecting...', 'yellow');
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const account = pluginConfig[0].accounts[this.deviceIndex];
|
|
182
|
+
const { name: accountName, user, passwd, language } = account;
|
|
183
|
+
const payload = { accountName, user, passwd, language };
|
|
184
|
+
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
185
|
+
|
|
186
|
+
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;
|
|
164
194
|
}
|
|
195
|
+
}
|
|
165
196
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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);
|
|
177
236
|
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
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);
|
|
178
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);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
179
271
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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);
|
|
191
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
|
+
});
|
|
192
302
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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}`;
|
|
196
313
|
|
|
197
|
-
|
|
198
|
-
|
|
314
|
+
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
315
|
+
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
316
|
+
const removedCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
199
317
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
} catch (err) {
|
|
203
|
-
document.getElementById('info').innerHTML = `Error: ${err.message || err}`;
|
|
204
|
-
} finally {
|
|
205
|
-
homebridge.hideSpinner();
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Config button click
|
|
210
|
-
configButton.addEventListener('click', () => {
|
|
211
|
-
if (configButton.classList.contains('btn-primary')) {
|
|
212
|
-
homebridge.showSchemaForm();
|
|
318
|
+
if (newDevicesCount === 0 && newPresetsCount === 0 && removedCount === 0) {
|
|
319
|
+
updateInfo('info', 'No changes detected.', 'white');
|
|
213
320
|
} else {
|
|
214
|
-
|
|
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
|
+
}
|
|
215
330
|
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
331
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const addLi = document.createElement("li");
|
|
224
|
-
addLi.classList.add("nav-item");
|
|
225
|
-
addLi.innerHTML = `<button class="nav-link" id="addTab" type="button" role="tab">➕ Add Account</button>`;
|
|
226
|
-
tabs.appendChild(addLi);
|
|
332
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
333
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
334
|
+
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
335
|
+
homebridge.hideSpinner();
|
|
227
336
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
new bootstrap.Tab(document.getElementById(`tab-${i}`)).show();
|
|
337
|
+
} catch (error) {
|
|
338
|
+
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
339
|
+
updateInfo('info1', `Error: ${error}`, 'red');
|
|
340
|
+
document.getElementById('logIn').setAttribute('class', 'btn btn-secondary');
|
|
341
|
+
} finally {
|
|
342
|
+
homebridge.hideSpinner();
|
|
343
|
+
}
|
|
236
344
|
});
|
|
237
|
-
|
|
238
345
|
})();
|
|
239
346
|
</script>
|
|
240
347
|
</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.
|
|
4
|
+
"version": "3.9.0-beta.15",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|