homebridge-melcloud-control 4.0.0-beta.43 → 4.0.0-beta.430
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/README.md +12 -11
- package/config.schema.json +272 -314
- package/homebridge-ui/public/index.html +104 -64
- package/homebridge-ui/server.js +5 -9
- package/index.js +38 -20
- package/package.json +6 -3
- package/src/constants.js +15 -22
- package/src/deviceata.js +199 -188
- package/src/deviceatw.js +7 -5
- package/src/deviceerv.js +28 -26
- package/src/functions.js +124 -3
- package/src/melcloud.js +312 -131
- package/src/melcloudata.js +139 -308
- package/src/melcloudatw.js +3 -228
- package/src/melclouderv.js +5 -162
- package/src/melcloudhometoken.js +231 -0
- 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>
|
|
@@ -90,14 +90,14 @@
|
|
|
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="d-flex flex-wrap justify-content-center gap-1 mt-3"></div>
|
|
93
94
|
</div>
|
|
94
|
-
|
|
95
|
-
<div id="accountButton" class="mt-2"></div>
|
|
96
95
|
</div>
|
|
97
96
|
|
|
98
97
|
<script>
|
|
99
98
|
(async () => {
|
|
100
99
|
const pluginConfig = await homebridge.getPluginConfig();
|
|
100
|
+
|
|
101
101
|
if (!pluginConfig.length) {
|
|
102
102
|
pluginConfig.push({});
|
|
103
103
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -105,54 +105,79 @@
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
const accountsCount =
|
|
111
|
-
|
|
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) => {
|
|
112
131
|
const button = document.createElement("button");
|
|
113
132
|
button.type = "button";
|
|
114
133
|
button.id = `button${i}`;
|
|
115
134
|
button.className = "btn btn-primary";
|
|
116
135
|
button.style.textTransform = 'none';
|
|
117
|
-
button.innerText =
|
|
118
|
-
|
|
136
|
+
button.innerText = account.name || `Account ${i + 1}`;
|
|
137
|
+
container.appendChild(button);
|
|
119
138
|
|
|
120
139
|
button.addEventListener('click', async () => {
|
|
121
|
-
|
|
122
|
-
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-secondary' : 'btn btn-primary');
|
|
123
|
-
}
|
|
140
|
+
this.accountIndex = i;
|
|
124
141
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
document.getElementById('passwd').value = acc.passwd || '';
|
|
130
|
-
document.getElementById('language').value = acc.language || '';
|
|
131
|
-
document.getElementById('displayType').value = acc.displayType || '';
|
|
132
|
-
document.getElementById('logIn').disabled = !(acc.name && acc.user && acc.passwd && acc.language && acc.displayType);
|
|
133
|
-
this.deviceIndex = i;
|
|
134
|
-
});
|
|
142
|
+
// Zmieniamy klasę wszystkich przycisków
|
|
143
|
+
accounts.forEach((_, j) => {
|
|
144
|
+
document.getElementById(`button${j}`).className = (j === i ? 'btn btn-primary' : 'btn btn-secondary');
|
|
145
|
+
});
|
|
135
146
|
|
|
136
|
-
|
|
137
|
-
|
|
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);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
138
157
|
|
|
139
|
-
|
|
158
|
+
// Klikamy pierwszy przycisk po zakończeniu pętli
|
|
159
|
+
if (accountsCount > 0) document.getElementById('button0').click();
|
|
140
160
|
|
|
161
|
+
// Jeden listener input dla całego formularza
|
|
141
162
|
document.getElementById('configForm').addEventListener('input', async () => {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
163
|
+
const account = accounts[this.accountIndex];
|
|
164
|
+
if (!account) return;
|
|
165
|
+
|
|
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;
|
|
148
171
|
|
|
149
|
-
|
|
172
|
+
formElements.logIn.disabled = !(account.name && account.user && account.passwd && account.language && account.type);
|
|
150
173
|
|
|
151
174
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
152
175
|
await homebridge.savePluginConfig(pluginConfig);
|
|
153
176
|
});
|
|
154
177
|
|
|
155
|
-
|
|
178
|
+
document.getElementById('melCloudAccount').style.display = 'block';
|
|
179
|
+
|
|
180
|
+
// Config Button Toggle
|
|
156
181
|
const configButton = document.getElementById('configButton');
|
|
157
182
|
let configButtonState = false;
|
|
158
183
|
configButton.addEventListener('click', () => {
|
|
@@ -174,13 +199,14 @@
|
|
|
174
199
|
}
|
|
175
200
|
});
|
|
176
201
|
|
|
177
|
-
//
|
|
202
|
+
// Device Handling & Login Logic
|
|
178
203
|
function removeStaleDevices(configDevices, melcloudDevices) {
|
|
179
204
|
const melcloudIds = melcloudDevices.map(d => d.DeviceID);
|
|
180
205
|
const removedDevices = [];
|
|
206
|
+
|
|
181
207
|
for (let i = configDevices.length - 1; i >= 0; i--) {
|
|
182
208
|
const device = configDevices[i];
|
|
183
|
-
if (device.id !== 0 && !melcloudIds.includes(device.id)) {
|
|
209
|
+
if (device.id !== "0" && !melcloudIds.includes(device.id)) {
|
|
184
210
|
removedDevices.push(device);
|
|
185
211
|
configDevices.splice(i, 1);
|
|
186
212
|
}
|
|
@@ -197,14 +223,14 @@
|
|
|
197
223
|
}
|
|
198
224
|
|
|
199
225
|
document.getElementById('logIn').addEventListener('click', async () => {
|
|
200
|
-
homebridge.showSpinner();
|
|
201
226
|
document.getElementById(`logIn`).className = "btn btn-primary";
|
|
227
|
+
|
|
202
228
|
updateInfo('info', 'Connecting...', 'yellow');
|
|
229
|
+
homebridge.showSpinner();
|
|
203
230
|
|
|
204
231
|
try {
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const devicesInMelCloud = await homebridge.request('/connect', payload);
|
|
232
|
+
const account = pluginConfig[0].accounts[this.accountIndex];
|
|
233
|
+
const devicesInMelCloud = await homebridge.request('/connect', account);
|
|
208
234
|
|
|
209
235
|
// Initialize devices arrays
|
|
210
236
|
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
@@ -216,42 +242,53 @@
|
|
|
216
242
|
if (d.Type === 3) devicesByType.erv.push(d);
|
|
217
243
|
});
|
|
218
244
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
245
|
+
account.ataDevices ??= [];
|
|
246
|
+
account.atwDevices ??= [];
|
|
247
|
+
account.ervDevices ??= [];
|
|
222
248
|
|
|
223
|
-
const removedAta = removeStaleDevices(
|
|
224
|
-
const removedAtw = removeStaleDevices(
|
|
225
|
-
const removedErv = removeStaleDevices(
|
|
249
|
+
const removedAta = removeStaleDevices(account.ataDevices, devicesByType.ata);
|
|
250
|
+
const removedAtw = removeStaleDevices(account.atwDevices, devicesByType.atw);
|
|
251
|
+
const removedErv = removeStaleDevices(account.ervDevices, devicesByType.erv);
|
|
226
252
|
|
|
227
|
-
// Function to handle device & presets
|
|
228
253
|
const handleDevices = (devicesInCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
229
254
|
devicesInCloud.forEach(device => {
|
|
230
255
|
const { DeviceID: id, Type: type, DeviceName: name, Presets: presets = [] } = device;
|
|
231
|
-
const devObj = {
|
|
256
|
+
const devObj = structuredClone({
|
|
257
|
+
id,
|
|
258
|
+
type,
|
|
259
|
+
typeString,
|
|
260
|
+
name,
|
|
261
|
+
displayType: 0,
|
|
262
|
+
presets: [],
|
|
263
|
+
buttonsSensors: []
|
|
264
|
+
});
|
|
265
|
+
|
|
232
266
|
if (!devicesInConfig.some(d => d.id === id)) {
|
|
233
267
|
devicesInConfig.push(devObj);
|
|
234
|
-
newArr.push(devObj);
|
|
268
|
+
newArr.push(structuredClone(devObj));
|
|
235
269
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
p
|
|
239
|
-
|
|
240
|
-
|
|
270
|
+
|
|
271
|
+
presets.forEach(preset => {
|
|
272
|
+
const p = structuredClone({
|
|
273
|
+
id: preset.ID,
|
|
274
|
+
name: preset.NumberDescription,
|
|
275
|
+
displayType: 0,
|
|
276
|
+
namePrefix: false
|
|
277
|
+
});
|
|
278
|
+
|
|
241
279
|
const devConfig = devicesInConfig.find(d => d.id === id);
|
|
242
|
-
if (devConfig && !devConfig.presets.some(x => x.id === p.
|
|
280
|
+
if (devConfig && !devConfig.presets.some(x => x.id === p.id)) {
|
|
243
281
|
devConfig.presets.push(p);
|
|
244
|
-
newPresets.push(p);
|
|
282
|
+
newPresets.push(structuredClone(p));
|
|
245
283
|
}
|
|
246
284
|
});
|
|
247
285
|
});
|
|
248
286
|
};
|
|
249
287
|
|
|
250
|
-
handleDevices(devicesByType.ata,
|
|
251
|
-
handleDevices(devicesByType.atw,
|
|
252
|
-
handleDevices(devicesByType.erv,
|
|
288
|
+
handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
289
|
+
handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
290
|
+
handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
|
|
253
291
|
|
|
254
|
-
// Display summary
|
|
255
292
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
256
293
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
257
294
|
const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
@@ -259,9 +296,12 @@
|
|
|
259
296
|
if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
|
|
260
297
|
updateInfo('info', 'No changes detected.', 'white');
|
|
261
298
|
} else {
|
|
262
|
-
if (newDevicesCount)
|
|
263
|
-
|
|
264
|
-
if (
|
|
299
|
+
if (newDevicesCount)
|
|
300
|
+
updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
301
|
+
if (newPresetsCount)
|
|
302
|
+
updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
303
|
+
if (removedDevicesCount)
|
|
304
|
+
updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
265
305
|
}
|
|
266
306
|
|
|
267
307
|
await homebridge.updatePluginConfig(pluginConfig);
|
|
@@ -270,7 +310,7 @@
|
|
|
270
310
|
|
|
271
311
|
} catch (error) {
|
|
272
312
|
updateInfo('info', 'Check Your credentials data and try again.', 'yellow');
|
|
273
|
-
updateInfo('info1', `Error: ${error}`, 'red');
|
|
313
|
+
updateInfo('info1', `Error: ${JSON.stringify(error)}`, 'red');
|
|
274
314
|
document.getElementById('logIn').className = "btn btn-secondary";
|
|
275
315
|
} finally {
|
|
276
316
|
homebridge.hideSpinner();
|
package/homebridge-ui/server.js
CHANGED
|
@@ -12,20 +12,16 @@ 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
|
-
|
|
28
|
-
const devices = await melCloud.checkDevicesList(
|
|
23
|
+
await melCloud.connect();
|
|
24
|
+
const devices = await melCloud.checkDevicesList();
|
|
29
25
|
return devices;
|
|
30
26
|
} catch (error) {
|
|
31
27
|
throw new Error(`MELCloud error: ${error.message ?? error}.`);
|
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;
|
|
@@ -80,7 +80,6 @@ class MelCloudPlatform {
|
|
|
80
80
|
const buildingsFile = `${prefDir}/${accountName}_Buildings`;
|
|
81
81
|
const devicesFile = `${prefDir}/${accountName}_Devices`;
|
|
82
82
|
|
|
83
|
-
|
|
84
83
|
//set account refresh interval
|
|
85
84
|
const refreshInterval = (account.refreshInterval ?? 120) * 1000
|
|
86
85
|
|
|
@@ -90,7 +89,7 @@ class MelCloudPlatform {
|
|
|
90
89
|
.on('start', async () => {
|
|
91
90
|
try {
|
|
92
91
|
//melcloud account
|
|
93
|
-
const melCloud = new MelCloud(
|
|
92
|
+
const melCloud = new MelCloud(account, accountFile, buildingsFile, devicesFile, true)
|
|
94
93
|
.on('success', (msg) => logLevel.success && log.success(`${accountName}, ${msg}`))
|
|
95
94
|
.on('info', (msg) => logLevel.info && log.info(`${accountName}, ${msg}`))
|
|
96
95
|
.on('debug', (msg) => logLevel.debug && log.info(`${accountName}, debug: ${msg}`))
|
|
@@ -98,17 +97,16 @@ class MelCloudPlatform {
|
|
|
98
97
|
.on('error', (msg) => logLevel.error && log.error(`${accountName}, ${msg}`));
|
|
99
98
|
|
|
100
99
|
//connect
|
|
101
|
-
let
|
|
100
|
+
let accountInfo;
|
|
102
101
|
try {
|
|
103
|
-
|
|
102
|
+
accountInfo = await melCloud.connect();
|
|
104
103
|
} catch (error) {
|
|
105
104
|
if (logLevel.error) log.error(`${accountName}, Connect error: ${error.message ?? error}`);
|
|
106
105
|
return;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const useFahrenheit = response.useFahrenheit;
|
|
108
|
+
const contextKey = accountInfo.ContextKey;
|
|
109
|
+
const useFahrenheit = accountInfo.UseFahrenheit;
|
|
112
110
|
|
|
113
111
|
if (!contextKey) {
|
|
114
112
|
return;
|
|
@@ -117,7 +115,7 @@ class MelCloudPlatform {
|
|
|
117
115
|
//check devices list
|
|
118
116
|
let devicesInMelcloud;
|
|
119
117
|
try {
|
|
120
|
-
devicesInMelcloud = await melCloud.checkDevicesList(
|
|
118
|
+
devicesInMelcloud = await melCloud.checkDevicesList();
|
|
121
119
|
} catch (error) {
|
|
122
120
|
if (logLevel.error) log.error(`${accountName}, Check devices list error: ${error.message ?? error}`);
|
|
123
121
|
return;
|
|
@@ -133,10 +131,9 @@ class MelCloudPlatform {
|
|
|
133
131
|
|
|
134
132
|
for (const device of devices) {
|
|
135
133
|
//chack device from config exist on melcloud
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
if (!deviceExistInMelCloud || !displayMode) {
|
|
134
|
+
const displayType = device.displayType > 0;
|
|
135
|
+
const deviceExistInMelCloud = devicesInMelcloud.some(dev => dev.DeviceID === device.id);
|
|
136
|
+
if (!deviceExistInMelCloud || !displayType) {
|
|
140
137
|
continue;
|
|
141
138
|
}
|
|
142
139
|
|
|
@@ -144,19 +141,39 @@ class MelCloudPlatform {
|
|
|
144
141
|
const deviceType = device.type;
|
|
145
142
|
const deviceTypeText = device.typeString;
|
|
146
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) {
|
|
159
|
+
log.error(`Device: ${host} ${deviceName}, File init error: ${error.message}`);
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
147
164
|
|
|
148
165
|
let configuredDevice;
|
|
149
166
|
switch (deviceType) {
|
|
150
167
|
case 0: //ATA
|
|
151
|
-
configuredDevice = new DeviceAta(api, account, device,
|
|
168
|
+
configuredDevice = new DeviceAta(api, account, device, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
152
169
|
break;
|
|
153
170
|
case 1: //ATW
|
|
154
|
-
configuredDevice = new DeviceAtw(api, account, device, contextKey, devicesFile, useFahrenheit, restFul, mqtt);
|
|
171
|
+
configuredDevice = new DeviceAtw(api, account, device, contextKey, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
155
172
|
break;
|
|
156
173
|
case 2:
|
|
157
174
|
break;
|
|
158
175
|
case 3: //ERV
|
|
159
|
-
configuredDevice = new DeviceErv(api, account, device, contextKey, devicesFile, useFahrenheit, restFul, mqtt);
|
|
176
|
+
configuredDevice = new DeviceErv(api, account, device, contextKey, devicesFile, defaultTempsFile, useFahrenheit, restFul, mqtt);
|
|
160
177
|
break;
|
|
161
178
|
default:
|
|
162
179
|
if (logLevel.warn) log.warn(`${accountName}, ${deviceTypeText}, ${deviceName}, unknown device: ${deviceType}.`);
|
|
@@ -183,8 +200,9 @@ class MelCloudPlatform {
|
|
|
183
200
|
api.publishExternalAccessories(PluginName, [accessory]);
|
|
184
201
|
if (logLevel.success) log.success(`${accountName}, ${deviceTypeText}, ${deviceName}, Published as external accessory.`);
|
|
185
202
|
|
|
186
|
-
//start impulse generators
|
|
187
|
-
|
|
203
|
+
//start impulse generators\
|
|
204
|
+
const timmers = accountType === 'melcloudhome' ? [{ name: 'connect', sampling: 150000 }, { name: 'checkDevicesList', sampling: deviceRefreshInterval }] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
205
|
+
await melCloud.impulseGenerator.state(true, timmers);
|
|
188
206
|
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
|
|
189
207
|
|
|
190
208
|
//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.430",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
|
@@ -37,9 +37,12 @@
|
|
|
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.0",
|
|
41
41
|
"express": "^5.1.0",
|
|
42
|
-
"puppeteer": "^24.26.
|
|
42
|
+
"puppeteer-core": "^24.26.1",
|
|
43
|
+
"axios-cookiejar-support": "^6.0.4",
|
|
44
|
+
"tough-cookie": "^6.0.0",
|
|
45
|
+
"jsdom": "^27.0.1"
|
|
43
46
|
},
|
|
44
47
|
"keywords": [
|
|
45
48
|
"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: "ir Conditioner On", 2: "ir 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 = {
|