homebridge-melcloud-control 4.2.3-beta.8 → 4.2.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +36 -21
- package/config.schema.json +433 -25
- package/homebridge-ui/public/index.html +86 -16
- package/index.js +3 -3
- package/package.json +2 -2
- package/src/constants.js +48 -5
- package/src/deviceata.js +274 -155
- package/src/deviceatw.js +334 -218
- package/src/deviceerv.js +250 -126
- package/src/melcloud.js +24 -31
- package/src/melcloudata.js +44 -23
- package/src/melcloudatw.js +50 -31
- package/src/melclouderv.js +41 -23
- package/src/melcloudhome.js +76 -37
|
@@ -246,10 +246,11 @@
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
// Initialize devices arrays
|
|
249
|
-
const newDevices = { ata: [], ataPresets: [], atw: [], atwPresets: [], erv: [], ervPresets: [] };
|
|
249
|
+
const newDevices = { ata: [], ataPresets: [], ataSchedules: [], atw: [], atwPresets: [], atwSchedules: [], erv: [], ervPresets: [], ervSchedules: [], scenes: [] };
|
|
250
250
|
const devicesByType = { ata: [], atw: [], erv: [] };
|
|
251
251
|
|
|
252
252
|
response.Devices.forEach(d => {
|
|
253
|
+
d.Scenes = response.Scenes ?? [];
|
|
253
254
|
if (d.Type === 0) devicesByType.ata.push(d);
|
|
254
255
|
if (d.Type === 1) devicesByType.atw.push(d);
|
|
255
256
|
if (d.Type === 3) devicesByType.erv.push(d);
|
|
@@ -263,7 +264,7 @@
|
|
|
263
264
|
const removedAtw = removeStaleDevices(account.atwDevices, devicesByType.atw);
|
|
264
265
|
const removedErv = removeStaleDevices(account.ervDevices, devicesByType.erv);
|
|
265
266
|
|
|
266
|
-
const handleDevices = (devicesInMelCloud, devicesInConfig, typeString, newArr, newPresets) => {
|
|
267
|
+
const handleDevices = (devicesInMelCloud, devicesInConfig, typeString, newArr, newPresets, newSchedules, newScenes) => {
|
|
267
268
|
try {
|
|
268
269
|
const configDevicesMap = new Map(devicesInConfig.map(dev => [String(dev.id), dev]));
|
|
269
270
|
const isMelcloud = account.type === 'melcloud';
|
|
@@ -286,6 +287,7 @@
|
|
|
286
287
|
name: device.DeviceName,
|
|
287
288
|
presets: [],
|
|
288
289
|
schedules: [],
|
|
290
|
+
scenes: [],
|
|
289
291
|
buttonsSensors: []
|
|
290
292
|
};
|
|
291
293
|
devicesInConfig.push(deviceInConfig);
|
|
@@ -294,19 +296,18 @@
|
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
// === Process presets/schedules ===
|
|
297
|
-
const presets = device
|
|
298
|
-
const presetsInConfig = deviceInConfig
|
|
299
|
+
const presets = device.Presets || [];
|
|
300
|
+
const presetsInConfig = deviceInConfig.presets || [];
|
|
299
301
|
const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
|
|
300
302
|
|
|
301
303
|
let addedNewPreset = false;
|
|
302
|
-
|
|
303
304
|
presets.forEach((preset, index) => {
|
|
304
|
-
const presetId = String(preset
|
|
305
|
+
const presetId = String(preset.ID);
|
|
305
306
|
if (!presetIds.has(presetId)) {
|
|
306
307
|
const presetObj = {
|
|
307
308
|
id: presetId,
|
|
308
309
|
displayType: 0,
|
|
309
|
-
name: preset.NumberDescription || `
|
|
310
|
+
name: preset.NumberDescription || `Preset ${index}`,
|
|
310
311
|
namePrefix: false
|
|
311
312
|
};
|
|
312
313
|
presetsInConfig.push(presetObj);
|
|
@@ -319,13 +320,80 @@
|
|
|
319
320
|
// === Remove placeholder presets/schedules (id === '0') if new ones were added ===
|
|
320
321
|
if (addedNewPreset) {
|
|
321
322
|
const beforeCount = presetsInConfig.length;
|
|
322
|
-
deviceInConfig
|
|
323
|
-
const removedCount = beforeCount - deviceInConfig
|
|
323
|
+
deviceInConfig.presets = presetsInConfig.filter(p => String(p.id) !== '0');
|
|
324
|
+
const removedCount = beforeCount - deviceInConfig.presets.length;
|
|
324
325
|
|
|
325
326
|
if (removedCount > 0 && removedCount < beforeCount) {
|
|
326
|
-
updateInfo('info2', `Removed ${removedCount} placeholder
|
|
327
|
+
updateInfo('info2', `Removed ${removedCount} placeholder preset from device ${device.DeviceID}`, 'yellow');
|
|
327
328
|
}
|
|
328
329
|
}
|
|
330
|
+
|
|
331
|
+
// === Process presets/schedules ===
|
|
332
|
+
const schedules = device.Schedules || [];
|
|
333
|
+
const schedulesInConfig = deviceInConfig.schedules || [];
|
|
334
|
+
const scheduleIds = new Set(schedulesInConfig.map(s => String(s.id)));
|
|
335
|
+
|
|
336
|
+
let addedNewSchedule = false;
|
|
337
|
+
schedules.forEach((schedule, index) => {
|
|
338
|
+
const scheduleId = String(schedule.Id);
|
|
339
|
+
if (!scheduleIds.has(scheduleId)) {
|
|
340
|
+
const presetObj = {
|
|
341
|
+
id: scheduleId,
|
|
342
|
+
displayType: 0,
|
|
343
|
+
name: `Schedule ${index}`,
|
|
344
|
+
namePrefix: false
|
|
345
|
+
};
|
|
346
|
+
schedulesInConfig.push(presetObj);
|
|
347
|
+
newPresets.push(presetObj);
|
|
348
|
+
scheduleIds.add(scheduleId);
|
|
349
|
+
addedNewSchedule = true;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// === Remove placeholder schedules (id === '0') if new ones were added ===
|
|
354
|
+
if (addedNewSchedule) {
|
|
355
|
+
const beforeCount = schedulesInConfig.length;
|
|
356
|
+
deviceInConfig.schedules = schedulesInConfig.filter(s => String(s.id) !== '0');
|
|
357
|
+
const removedCount = beforeCount - deviceInConfig.schedules.length;
|
|
358
|
+
|
|
359
|
+
if (removedCount > 0 && removedCount < beforeCount) {
|
|
360
|
+
updateInfo('info2', `Removed ${removedCount} placeholder schedule from device ${device.DeviceID}`, 'yellow');
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// === Process scenes ===
|
|
365
|
+
const scenes = device.Scenes || [];
|
|
366
|
+
const scenesInConfig = deviceInConfig.scenes || [];
|
|
367
|
+
const sceneIds = new Set(scenesInConfig.map(s => String(s.id)));
|
|
368
|
+
|
|
369
|
+
let addedNewScenes = false;
|
|
370
|
+
scenes.forEach((scene, index) => {
|
|
371
|
+
const sceneId = String(scene.Id);
|
|
372
|
+
if (!sceneIds.has(sceneId)) {
|
|
373
|
+
const sceneObj = {
|
|
374
|
+
id: sceneId,
|
|
375
|
+
displayType: 0,
|
|
376
|
+
name: scene.Name || `Scene ${index}`,
|
|
377
|
+
namePrefix: false
|
|
378
|
+
};
|
|
379
|
+
scenesInConfig.push(sceneObj);
|
|
380
|
+
newScenes.push(sceneObj);
|
|
381
|
+
sceneIds.add(sceneId);
|
|
382
|
+
addedNewScenes = true;
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// === Remove placeholder scenes (id === '0') if new ones were added ===
|
|
387
|
+
if (addedNewScenes) {
|
|
388
|
+
const beforeCount = scenesInConfig.length;
|
|
389
|
+
deviceInConfig.scenes = scenesInConfig.filter(s => String(s.id) !== '0');
|
|
390
|
+
const removedCount = beforeCount - deviceInConfig.scenes.length;
|
|
391
|
+
|
|
392
|
+
if (removedCount > 0 && removedCount < beforeCount) {
|
|
393
|
+
updateInfo('info2', `Removed ${removedCount} placeholder scene from device ${device.DeviceID}`, 'yellow');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
329
397
|
});
|
|
330
398
|
|
|
331
399
|
// Return filtered devicesInConfig to make sure upstream code uses it
|
|
@@ -336,21 +404,23 @@
|
|
|
336
404
|
}
|
|
337
405
|
};
|
|
338
406
|
|
|
339
|
-
account.ataDevices = handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets);
|
|
340
|
-
account.atwDevices = handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets);
|
|
341
|
-
account.ervDevices = handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets);
|
|
407
|
+
account.ataDevices = handleDevices(devicesByType.ata, account.ataDevices, "Air Conditioner", newDevices.ata, newDevices.ataPresets, newDevices.ataSchedules, newDevices.scenes);
|
|
408
|
+
account.atwDevices = handleDevices(devicesByType.atw, account.atwDevices, "Heat Pump", newDevices.atw, newDevices.atwPresets, newDevices.atwSchedules, newDevices.scenes);
|
|
409
|
+
account.ervDevices = handleDevices(devicesByType.erv, account.ervDevices, "Energy Recovery Ventilation", newDevices.erv, newDevices.ervPresets, newDevices.ervSchedules, newDevices.scenes);
|
|
342
410
|
|
|
343
411
|
const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
|
|
344
412
|
const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
|
|
413
|
+
const newSchedulesCount = newDevices.ataSchedules.length + newDevices.atwSchedules.length + newDevices.ervSchedules.length;
|
|
414
|
+
const newScenesCount = newDevices.scenes.length;
|
|
345
415
|
const removedDevicesCount = removedAta.length + removedAtw.length + removedErv.length;
|
|
346
416
|
|
|
347
|
-
if (!newDevicesCount && !newPresetsCount && !removedDevicesCount) {
|
|
417
|
+
if (!newDevicesCount && !newPresetsCount && !newSchedulesCount && !newScenesCount && !removedDevicesCount) {
|
|
348
418
|
updateInfo('info', 'No changes detected.', 'white');
|
|
349
419
|
} else {
|
|
350
420
|
if (newDevicesCount)
|
|
351
421
|
updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
|
|
352
|
-
if (newPresetsCount)
|
|
353
|
-
updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
|
|
422
|
+
if (newPresetsCount || newScenesCount || newSchedulesCount)
|
|
423
|
+
updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${account.type === 'melcloud' ? newDevices.ataPresets.length : newDevices.ataSchedules.length}, ATW: ${account.type === 'melcloud' ? newDevices.atwPresets.length : newDevices.atwSchedules.length}, ERV: ${account.type === 'melcloud' ? newDevices.ervPresets.length : newDevices.ervSchedules.length}, Scenes: ${newDevices.scenes.length}.`, 'green');
|
|
354
424
|
if (removedDevicesCount)
|
|
355
425
|
updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
|
|
356
426
|
}
|
package/index.js
CHANGED
|
@@ -183,8 +183,8 @@ class MelCloudPlatform {
|
|
|
183
183
|
|
|
184
184
|
configuredDevice.on('melCloud', async (key, value) => {
|
|
185
185
|
try {
|
|
186
|
-
accountInfo.LoginData[key] = value;
|
|
187
|
-
await melCloud.send(
|
|
186
|
+
const accountDate = account.type === 'melcloud' ? accountInfo.LoginData[key] = value : accountInfo[key];
|
|
187
|
+
await melCloud.send(accountDate);
|
|
188
188
|
} catch (error) {
|
|
189
189
|
if (logLevel.error) log.error(`${accountName}, ${deviceTypeText}, ${deviceName}, ${error.message ?? error}.`);
|
|
190
190
|
}
|
|
@@ -204,7 +204,7 @@ class MelCloudPlatform {
|
|
|
204
204
|
//start impulse generators\
|
|
205
205
|
const timmers = accountType === 'melcloudhome' ? [{ name: 'connect', sampling: 1800000 }, { name: 'checkDevicesList', sampling: deviceRefreshInterval }] : [{ name: 'checkDevicesList', sampling: refreshInterval }];
|
|
206
206
|
await melCloud.impulseGenerator.state(true, timmers, false);
|
|
207
|
-
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval
|
|
207
|
+
await configuredDevice.startStopImpulseGenerator(true, [{ name: 'checkState', sampling: deviceRefreshInterval }]);
|
|
208
208
|
|
|
209
209
|
//stop impulse generator
|
|
210
210
|
await impulseGenerator.state(false);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"displayName": "MELCloud Control",
|
|
3
3
|
"name": "homebridge-melcloud-control",
|
|
4
|
-
"version": "4.2.
|
|
4
|
+
"version": "4.2.5-beta.0",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"node": "^20 || ^22 || ^24 || ^25"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@homebridge/plugin-ui-utils": "^2.1.
|
|
38
|
+
"@homebridge/plugin-ui-utils": "^2.1.1",
|
|
39
39
|
"async-mqtt": "^2.6.3",
|
|
40
40
|
"axios": "^1.13.2",
|
|
41
41
|
"express": "^5.1.0",
|
package/src/constants.js
CHANGED
|
@@ -20,14 +20,28 @@ export const ApiUrls = {
|
|
|
20
20
|
export const ApiUrlsHome = {
|
|
21
21
|
BaseURL: "https://melcloudhome.com",
|
|
22
22
|
GetUserContext: "/api/user/context",
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
GetUserScenes: "/api/user/scenes",
|
|
24
|
+
PutAta: "/api/ataunit/deviceid",
|
|
25
|
+
PutAtw: "/api/atwunit/deviceid",
|
|
26
|
+
PutErv: "/api/ervunit/deviceid",
|
|
27
27
|
PostSchedule: " /api/cloudschedule/deviceid", // POST {"days":[2],"time":"17:59:00","enabled":true,"id":"53c5e804-0663-47d0-85c2-2d8ccd2573de","power":false,"operationMode":null,"setPoint":null,"vaneVerticalDirection":null,"vaneHorizontalDirection":null,"setFanSpeed":null}
|
|
28
28
|
PostProtectionFrost: "/api/protection/frost", // POST {"enabled":true,"min":13,"max":16,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
29
29
|
PostProtectionOverheat: "api/protection/overheat", // POST {"enabled":true,"min":32,"max":35,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
30
|
-
PostHolidayMode: " /api/holidaymode" // POST {"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
30
|
+
PostHolidayMode: " /api/holidaymode", // POST {"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
31
|
+
PutScheduleEnabled: "/api/cloudschedule/deviceid/enabled", // PUT {"enabled":true}
|
|
32
|
+
PutScene: {
|
|
33
|
+
Enable: "/api/scene/sceneid/enable",
|
|
34
|
+
Disable: "/api/scene/sceneid/disable",
|
|
35
|
+
},
|
|
36
|
+
Referers: {
|
|
37
|
+
GetPutScenes: "https://melcloudhome.com/scenes",
|
|
38
|
+
PostHolidayMode: "https://melcloudhome.com/ata/deviceid/holidaymode",
|
|
39
|
+
PostProtectionFrost: "https://melcloudhome.com/ata/deviceid/frostprotection",
|
|
40
|
+
PostProtectionOverheat: "https://melcloudhome.com/ata/deviceid/overheatprotection",
|
|
41
|
+
PutDeviceSettings: "https://melcloudhome.com/dashboard",
|
|
42
|
+
PutScheduleEnabled: "https://melcloudhome.com/ata/deviceid/schedule",
|
|
43
|
+
},
|
|
44
|
+
Origin: "https://melcloudhome.com"
|
|
31
45
|
};
|
|
32
46
|
|
|
33
47
|
export const DeviceType = [
|
|
@@ -163,3 +177,32 @@ export const AccessLevel = {
|
|
|
163
177
|
Quest: 3,
|
|
164
178
|
Owner: 4
|
|
165
179
|
};
|
|
180
|
+
|
|
181
|
+
export const LanguageLocaleMap = {
|
|
182
|
+
"0": "en-US,en;q=0.9",
|
|
183
|
+
"1": "bg-BG,bg;q=0.9",
|
|
184
|
+
"2": "cs-CZ,cs;q=0.9",
|
|
185
|
+
"3": "da-DK,da;q=0.9",
|
|
186
|
+
"4": "de-DE,de;q=0.9",
|
|
187
|
+
"5": "et-EE,et;q=0.9",
|
|
188
|
+
"6": "es-ES,es;q=0.9",
|
|
189
|
+
"7": "fr-FR,fr;q=0.9",
|
|
190
|
+
"8": "hy-AM,hy;q=0.9",
|
|
191
|
+
"9": "lv-LV,lv;q=0.9",
|
|
192
|
+
"10": "lt-LT,lt;q=0.9",
|
|
193
|
+
"11": "hu-HU,hu;q=0.9",
|
|
194
|
+
"12": "nl-NL,nl;q=0.9",
|
|
195
|
+
"13": "no-NO,no;q=0.9",
|
|
196
|
+
"14": "pl-PL,pl;q=0.9",
|
|
197
|
+
"15": "pt-PT,pt;q=0.9",
|
|
198
|
+
"16": "ru-RU,ru;q=0.9",
|
|
199
|
+
"17": "fi-FI,fi;q=0.9",
|
|
200
|
+
"18": "sv-SE,sv;q=0.9",
|
|
201
|
+
"19": "it-IT,it;q=0.9",
|
|
202
|
+
"20": "uk-UA,uk;q=0.9",
|
|
203
|
+
"21": "tr-TR,tr;q=0.9",
|
|
204
|
+
"22": "el-GR,el;q=0.9",
|
|
205
|
+
"23": "hr-HR,hr;q=0.9",
|
|
206
|
+
"24": "ro-RO,ro;q=0.9",
|
|
207
|
+
"25": "sl-SI,sl;q=0.9"
|
|
208
|
+
};
|