homebridge-melcloud-control 4.0.0-beta.52 → 4.0.0-beta.521
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 +9 -4
- package/README.md +10 -12
- package/config.schema.json +294 -336
- package/homebridge-ui/public/index.html +110 -64
- package/homebridge-ui/server.js +7 -9
- package/index.js +45 -28
- package/package.json +5 -4
- package/src/constants.js +15 -22
- package/src/deviceata.js +252 -243
- package/src/deviceatw.js +50 -40
- package/src/deviceerv.js +43 -35
- package/src/functions.js +155 -5
- package/src/melcloud.js +345 -180
- package/src/melcloudata.js +146 -306
- package/src/melcloudatw.js +145 -340
- package/src/melclouderv.js +144 -271
- package/src/restful.js +1 -1
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
</div>
|
|
78
78
|
|
|
79
79
|
<div class="mb-2">
|
|
80
|
-
<label for="
|
|
81
|
-
<select id="
|
|
80
|
+
<label for="accountType" class="form-label">Account Type</label>
|
|
81
|
+
<select id="accountType" name="accountType" class="form-control">
|
|
82
82
|
<option value="disabled">None/Disabled</option>
|
|
83
83
|
<option value="melcloud">MELCloud</option>
|
|
84
84
|
<option value="melcloudhome">MELCloud Home</option>
|
|
@@ -86,17 +86,18 @@
|
|
|
86
86
|
</div>
|
|
87
87
|
|
|
88
88
|
<div class="text-center">
|
|
89
|
-
<button id="logIn" type="button" class="btn btn-secondary">
|
|
89
|
+
<button id="logIn" type="button" class="btn btn-secondary">Connect to MELCloud</button>
|
|
90
90
|
<button id="configButton" type="button" class="btn btn-secondary"><i class="fas fa-gear"></i></button>
|
|
91
91
|
</div>
|
|
92
92
|
</form>
|
|
93
|
-
<div id="accountButton" class="
|
|
93
|
+
<div id="accountButton" class="d-flex flex-wrap justify-content-center gap-1 mt-3"></div>
|
|
94
94
|
</div>
|
|
95
95
|
</div>
|
|
96
96
|
|
|
97
97
|
<script>
|
|
98
98
|
(async () => {
|
|
99
99
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
100
|
+
|
|
100
101
|
if (!pluginConfig.length) {
|
|
101
102
|
pluginConfig.push({});
|
|
102
103
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -104,54 +105,79 @@
|
|
|
104
105
|
return;
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
const accountsCount =
|
|
110
|
-
|
|
108
|
+
this.accountIndex = 0;
|
|
109
|
+
const accounts = pluginConfig[0].accounts || [];
|
|
110
|
+
const accountsCount = accounts.length;
|
|
111
|
+
|
|
112
|
+
const container = document.getElementById("accountButton");
|
|
113
|
+
container.style.display = 'flex';
|
|
114
|
+
container.style.flexWrap = 'wrap';
|
|
115
|
+
container.style.justifyContent = 'center';
|
|
116
|
+
container.style.gap = '0.25rem';
|
|
117
|
+
container.style.alignItems = 'center';
|
|
118
|
+
|
|
119
|
+
const formElements = {
|
|
120
|
+
name: document.getElementById('name'),
|
|
121
|
+
user: document.getElementById('user'),
|
|
122
|
+
passwd: document.getElementById('passwd'),
|
|
123
|
+
language: document.getElementById('language'),
|
|
124
|
+
accountType: document.getElementById('accountType'),
|
|
125
|
+
logIn: document.getElementById('logIn'),
|
|
126
|
+
accountName: document.getElementById('accountName')
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Tworzenie przycisków
|
|
130
|
+
accounts.forEach((account, i) => {
|
|
111
131
|
const button = document.createElement("button");
|
|
112
132
|
button.type = "button";
|
|
113
133
|
button.id = `button${i}`;
|
|
114
134
|
button.className = "btn btn-primary";
|
|
115
135
|
button.style.textTransform = 'none';
|
|
116
|
-
button.innerText =
|
|
117
|
-
|
|
136
|
+
button.innerText = account.name || `Account ${i + 1}`;
|
|
137
|
+
container.appendChild(button);
|
|
118
138
|
|
|
119
139
|
button.addEventListener('click', async () => {
|
|
120
|
-
|
|
140
|
+
this.accountIndex = i;
|
|
141
|
+
|
|
142
|
+
// Zmieniamy klasę wszystkich przycisków
|
|
143
|
+
accounts.forEach((_, j) => {
|
|
121
144
|
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-primary' : 'btn btn-secondary');
|
|
122
|
-
}
|
|
145
|
+
});
|
|
123
146
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.deviceIndex = i;
|
|
147
|
+
// Ustawiamy formularz
|
|
148
|
+
formElements.accountName.innerText = account.name || '';
|
|
149
|
+
formElements.name.value = account.name || '';
|
|
150
|
+
formElements.user.value = account.user || '';
|
|
151
|
+
formElements.passwd.value = account.passwd || '';
|
|
152
|
+
formElements.language.value = account.language || '0';
|
|
153
|
+
formElements.accountType.value = account.type || 'disabled';
|
|
154
|
+
formElements.logIn.disabled = !(account.name && account.user && account.passwd && account.language && account.type);
|
|
133
155
|
});
|
|
156
|
+
});
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
document.getElementById('melCloudAccount').style.display = 'block';
|
|
158
|
+
// Klikamy pierwszy przycisk po zakończeniu pętli
|
|
159
|
+
if (accountsCount > 0) document.getElementById('button0').click();
|
|
139
160
|
|
|
161
|
+
// Jeden listener input dla całego formularza
|
|
140
162
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
acc.user = document.querySelector('#user').value;
|
|
144
|
-
acc.passwd = document.querySelector('#passwd').value;
|
|
145
|
-
acc.language = document.querySelector('#language').value;
|
|
146
|
-
acc.displayType = document.querySelector('#displayType').value;
|
|
163
|
+
const account = accounts[this.accountIndex];
|
|
164
|
+
if (!account) return;
|
|
147
165
|
|
|
148
|
-
|
|
166
|
+
account.name = formElements.name.value;
|
|
167
|
+
account.user = formElements.user.value;
|
|
168
|
+
account.passwd = formElements.passwd.value;
|
|
169
|
+
account.language = formElements.language.value;
|
|
170
|
+
account.type = formElements.accountType.value;
|
|
171
|
+
|
|
172
|
+
formElements.logIn.disabled = !(account.name && account.user && account.passwd && account.language && account.type);
|
|
149
173
|
|
|
150
174
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
151
175
|
await homebridge.savePluginConfig(pluginConfig);
|
|
152
176
|
});
|
|
153
177
|
|
|
154
|
-
|
|
178
|
+
document.getElementById('melCloudAccount').style.display = 'block';
|
|
179
|
+
|
|
180
|
+
// Config Button Toggle
|
|
155
181
|
const configButton = document.getElementById('configButton');
|
|
156
182
|
let configButtonState = false;
|
|
157
183
|
configButton.addEventListener('click', () => {
|
|
@@ -173,13 +199,14 @@
|
|
|
173
199
|
}
|
|
174
200
|
});
|
|
175
201
|
|
|
176
|
-
//
|
|
202
|
+
// Device Handling & Login Logic
|
|
177
203
|
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
178
204
|
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
179
205
|
const removedDevices = [];
|
|
206
|
+
|
|
180
207
|
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
181
208
|
const device = configDevices[i];
|
|
182
|
-
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
209
|
+
if (device.id !== "0" && !melcloudIds.includes(device.id)) {
|
|
183
210
|
removedDevices.push(device);
|
|
184
211
|
configDevices.splice(i, 1);
|
|
185
212
|
}
|
|
@@ -196,61 +223,77 @@
|
|
|
196
223
|
}
|
|
197
224
|
|
|
198
225
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
199
|
-
homebridge.showSpinner();
|
|
200
226
|
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
227
|
+
|
|
201
228
|
updateInfo('info', 'Connecting...', 'yellow');
|
|
229
|
+
homebridge.showSpinner();
|
|
202
230
|
|
|
203
231
|
try {
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
232
|
+
const account = pluginConfig[0].accounts[this.accountIndex];
|
|
233
|
+
const response = await homebridge.request('/connect', account);
|
|
234
|
+
if (!response.State) {
|
|
235
|
+
homebridge.hideSpinner();
|
|
236
|
+
updateInfo('info', response.Info);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
207
239
|
|
|
208
240
|
// Initialize devices arrays
|
|
209
241
|
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
210
242
|
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
211
243
|
|
|
212
|
-
|
|
244
|
+
response.Devices.forEach(d => {
|
|
213
245
|
if (d.Type === 0) devicesByType.ata.push(d);
|
|
214
246
|
if (d.Type === 1) devicesByType.atw.push(d);
|
|
215
247
|
if (d.Type === 3) devicesByType.erv.push(d);
|
|
216
248
|
});
|
|
217
249
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
250
|
+
account.ataDevices ??= [];
|
|
251
|
+
account.atwDevices ??= [];
|
|
252
|
+
account.ervDevices ??= [];
|
|
221
253
|
|
|
222
|
-
const removedAta = removeStaleDevices(
|
|
223
|
-
const removedAtw = removeStaleDevices(
|
|
224
|
-
const removedErv = removeStaleDevices(
|
|
254
|
+
const removedAta = removeStaleDevices(account.ataDevices, devicesByType.ata);
|
|
255
|
+
const removedAtw = removeStaleDevices(account.atwDevices, devicesByType.atw);
|
|
256
|
+
const removedErv = removeStaleDevices(account.ervDevices, devicesByType.erv);
|
|
225
257
|
|
|
226
|
-
// Function to handle device & presets
|
|
227
258
|
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
228
259
|
devicesInCloud.forEach(device => {
|
|
229
260
|
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
230
|
-
const devObj = {
|
|
261
|
+
const devObj = structuredClone({
|
|
262
|
+
id,
|
|
263
|
+
type,
|
|
264
|
+
typeString,
|
|
265
|
+
name,
|
|
266
|
+
displayType: 0,
|
|
267
|
+
presets: [],
|
|
268
|
+
buttonsSensors: []
|
|
269
|
+
});
|
|
270
|
+
|
|
231
271
|
if (!devicesInConfig.some(d => d.id === id)) {
|
|
232
272
|
devicesInConfig.push(devObj);
|
|
233
|
-
newArr.push(devObj);
|
|
273
|
+
newArr.push(structuredClone(devObj));
|
|
234
274
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
p
|
|
238
|
-
|
|
239
|
-
|
|
275
|
+
|
|
276
|
+
presets.forEach(preset => {
|
|
277
|
+
const p = structuredClone({
|
|
278
|
+
id: preset.ID,
|
|
279
|
+
name: preset.NumberDescription,
|
|
280
|
+
displayType: 0,
|
|
281
|
+
namePrefix: false
|
|
282
|
+
});
|
|
283
|
+
|
|
240
284
|
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
241
|
-
if (devConfig && !devConfig.presets.some(x => x.id === p.
|
|
285
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.id)) {
|
|
242
286
|
devConfig.presets.push(p);
|
|
243
|
-
newPresets.push(p);
|
|
287
|
+
newPresets.push(structuredClone(p));
|
|
244
288
|
}
|
|
245
289
|
});
|
|
246
290
|
});
|
|
247
291
|
};
|
|
248
292
|
|
|
249
|
-
handleDevices(devicesByType.ata,
|
|
250
|
-
handleDevices(devicesByType.atw,
|
|
251
|
-
handleDevices(devicesByType.erv,
|
|
293
|
+
handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
294
|
+
handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
295
|
+
handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
|
|
252
296
|
|
|
253
|
-
// Display summary
|
|
254
297
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
255
298
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
256
299
|
const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
@@ -258,9 +301,12 @@
|
|
|
258
301
|
if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
|
|
259
302
|
updateInfo('info', 'No changes detected.', 'white');
|
|
260
303
|
} else {
|
|
261
|
-
if (newDevicesCount)
|
|
262
|
-
|
|
263
|
-
if (
|
|
304
|
+
if (newDevicesCount)
|
|
305
|
+
updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
306
|
+
if (newPresetsCount)
|
|
307
|
+
updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
308
|
+
if (removedDevicesCount)
|
|
309
|
+
updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
264
310
|
}
|
|
265
311
|
|
|
266
312
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -269,7 +315,7 @@
|
|
|
269
315
|
|
|
270
316
|
} catch (error) {
|
|
271
317
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
272
|
-
updateInfo('info1', `Error: ${error}`, 'red');
|
|
318
|
+
updateInfo('info1', `Error: ${JSON.stringify(error)}`, 'red');
|
|
273
319
|
document.getElementById('logIn').className = "btn btn-secondary";
|
|
274
320
|
} finally {
|
|
275
321
|
homebridge.hideSpinner();
|
package/homebridge-ui/server.js
CHANGED
|
@@ -12,21 +12,19 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
12
12
|
this.ready();
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
async start(
|
|
16
|
-
const accountName =
|
|
17
|
-
const user = payload.user;
|
|
18
|
-
const passwd = payload.passwd;
|
|
19
|
-
const language = payload.language;
|
|
20
|
-
const displayType = payload.displayType;
|
|
15
|
+
async start(account) {
|
|
16
|
+
const accountName = account.name;
|
|
21
17
|
const accountFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Account`;
|
|
22
18
|
const buildingsFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Buildings`;
|
|
23
19
|
const devicesFile = `${this.homebridgeStoragePath}/melcloud/${accountName}_Devices`;
|
|
24
|
-
const melCloud = new MelCloud(
|
|
20
|
+
const melCloud = new MelCloud(account, accountFile, buildingsFile, devicesFile);
|
|
25
21
|
|
|
26
22
|
try {
|
|
27
23
|
const accountInfo = await melCloud.connect();
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
if (!accountInfo.State) return accountInfo;
|
|
25
|
+
|
|
26
|
+
const devicesList = await melCloud.checkDevicesList();
|
|
27
|
+
return devicesList;
|
|
30
28
|
} catch (error) {
|
|
31
29
|
throw new Error(`MELCloud error: ${error.message ?? error}.`);
|
|
32
30
|
};
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
|
-
import { mkdirSync } from 'fs';
|
|
2
|
+
import { mkdirSync, existsSync, writeFileSync } from 'fs';
|
|
3
3
|
import MelCloud from './src/melcloud.js';
|
|
4
4
|
import DeviceAta from './src/deviceata.js';
|
|
5
5
|
import DeviceAtw from './src/deviceatw.js';
|
|
@@ -30,8 +30,8 @@ class MelCloudPlatform {
|
|
|
30
30
|
api.on('didFinishLaunching', async () => {
|
|
31
31
|
//loop through accounts
|
|
32
32
|
for (const account of config.accounts) {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
33
|
+
const accountType = account.type || 'disabled';
|
|
34
|
+
if (accountType === 'disabled') continue;
|
|
35
35
|
|
|
36
36
|
const accountName = account.name;
|
|
37
37
|
const user = account.user;
|
|
@@ -59,7 +59,6 @@ class MelCloudPlatform {
|
|
|
59
59
|
debug: account.log?.debug
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
|
|
63
62
|
if (logLevel.debug) {
|
|
64
63
|
log.info(`${accountName}, debug: did finish launching.`);
|
|
65
64
|
const safeConfig = {
|
|
@@ -80,7 +79,6 @@ class MelCloudPlatform {
|
|
|
80
79
|
const buildingsFile = `${prefDir}/${accountName}_Buildings`;
|
|
81
80
|
const devicesFile = `${prefDir}/${accountName}_Devices`;
|
|
82
81
|
|
|
83
|
-
|
|
84
82
|
//set account refresh interval
|
|
85
83
|
const refreshInterval = (account.refreshInterval ?? 120) * 1000
|
|
86
84
|
|
|
@@ -90,7 +88,7 @@ class MelCloudPlatform {
|
|
|
90
88
|
.on('start', async () => {
|
|
91
89
|
try {
|
|
92
90
|
//melcloud account
|
|
93
|
-
const melCloud = new MelCloud(
|
|
91
|
+
const melCloud = new MelCloud(account, accountFile, buildingsFile, devicesFile, true)
|
|
94
92
|
.on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
|
|
95
93
|
.on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
|
|
96
94
|
.on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
|
|
@@ -106,56 +104,74 @@ class MelCloudPlatform {
|
|
|
106
104
|
return;
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!contextKey) {
|
|
107
|
+
if (!accountInfo.State) {
|
|
108
|
+
if (logLevel.warn) log.warn(`${accountName}, ${accountInfo.Info}`);
|
|
113
109
|
return;
|
|
114
110
|
}
|
|
111
|
+
if (logLevel.success) log.success(accountInfo.Info);
|
|
112
|
+
const useFahrenheit = accountInfo.UseFahrenheit;
|
|
115
113
|
|
|
116
114
|
//check devices list
|
|
117
|
-
let
|
|
115
|
+
let devicesList;
|
|
118
116
|
try {
|
|
119
|
-
|
|
117
|
+
devicesList = await melCloud.checkDevicesList();
|
|
120
118
|
} catch (error) {
|
|
121
119
|
if (logLevel.error) log.error(`${accountName}, Check devices list error: ${error.message ?? error}`);
|
|
122
120
|
return;
|
|
123
121
|
}
|
|
124
|
-
if (!
|
|
122
|
+
if (!devicesList.State) {
|
|
123
|
+
if (logLevel.warn) log.warn(`${accountName}, ${devicesList.Info}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
125
126
|
|
|
126
127
|
//configured devices
|
|
127
|
-
const ataDevices = account.ataDevices
|
|
128
|
-
const atwDevices = account.atwDevices
|
|
129
|
-
const ervDevices = account.ervDevices
|
|
128
|
+
const ataDevices = (account.ataDevices || []).filter(device => (device.id ?? '0') !== '0');
|
|
129
|
+
const atwDevices = (account.atwDevices || []).filter(device => (device.id ?? '0') !== '0');
|
|
130
|
+
const ervDevices = (account.ervDevices || []).filter(device => (device.id ?? '0') !== '0');
|
|
130
131
|
const devices = [...ataDevices, ...atwDevices, ...ervDevices];
|
|
131
132
|
if (logLevel.debug) log.info(`Found configured devices ATA: ${ataDevices.length}, ATW: ${atwDevices.length}, ERV: ${ervDevices.length}.`);
|
|
132
133
|
|
|
133
134
|
for (const device of devices) {
|
|
134
135
|
//chack device from config exist on melcloud
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
if (!deviceExistInMelCloud || !displayMode) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
136
|
+
const displayType = device.displayType > 0;
|
|
137
|
+
const deviceExistInMelCloud = devicesList.Devices.some(dev => dev.DeviceID === device.id);
|
|
138
|
+
if (!deviceExistInMelCloud || !displayType) continue;
|
|
141
139
|
|
|
142
140
|
const deviceName = device.name;
|
|
143
141
|
const deviceType = device.type;
|
|
144
142
|
const deviceTypeText = device.typeString;
|
|
145
143
|
const deviceRefreshInterval = (device.refreshInterval ?? 5) * 1000;
|
|
144
|
+
const defaultTempsFile = `${prefDir}/${accountName}_${device.id}_Temps`;
|
|
145
|
+
|
|
146
|
+
if (accountType === 'melcloudhome') {
|
|
147
|
+
try {
|
|
148
|
+
const temps = {
|
|
149
|
+
defaultCoolingSetTemperature: 24,
|
|
150
|
+
defaultHeatingSetTemperature: 20
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (!existsSync(defaultTempsFile)) {
|
|
154
|
+
writeFileSync(defaultTempsFile, JSON.stringify(temps, null, 2));
|
|
155
|
+
if (logLevel.debug) log.debug(`Default temperature file created: ${defaultTempsFile}`);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (logLevel.error) log.error(`Device: ${host} ${deviceName}, File init error: ${error.message}`);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
146
162
|
|
|
147
163
|
let configuredDevice;
|
|
148
164
|
switch (deviceType) {
|
|
149
165
|
case 0: //ATA
|
|
150
|
-
configuredDevice = new DeviceAta(api, account, device,
|
|
166
|
+
configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
151
167
|
break;
|
|
152
168
|
case 1: //ATW
|
|
153
|
-
configuredDevice = new DeviceAtw(api, account, device,
|
|
169
|
+
configuredDevice = new DeviceAtw(api, account, device, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
154
170
|
break;
|
|
155
171
|
case 2:
|
|
156
172
|
break;
|
|
157
173
|
case 3: //ERV
|
|
158
|
-
configuredDevice = new DeviceErv(api, account, device,
|
|
174
|
+
configuredDevice = new DeviceErv(api, account, device, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
159
175
|
break;
|
|
160
176
|
default:
|
|
161
177
|
if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeText}, ${deviceName}, unknown device: ${deviceType}.`);
|
|
@@ -164,7 +180,7 @@ class MelCloudPlatform {
|
|
|
164
180
|
|
|
165
181
|
configuredDevice.on('melCloud', async (key, value) => {
|
|
166
182
|
try {
|
|
167
|
-
accountInfo[key] = value;
|
|
183
|
+
accountInfo.LoginData[key] = value;
|
|
168
184
|
await melCloud.send(accountInfo);
|
|
169
185
|
} catch (error) {
|
|
170
186
|
if (logLevel.error) log.error(`${accountName}, ${deviceTypeText}, ${deviceName}, ${error.message ?? error}.`);
|
|
@@ -182,8 +198,9 @@ class MelCloudPlatform {
|
|
|
182
198
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
183
199
|
if (logLevel.success) log.success(`${accountName}, ${deviceTypeText}, ${deviceName}, Published as external accessory.`);
|
|
184
200
|
|
|
185
|
-
//start impulse generators
|
|
186
|
-
|
|
201
|
+
//start impulse generators\
|
|
202
|
+
const timmers = accountType === 'melcloudhome' ? [{ name: 'connect', sampling: 900000 }, { name: 'checkDevicesList', sampling: deviceRefreshInterval }] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
203
|
+
await melCloud.impulseGenerator.state(true, timmers);
|
|
187
204
|
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
|
|
188
205
|
|
|
189
206
|
//stop impulse generator
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.0.0-beta.
|
|
4
|
+
"version": "4.0.0-beta.521",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
|
@@ -32,14 +32,15 @@
|
|
|
32
32
|
],
|
|
33
33
|
"engines": {
|
|
34
34
|
"homebridge": "^1.9.0 || ^2.0.0 || ^2.0.0-beta.30 || ^2.0.0-alpha.60",
|
|
35
|
-
"node": "^20 || ^22 || ^24"
|
|
35
|
+
"node": "^20 || ^22 || ^24 || ^25"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@homebridge/plugin-ui-utils": "^2.1.0",
|
|
39
39
|
"async-mqtt": "^2.6.3",
|
|
40
|
-
"axios": "^1.
|
|
40
|
+
"axios": "^1.13.1",
|
|
41
41
|
"express": "^5.1.0",
|
|
42
|
-
"puppeteer": "^24.
|
|
42
|
+
"puppeteer-core": "^24.27.0",
|
|
43
|
+
"util": "^0.12.5"
|
|
43
44
|
},
|
|
44
45
|
"keywords": [
|
|
45
46
|
"homebridge",
|
package/src/constants.js
CHANGED
|
@@ -18,8 +18,7 @@ export const ApiUrls = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export const ApiUrlsHome = {
|
|
21
|
-
|
|
22
|
-
BaseURL: 'https://melcloudhome.com',
|
|
21
|
+
BaseURL: "https://melcloudhome.com",
|
|
23
22
|
GetUserContext: "/api/user/context",
|
|
24
23
|
SetAta: "/api/ataunit/deviceid",
|
|
25
24
|
SetAtw: "/api/atwunit/deviceid",
|
|
@@ -36,25 +35,19 @@ export const DeviceType = [
|
|
|
36
35
|
export const TemperatureDisplayUnits = ["°C", "°F"];
|
|
37
36
|
|
|
38
37
|
export const AirConditioner = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"STRONG", "VERY STRONG", "OFF"
|
|
53
|
-
],
|
|
54
|
-
CurrentOperationModeHeatherCooler: [
|
|
55
|
-
"INACTIVE", "IDLE", "HEATING", "COOLING"
|
|
56
|
-
],
|
|
57
|
-
CurrentOperationModeThermostat: ["INACTIVE", "HEATING", "COOLING"],
|
|
38
|
+
SystemMapEnumToString: { 0: "Air Conditioner Off", 1: "Air Conditioner On", 2: "Air Conditioner Offline" },
|
|
39
|
+
AirDirectionMapEnumToString: { 0: "Auto", 1: "Swing" },
|
|
40
|
+
CurrentOperationModeHeatherCoolerMapEnumToString: { 0: "Inactive", 1: "Idle", 2: "Heating", 3: "Cooling" },
|
|
41
|
+
CurrentOperationModeThermostatMapEnumToString: { 0: "Inactive", 1: "Heating", 2: "Cooling" },
|
|
42
|
+
OperationModeMapStringToEnum: { "0": 0, "Heat": 1, "Dry": 2, "Cool": 3, "4": 4, "5": 5, "6": 6, "Fan": 7, "Automatic": 8, "Isee Heat": 9, "Isee Dry": 10, "Isee Cool": 11 },
|
|
43
|
+
OperationModeMapEnumToString: { 0: "0", 1: "Heat", 2: "Dry", 3: "Cool", 4: "4", 5: "5", 6: "6", 7: "Fan", 8: "Automatic", 9: "Isee Heat", 10: "Isee Dry", 11: "Isee Cool" },
|
|
44
|
+
FanSpeedMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5 },
|
|
45
|
+
FanSpeedMapEnumToString: { 0: "Auto", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five" },
|
|
46
|
+
FanSpeedCurrentMapEnumToString: { 0: "Quiet", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five" },
|
|
47
|
+
VaneVerticalDirectionMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5, "Six": 6, "Swing": 7 },
|
|
48
|
+
VaneVerticalDirectionMapEnumToString: { 0: "Auto", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Swing" },
|
|
49
|
+
VaneHorizontalDirectionMapStringToEnum: { "Auto": 0, "Left": 1, "LeftCentre": 2, "Centre": 3, "RightCentre": 4, "Right": 5, "Six": 6, "Seven": 7, "Split": 8, "Nine": 9, "Ten": 10, "Eleven": 11, "Swing": 12 },
|
|
50
|
+
VaneHorizontalDirectionMapEnumToString: { 0: "Auto", 1: "Left", 2: "LeftCentre", 3: "Centre", 4: "RightCentre", 5: "Right", 6: "Six", 7: "Seven", 8: "Split", 9: "Nine", 10: "Ten", 11: "Eleven", 12: "Swing" },
|
|
58
51
|
EffectiveFlags: {
|
|
59
52
|
Power: 1,
|
|
60
53
|
OperationMode: 2,
|
|
@@ -74,7 +67,7 @@ export const AirConditioner = {
|
|
|
74
67
|
Presets: 287,
|
|
75
68
|
HolidayMode: 131072,
|
|
76
69
|
All: 281483566710825
|
|
77
|
-
}
|
|
70
|
+
},
|
|
78
71
|
};
|
|
79
72
|
|
|
80
73
|
export const HeatPump = {
|