homebridge-melcloud-control 3.9.0-beta.0 → 3.9.0-beta.10
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/homebridge-ui/public/index.html +236 -277
- package/package.json +1 -1
|
@@ -6,325 +6,284 @@
|
|
|
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/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
|
|
10
10
|
</head>
|
|
11
11
|
|
|
12
12
|
<body>
|
|
13
|
-
|
|
14
13
|
<div class="container mt-4">
|
|
15
14
|
<div class="text-center">
|
|
16
|
-
<img src="homebridge-melcloud-control.png" alt="Image" height="
|
|
15
|
+
<img src="homebridge-melcloud-control.png" alt="Image" height="100" />
|
|
17
16
|
</div>
|
|
18
17
|
|
|
19
|
-
<div
|
|
20
|
-
<
|
|
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: 17px;"></label>
|
|
24
|
-
<label id="info1" class="d-block" style="font-size: 15px;"></label>
|
|
25
|
-
<label id="info2" class="d-block" style="font-size: 15px;"></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>
|
|
42
|
-
|
|
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>
|
|
73
|
-
|
|
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>
|
|
18
|
+
<div class="text-center mt-3">
|
|
19
|
+
<button type="button" class="btn btn-primary" id="configButton">⚙️ Config</button>
|
|
79
20
|
</div>
|
|
80
21
|
|
|
81
|
-
|
|
22
|
+
<!-- Tabs -->
|
|
23
|
+
<ul class="nav nav-tabs mt-3" id="accountTabs" role="tablist"></ul>
|
|
24
|
+
|
|
25
|
+
<!-- Tab contents -->
|
|
26
|
+
<div class="tab-content mt-3" id="accountTabsContent"></div>
|
|
27
|
+
|
|
28
|
+
<!-- Info Section -->
|
|
29
|
+
<div class="mt-3">
|
|
30
|
+
<div id="info"></div>
|
|
31
|
+
<div id="info1"></div>
|
|
32
|
+
<div id="info2"></div>
|
|
33
|
+
</div>
|
|
82
34
|
</div>
|
|
83
35
|
|
|
84
36
|
<script>
|
|
85
37
|
(async () => {
|
|
86
38
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
87
|
-
|
|
88
39
|
if (!pluginConfig.length) {
|
|
89
|
-
pluginConfig.push({});
|
|
40
|
+
pluginConfig.push({ accounts: [] });
|
|
90
41
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
91
42
|
homebridge.showSchemaForm();
|
|
92
43
|
return;
|
|
93
44
|
}
|
|
94
|
-
this.configButtonState = false;
|
|
95
|
-
|
|
96
|
-
const accountsCount = pluginConfig[0].accounts.length;
|
|
97
|
-
this.deviceIndex = 0;
|
|
98
|
-
|
|
99
|
-
for (let i = 0; i < accountsCount; i++) {
|
|
100
|
-
const button = document.createElement("button");
|
|
101
|
-
button.setAttribute("type", "button");
|
|
102
|
-
button.setAttribute("id", `button${i}`);
|
|
103
|
-
button.setAttribute("class", "btn btn-primary");
|
|
104
|
-
button.style.textTransform = 'none';
|
|
105
|
-
button.innerText = pluginConfig[0].accounts[i].name;
|
|
106
|
-
document.getElementById("accountButton").appendChild(button);
|
|
107
|
-
|
|
108
|
-
document.getElementById(`button${i}`).addEventListener('click', async () => {
|
|
109
|
-
for (let j = 0; j < accountsCount; j++) {
|
|
110
|
-
document.getElementById(`button${j}`).setAttribute(
|
|
111
|
-
"class",
|
|
112
|
-
j === i ? "btn btn-secondary" : "btn btn-primary"
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
document.getElementById('accountName').innerHTML = pluginConfig[0].accounts[i].name || '';
|
|
117
|
-
document.getElementById('name').value = pluginConfig[0].accounts[i].name || '';
|
|
118
|
-
document.getElementById('user').value = pluginConfig[0].accounts[i].user || '';
|
|
119
|
-
document.getElementById('passwd').value = pluginConfig[0].accounts[i].passwd || '';
|
|
120
|
-
document.getElementById('language').value = pluginConfig[0].accounts[i].language || '';
|
|
121
45
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
46
|
+
const accounts = pluginConfig[0].accounts;
|
|
47
|
+
const tabs = document.getElementById("accountTabs");
|
|
48
|
+
const content = document.getElementById("accountTabsContent");
|
|
49
|
+
|
|
50
|
+
function createAccountForm(account, i) {
|
|
51
|
+
// Tab header
|
|
52
|
+
const li = document.createElement("li");
|
|
53
|
+
li.classList.add("nav-item");
|
|
54
|
+
li.id = `tab-li-${i}`;
|
|
55
|
+
li.innerHTML = `
|
|
56
|
+
<button class="nav-link ${i === 0 ? 'active' : ''}"
|
|
57
|
+
id="tab-${i}" data-bs-toggle="tab"
|
|
58
|
+
data-bs-target="#pane-${i}"
|
|
59
|
+
type="button" role="tab">
|
|
60
|
+
${account.name || 'Account ' + (i + 1)}
|
|
61
|
+
</button>`;
|
|
62
|
+
tabs.insertBefore(li, document.getElementById("addTab"));
|
|
63
|
+
|
|
64
|
+
// Tab content pane
|
|
65
|
+
const pane = document.createElement("div");
|
|
66
|
+
pane.classList.add("tab-pane", "fade", i === 0 ? "show" : "", i === 0 ? "active" : "");
|
|
67
|
+
pane.id = `pane-${i}`;
|
|
68
|
+
pane.role = "tabpanel";
|
|
69
|
+
pane.innerHTML = `
|
|
70
|
+
<form id="configForm-${i}">
|
|
71
|
+
<div class="mb-3">
|
|
72
|
+
<label class="form-label">Name</label>
|
|
73
|
+
<input type="text" class="form-control" id="name-${i}" value="${account.name || ''}">
|
|
74
|
+
</div>
|
|
75
|
+
<div class="mb-3">
|
|
76
|
+
<label class="form-label">User Name</label>
|
|
77
|
+
<input type="text" class="form-control" id="user-${i}" value="${account.user || ''}">
|
|
78
|
+
</div>
|
|
79
|
+
<div class="mb-3">
|
|
80
|
+
<label class="form-label">Password</label>
|
|
81
|
+
<input type="password" class="form-control" id="passwd-${i}" value="${account.passwd || ''}">
|
|
82
|
+
</div>
|
|
83
|
+
<div class="mb-3">
|
|
84
|
+
<label class="form-label">Language</label>
|
|
85
|
+
<select class="form-control" id="language-${i}">
|
|
86
|
+
<option value="0" ${account.language == 0 ? 'selected' : ''}>English</option>
|
|
87
|
+
<option value="1" ${account.language == 1 ? 'selected' : ''}>Български</option>
|
|
88
|
+
<option value="2" ${account.language == 2 ? 'selected' : ''}>Česky</option>
|
|
89
|
+
<option value="3" ${account.language == 3 ? 'selected' : ''}>Dansk</option>
|
|
90
|
+
<option value="4" ${account.language == 4 ? 'selected' : ''}>Deutsch</option>
|
|
91
|
+
<option value="5" ${account.language == 5 ? 'selected' : ''}>Eesti</option>
|
|
92
|
+
<option value="6" ${account.language == 6 ? 'selected' : ''}>Español</option>
|
|
93
|
+
<option value="7" ${account.language == 7 ? 'selected' : ''}>Français</option>
|
|
94
|
+
<option value="8" ${account.language == 8 ? 'selected' : ''}>Italiano</option>
|
|
95
|
+
<option value="9" ${account.language == 9 ? 'selected' : ''}>Latviešu</option>
|
|
96
|
+
<option value="10" ${account.language == 10 ? 'selected' : ''}>Lietuvių</option>
|
|
97
|
+
<option value="11" ${account.language == 11 ? 'selected' : ''}>Magyar</option>
|
|
98
|
+
<option value="12" ${account.language == 12 ? 'selected' : ''}>Nederlands</option>
|
|
99
|
+
<option value="13" ${account.language == 13 ? 'selected' : ''}>Norsk</option>
|
|
100
|
+
<option value="14" ${account.language == 14 ? 'selected' : ''}>Polski</option>
|
|
101
|
+
<option value="15" ${account.language == 15 ? 'selected' : ''}>Português</option>
|
|
102
|
+
<option value="16" ${account.language == 16 ? 'selected' : ''}>Русский</option>
|
|
103
|
+
<option value="17" ${account.language == 17 ? 'selected' : ''}>Suomi</option>
|
|
104
|
+
<option value="18" ${account.language == 18 ? 'selected' : ''}>Svenska</option>
|
|
105
|
+
<option value="19" ${account.language == 19 ? 'selected' : ''}>Türkçe</option>
|
|
106
|
+
</select>
|
|
107
|
+
</div>
|
|
108
|
+
<button type="button" class="btn btn-secondary" id="logIn-${i}">Connect</button>
|
|
109
|
+
<button type="button" class="btn btn-danger ms-2" id="remove-${i}">❌ Remove Account</button>
|
|
110
|
+
</form>`;
|
|
111
|
+
content.appendChild(pane);
|
|
112
|
+
|
|
113
|
+
// Handle input updates
|
|
114
|
+
pane.querySelector("form").addEventListener("input", async () => {
|
|
115
|
+
account.name = document.getElementById(`name-${i}`).value;
|
|
116
|
+
account.user = document.getElementById(`user-${i}`).value;
|
|
117
|
+
account.passwd = document.getElementById(`passwd-${i}`).value;
|
|
118
|
+
account.language = document.getElementById(`language-${i}`).value;
|
|
130
119
|
|
|
131
120
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
132
|
-
|
|
121
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
122
|
+
document.querySelector(`#tab-${i}`).innerText = account.name || `Account ${i + 1}`;
|
|
133
123
|
});
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
125
|
+
// Remove account
|
|
126
|
+
document.getElementById(`remove-${i}`).addEventListener("click", async () => {
|
|
127
|
+
accounts.splice(i, 1);
|
|
128
|
+
document.getElementById(`tab-li-${i}`).remove();
|
|
129
|
+
document.getElementById(`pane-${i}`).remove();
|
|
130
|
+
|
|
131
|
+
// Rebuild IDs
|
|
132
|
+
const panes = document.querySelectorAll(".tab-pane");
|
|
133
|
+
panes.forEach((p, index) => {
|
|
134
|
+
p.id = `pane-${index}`;
|
|
135
|
+
p.querySelector("form").id = `configForm-${index}`;
|
|
136
|
+
p.querySelector(`[id^="name-"]`).id = `name-${index}`;
|
|
137
|
+
p.querySelector(`[id^="user-"]`).id = `user-${index}`;
|
|
138
|
+
p.querySelector(`[id^="passwd-"]`).id = `passwd-${index}`;
|
|
139
|
+
p.querySelector(`[id^="language-"]`).id = `language-${index}`;
|
|
140
|
+
p.querySelector(`[id^="logIn-"]`).id = `logIn-${index}`;
|
|
141
|
+
p.querySelector(`[id^="remove-"]`).id = `remove-${index}`;
|
|
142
|
+
});
|
|
143
|
+
const tabButtons = document.querySelectorAll(".nav-link");
|
|
144
|
+
tabButtons.forEach((btn, idx) => {
|
|
145
|
+
btn.id = `tab-${idx}`;
|
|
146
|
+
btn.dataset.bsTarget = `#pane-${idx}`;
|
|
147
|
+
});
|
|
154
148
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
await homebridge.updatePluginConfig(pluginConfig);
|
|
158
|
-
await homebridge.savePluginConfig(pluginConfig);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
document.getElementById('configButton').addEventListener('click', async () => {
|
|
162
|
-
this.configButtonState ? homebridge.hideSchemaForm() : homebridge.showSchemaForm();
|
|
163
|
-
document
|
|
164
|
-
.getElementById('configButton')
|
|
165
|
-
.setAttribute("class", this.configButtonState ? "btn btn-secondary" : "btn btn-primary");
|
|
166
|
-
this.configButtonState = !this.configButtonState;
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
document.getElementById('logIn').addEventListener('click', async () => {
|
|
170
|
-
homebridge.showSpinner();
|
|
171
|
-
|
|
172
|
-
document.getElementById(`logIn`).setAttribute("class", "btn btn-primary");
|
|
173
|
-
document.getElementById('info').innerHTML = 'Connecting...';
|
|
174
|
-
document.getElementById('info').style.color = 'yellow';
|
|
149
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
150
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
175
151
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const { name: accountName, user, passwd, language } = account;
|
|
152
|
+
if (accounts.length > 0) new bootstrap.Tab(document.getElementById("tab-0")).show();
|
|
153
|
+
});
|
|
179
154
|
|
|
180
|
-
|
|
181
|
-
|
|
155
|
+
document.getElementById('configButton').addEventListener('click', async () => {
|
|
156
|
+
if (this.configButtonState) {
|
|
157
|
+
homebridge.hideSchemaForm();
|
|
158
|
+
document.getElementById('configButton').className = "btn btn-secondary";
|
|
159
|
+
} else {
|
|
160
|
+
homebridge.showSchemaForm();
|
|
161
|
+
document.getElementById('configButton').className = "btn btn-primary";
|
|
162
|
+
}
|
|
163
|
+
this.configButtonState = !this.configButtonState;
|
|
164
|
+
});
|
|
182
165
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
166
|
+
// Initialize state
|
|
167
|
+
this.configButtonState = false;
|
|
168
|
+
|
|
169
|
+
// Login button
|
|
170
|
+
document.getElementById(`logIn-${i}`).addEventListener("click", async () => {
|
|
171
|
+
homebridge.showSpinner();
|
|
172
|
+
try {
|
|
173
|
+
const account = accounts[i];
|
|
174
|
+
const devicesInMelCloud = await homebridge.request('/connect', {
|
|
175
|
+
accountName: account.name,
|
|
176
|
+
user: account.user,
|
|
177
|
+
passwd: account.passwd,
|
|
178
|
+
language: account.language
|
|
179
|
+
});
|
|
186
180
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
181
|
+
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
182
|
+
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
183
|
+
for (const d of devicesInMelCloud) {
|
|
184
|
+
if (d.Type === 0) devicesByType.ata.push(d);
|
|
185
|
+
if (d.Type === 1) devicesByType.atw.push(d);
|
|
186
|
+
if (d.Type === 3) devicesByType.erv.push(d);
|
|
192
187
|
}
|
|
193
|
-
}
|
|
194
188
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
189
|
+
// Remove stale function
|
|
190
|
+
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
191
|
+
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
192
|
+
const removed = [];
|
|
193
|
+
for (let j = configDevices.length - 1; j >= 0; j--) {
|
|
194
|
+
const dev = configDevices[j];
|
|
195
|
+
if (dev.id !== 0 && !melcloudIds.includes(dev.id)) {
|
|
196
|
+
removed.push(dev);
|
|
197
|
+
configDevices.splice(j, 1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return removed;
|
|
201
|
+
}
|
|
198
202
|
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
203
|
+
const ataDevices = account.ataDevices ?? (account.ataDevices = []);
|
|
204
|
+
const atwDevices = account.atwDevices ?? (account.atwDevices = []);
|
|
205
|
+
const ervDevices = account.ervDevices ?? (account.ervDevices = []);
|
|
206
|
+
|
|
207
|
+
const removedAta = removeStaleDevices(ataDevices, devicesByType.ata);
|
|
208
|
+
const removedAtw = removeStaleDevices(atwDevices, devicesByType.atw);
|
|
209
|
+
const removedErv = removeStaleDevices(ervDevices, devicesByType.erv);
|
|
210
|
+
|
|
211
|
+
// Add new devices & presets
|
|
212
|
+
function handleDevices(configArray, devicesArray, typeName, newDevicesArr, newPresetsArr) {
|
|
213
|
+
devicesArray.forEach(d => {
|
|
214
|
+
const existing = configArray.find(x => x.id === d.DeviceID);
|
|
215
|
+
if (!existing) {
|
|
216
|
+
const devObj = {
|
|
217
|
+
id: d.DeviceID,
|
|
218
|
+
type: d.Type,
|
|
219
|
+
typeString: typeName,
|
|
220
|
+
name: d.DeviceName,
|
|
221
|
+
displayMode: 1,
|
|
222
|
+
presets: [],
|
|
223
|
+
buttonsSensors: []
|
|
224
|
+
};
|
|
225
|
+
configArray.push(devObj);
|
|
226
|
+
newDevicesArr.push(devObj);
|
|
227
|
+
}
|
|
228
|
+
(d.Presets || []).forEach(p => {
|
|
229
|
+
const preset = { ...p, id: p.ID, name: p.NumberDescription, displayType: 0, namePrefix: false };
|
|
230
|
+
if (!existing?.presets.some(pr => pr.id === p.ID)) {
|
|
231
|
+
existing?.presets.push(preset);
|
|
232
|
+
newPresetsArr.push(preset);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
204
236
|
}
|
|
205
|
-
};
|
|
206
237
|
|
|
207
|
-
|
|
208
|
-
|
|
238
|
+
handleDevices(ataDevices, devicesByType.ata, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
239
|
+
handleDevices(atwDevices, devicesByType.atw, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
240
|
+
handleDevices(ervDevices, devicesByType.erv, "ERV", newDevices.erv, newDevices.ervPresets);
|
|
209
241
|
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
configArray.splice(i, 1);
|
|
215
|
-
}
|
|
242
|
+
// Info display
|
|
243
|
+
function updateInfo(id, text, color) {
|
|
244
|
+
const el = document.getElementById(id);
|
|
245
|
+
if (el) { el.innerHTML = text; el.style.color = color; }
|
|
216
246
|
}
|
|
217
247
|
|
|
218
|
-
devices.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const deviceObj = {
|
|
222
|
-
id: deviceId,
|
|
223
|
-
type: deviceType,
|
|
224
|
-
typeString,
|
|
225
|
-
name: deviceName,
|
|
226
|
-
...template,
|
|
227
|
-
presets: [],
|
|
228
|
-
buttonsSensors: []
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
if (!configArray.some(d => d.id === deviceId)) {
|
|
232
|
-
configArray.push(deviceObj);
|
|
233
|
-
newArray.push(deviceObj);
|
|
234
|
-
}
|
|
248
|
+
const textInfo = `Found devices: ATA(${newDevices.ata.length}), ATW(${newDevices.atw.length}), ERV(${newDevices.erv.length})`;
|
|
249
|
+
const textPresets = `Found presets: ATA(${newDevices.ataPresets.length}), ATW(${newDevices.atwPresets.length}), ERV(${newDevices.ervPresets.length})`;
|
|
250
|
+
const textRemoved = `Removed devices: ATA(${removedAta.length}), ATW(${removedAtw.length}), ERV(${removedErv.length})`;
|
|
235
251
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
updateInfo('info', textInfo, newDevices.ata.length + newDevices.atw.length + newDevices.erv.length > 0 ? 'green' : 'white');
|
|
253
|
+
updateInfo('info1', textPresets, newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length > 0 ? 'green' : 'white');
|
|
254
|
+
updateInfo('info2', textRemoved, removedAta.length + removedAtw.length + removedErv.length > 0 ? 'orange' : 'white');
|
|
239
255
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
});
|
|
256
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
257
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
document.getElementById('info').innerHTML = `Error: ${err.message || err}`;
|
|
260
|
+
} finally {
|
|
261
|
+
homebridge.hideSpinner();
|
|
247
262
|
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
248
265
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
heatDryFanMode: 1,
|
|
252
|
-
coolDryFanMode: 1,
|
|
253
|
-
autoDryFanMode: 1,
|
|
254
|
-
temperatureSensor: false,
|
|
255
|
-
temperatureSensorOutdoor: false,
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const atwTemplate = {
|
|
259
|
-
displayMode: 1,
|
|
260
|
-
hideZone: 0,
|
|
261
|
-
temperatureSensor: false,
|
|
262
|
-
temperatureSensorFlow: false,
|
|
263
|
-
temperatureSensorReturn: false,
|
|
264
|
-
temperatureSensorFlowZone1: false,
|
|
265
|
-
temperatureSensorReturnZone1: false,
|
|
266
|
-
temperatureSensorFlowWaterTank: false,
|
|
267
|
-
temperatureSensorReturnWaterTank: false,
|
|
268
|
-
temperatureSensorFlowZone2: false,
|
|
269
|
-
temperatureSensorReturnZone2: false,
|
|
270
|
-
temperatureSensorOutdoor: false,
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const ervTemplate = {
|
|
274
|
-
displayMode: 1,
|
|
275
|
-
temperatureSensor: false,
|
|
276
|
-
temperatureSensorOutdoor: false,
|
|
277
|
-
temperatureSensorSupply: false,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
handleDevices(devicesByTypeInMelCloud.ata, ataDevicesInConfig, newDevices.ata, newDevices.ataPresets, removedDevices.ata, "Air Conditioner", ataTemplate);
|
|
281
|
-
handleDevices(devicesByTypeInMelCloud.atw, atwDevicesInConfig, newDevices.atw, newDevices.atwPresets, removedDevices.atw, "Heat Pump", atwTemplate);
|
|
282
|
-
handleDevices(devicesByTypeInMelCloud.erv, ervDevicesInConfig, newDevices.erv, newDevices.ervPresets, removedDevices.erv, "Energy Recovery Ventilation", ervTemplate);
|
|
283
|
-
|
|
284
|
-
const textAta = `ATA: ${newDevices.ata.length}`;
|
|
285
|
-
const textAtw = `ATW: ${newDevices.atw.length}`;
|
|
286
|
-
const textErv = `ERV: ${newDevices.erv.length}`;
|
|
287
|
-
const textRemovedAta = `ATA: ${removedDevices.ata.length}`;
|
|
288
|
-
const textRemovedAtw = `ATW: ${removedDevices.atw.length}`;
|
|
289
|
-
const textRemovedErv = `ERV: ${removedDevices.erv.length}`;
|
|
290
|
-
const textAtaPresets = `ATA Presets: ${newDevices.ataPresets.length}`;
|
|
291
|
-
const textAtwPresets = `ATW Presets: ${newDevices.atwPresets.length}`;
|
|
292
|
-
const textErvPresets = `ERV Presets: ${newDevices.ervPresets.length}`;
|
|
293
|
-
|
|
294
|
-
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
295
|
-
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
296
|
-
const removedDevicesCount = removedDevices.ata.length + removedDevices.atw.length + removedDevices.erv.length;
|
|
297
|
-
|
|
298
|
-
if (newDevicesCount === 0 && newPresetsCount === 0 && removedDevicesCount === 0) {
|
|
299
|
-
updateInfo('info', 'No changes (already synced).', 'white');
|
|
300
|
-
} else {
|
|
301
|
-
if (newDevicesCount > 0) {
|
|
302
|
-
updateInfo('info', `Found, ${textAta}, ${textAtw}, ${textErv}.`, 'green');
|
|
303
|
-
updateInfo('info2', 'Now You can configure it.', 'green');
|
|
304
|
-
}
|
|
305
|
-
if (newPresetsCount > 0) {
|
|
306
|
-
updateInfo('info1', `${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
307
|
-
if (newDevicesCount === 0) {
|
|
308
|
-
updateInfo('info', `Found, ${textAtaPresets}, ${textAtwPresets}, ${textErvPresets}.`, 'green');
|
|
309
|
-
updateInfo('info1', 'Now You can configure it.', 'green');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (removedDevicesCount > 0) {
|
|
313
|
-
updateInfo('info', `Removed, ${textRemovedAta}, ${textRemovedAtw}, ${textRemovedErv}.`, 'orange');
|
|
314
|
-
}
|
|
315
|
-
}
|
|
266
|
+
// Render accounts
|
|
267
|
+
accounts.forEach((acc, i) => createAccountForm(acc, i));
|
|
316
268
|
|
|
317
|
-
|
|
269
|
+
// Add "Add Account" tab
|
|
270
|
+
const addLi = document.createElement("li");
|
|
271
|
+
addLi.classList.add("nav-item");
|
|
272
|
+
addLi.innerHTML = `<button class="nav-link" id="addTab" type="button" role="tab">➕ Add Account</button>`;
|
|
273
|
+
tabs.appendChild(addLi);
|
|
318
274
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
275
|
+
document.getElementById("addTab").addEventListener("click", async () => {
|
|
276
|
+
const newAccount = { name: "", user: "", passwd: "", language: 0 };
|
|
277
|
+
accounts.push(newAccount);
|
|
278
|
+
const i = accounts.length - 1;
|
|
279
|
+
createAccountForm(newAccount, i);
|
|
280
|
+
await homebridge.updatePluginConfig(pluginConfig);
|
|
281
|
+
await homebridge.savePluginConfig(pluginConfig);
|
|
282
|
+
new bootstrap.Tab(document.getElementById(`tab-${i}`)).show();
|
|
328
283
|
});
|
|
284
|
+
|
|
329
285
|
})();
|
|
330
|
-
</script>
|
|
286
|
+
</script>
|
|
287
|
+
</body>
|
|
288
|
+
|
|
289
|
+
</html>
|
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.10",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|