homebridge-melcloud-control 4.1.2-beta.61 → 4.1.2-beta.63

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 CHANGED
@@ -22,10 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
22
 
23
23
  - Do not use Homebridge UI > v5.5.0 because of break config.json
24
24
 
25
- ## [4.1.2] - (10.11.2025)
25
+ ## [4.1.2] - (11.11.2025)
26
26
 
27
27
  ## Changes
28
28
 
29
+ - fix [#218](https://github.com/grzegorz914/homebridge-melcloud-control/issues/218)
29
30
  - added presets/schedules support for MELCLoud Home ATA devices
30
31
  - config schema updated
31
32
  - readme updated
package/README.md CHANGED
@@ -65,6 +65,7 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
65
65
  * Vane V mode `AUTO/1/2/3/4/5/SWING`.
66
66
  * Fan speed mode `AUTO/1/2/3/4/5`.
67
67
  * Presets `SET/UNSET`.
68
+ * Schedules `ON/OFF`.
68
69
  * Sensors:
69
70
  * For automation and notifications.
70
71
  * Power `ON/OFF`.
@@ -232,10 +233,14 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
232
233
  | `ataDevices[].scheduleSensor` | This enable extra `Shedule` sensor to use with automations in HomeKit app. |
233
234
  | `ataDevices[].errorSensor` | This enable `Error` sensor to use with automations in HomeKit app. |
234
235
  | `ataDevices[].refreshInterval` | Here set the background devices state refresh time in (sec), default `5s`. |
235
- | `ataDevices[].presets[]` | Array of ATA device Presets created automatically after login to MELCloud from plugin config UI. |
236
+ | `ataDevices[].presets[]` | Array of ATA device `Presets` created automatically after login to MELCloud from plugin config UI. |
236
237
  | `ataDevices[].presets[].id` | Read only data, do not change it. |
237
238
  | `ataDevices[].presets[].name` | Here You can schange the `Preset Name` which is exposed to the `Homebridge/HomeKit`. |
238
239
  | `ataDevices[].presets[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
240
+ | `ataDevices[].schedules[]` | Array of ATA device `Schedules` created automatically after login to MELCloud Home from plugin config UI. |
241
+ | `ataDevices[].schedules[].id` | Read only data, do not change it. |
242
+ | `ataDevices[].schedules[].name` | Here You can schange the `Schedule Name` which is exposed to the `Homebridge/HomeKit`. |
243
+ | `ataDevices[].schedules[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
239
244
  | `ataDevices[].buttonsSensors[]` | Array of buttons sensors. |
240
245
  | `ataDevices[].buttonsSensors[].name` | Here set `Button Name` which You want expose to the `Homebridge/HomeKit`. |
241
246
  | `ataDevices[].buttonsSensors[].mode` | Here select button mode, VH - Vane Horizontal, VV - Vane Horizontal. |
@@ -259,10 +264,14 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
259
264
  | `atwDevices[].temperatureReturnZone2Sensor` | This enable extra `Return Zone 2` temperature sensor to use with automations in HomeKit app. |
260
265
  | `atwDevices[].errorSensor` | This enable `Error` sensors to use with automations in HomeKit app. |
261
266
  | `atwDevices[].refreshInterval` | Here set the background devices state refresh time in (sec), default `5s`. |
262
- | `atwDevices[].presets[]` | Array of ATA device Presets created automatically after login to MELCloud from plugin config UI. |
267
+ | `atwDevices[].presets[]` | Array of ATW device `Presets` created automatically after login to MELCloud from plugin config UI. |
263
268
  | `atwDevices[].presets[].id` | Read only data, do not change it. |
264
269
  | `atwDevices[].presets[].name` | Here You can schange the `Preset Name` which is exposed to the `Homebridge/HomeKit`. |
265
270
  | `atwDevices[].presets[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
271
+ | `atwDevices[].schedules[]` | Array of ATW device `Schedules` created automatically after login to MELCloud Home from plugin config UI. |
272
+ | `atwDevices[].schedules[].id` | Read only data, do not change it. |
273
+ | `atwDevices[].schedules[].name` | Here You can schange the `Schedule Name` which is exposed to the `Homebridge/HomeKit`. |
274
+ | `atwDevices[].schedules[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
266
275
  | `atwDevices[].buttonsSensors[]` | Array of buttons sensors. |
267
276
  | `atwDevices[].buttonsSensors[].name` | Here set `Button Name` which You want expose to the `Homebridge/HomeKit`. |
268
277
  | `atwDevices[].buttonsSensors[].mode` | Here select button mode. |
@@ -279,10 +288,14 @@ Homebridge plugin for Air Conditioner, Heat Pump and Energy Recovery Ventilation
279
288
  | `ervDevices[].temperatureSupplySensor` | This enable extra `Supply` temperature sensor to use with automations in HomeKit app. |
280
289
  | `ervDevices[].errorSensor` | This enable `Error` sensors to use with automations in HomeKit app. |
281
290
  | `ervDevices[].refreshInterval` | Here set the background devices state refresh time in (sec), default `5s`. |
282
- | `ervDevices[].presets[]` | Array of ATA device Presets created automatically after login to MELCloud from plugin config UI. |
291
+ | `ervDevices[].presets[]` | Array of ERV device `Presets` created automatically after login to MELCloud from plugin config UI. |
283
292
  | `ervDevices[].presets[].id` | Read only data, do not change it. |
284
293
  | `ervDevices[].presets[].name` | Here You can schange the `Preset Name` which is exposed to the `Homebridge/HomeKit`. |
285
294
  | `ervDevices[].presets[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
295
+ | `ervDevices[].schedules[]` | Array of ERV device `Schedules` created automatically after login to MELCloud Home from plugin config UI. |
296
+ | `ervDevices[].schedules[].id` | Read only data, do not change it. |
297
+ | `ervDevices[].schedules[].name` | Here You can schange the `Schedule Name` which is exposed to the `Homebridge/HomeKit`. |
298
+ | `ervDevices[].schedules[].displayType` | Here select display type in HomeKit, `0 - None/Disabled`, `1 - Outlet`, `2 - Switch`, `3 - Motion Sensor`, `4 - Occupancy Sensor`, `5 - Contact Sensor`. |
286
299
  | `ervDevices[].buttonsSensors[]` | Array of buttons sensors. |
287
300
  | `ervDevices[].buttonsSensors[].name` | Here set `Button Name` which You want expose to the `Homebridge/HomeKit`. |
288
301
  | `ervDevices[].buttonsSensors[].mode` | Here select button mode. |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.1.2-beta.61",
4
+ "version": "4.1.2-beta.63",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/functions.js CHANGED
@@ -70,25 +70,41 @@ class Functions extends EventEmitter {
70
70
 
71
71
  // --- Detect Docker ---
72
72
  let isDocker = false;
73
- try { await access('/.dockerenv', fs.constants.F_OK); isDocker = true; } catch { }
73
+ try {
74
+ await access('/.dockerenv', fs.constants.F_OK); isDocker = true;
75
+ } catch { }
76
+
74
77
  try {
75
78
  const { stdout } = await execPromise('cat /proc/1/cgroup || true');
76
79
  if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
77
80
  } catch { }
81
+
78
82
  if (isDocker && this.logDebug) this.emit('debug', 'Running inside Docker container.');
79
83
 
80
84
  // === macOS ===
81
85
  if (osName === 'Darwin') {
82
86
  chromiumPath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
83
- try { await access(chromiumPath, fs.constants.X_OK); return chromiumPath; } catch { return null; }
87
+ try {
88
+ await access(chromiumPath, fs.constants.X_OK);
89
+ return chromiumPath;
90
+ } catch {
91
+ return null;
92
+ }
84
93
  }
85
94
 
86
95
  // === ARM ===
87
96
  if (arch.startsWith('arm')) {
88
- try { await access('/usr/bin/chromium-browser', fs.constants.X_OK); return '/usr/bin/chromium-browser'; }
89
- catch {
90
- try { await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg'); return '/usr/bin/chromium-browser'; }
91
- catch { return null; }
97
+ try {
98
+ chromiumPath = '/usr/bin/chromium-browser';
99
+ await access(chromiumPath, fs.constants.X_OK);
100
+ return chromiumPath;
101
+ } catch {
102
+ try {
103
+ await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg');
104
+ return chromiumPath;
105
+ } catch {
106
+ return null;
107
+ }
92
108
  }
93
109
  }
94
110
 
@@ -102,13 +118,16 @@ class Functions extends EventEmitter {
102
118
 
103
119
  // --- Detect Entware (QNAP) ---
104
120
  let entwareExists = false;
105
- try { await access('/opt/bin/opkg', fs.constants.X_OK); entwareExists = true; } catch { }
121
+ try {
122
+ await access('/opt/bin/opkg', fs.constants.X_OK);
123
+ entwareExists = true;
124
+ } catch { }
106
125
 
107
126
  if (entwareExists) {
108
127
  try {
109
128
  await execPromise('/opt/bin/opkg update');
110
129
  await execPromise('/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr libatk libatk-bridge libcups libdrm libgbm libasound');
111
- process.env.LD_LIBRARY_PATH = '/opt/lib:' + (process.env.LD_LIBRARY_PATH || '');
130
+ process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
112
131
  } catch { }
113
132
  }
114
133
 
@@ -119,16 +138,16 @@ class Functions extends EventEmitter {
119
138
  'yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib'
120
139
  ];
121
140
  for (const cmd of depInstall) {
122
- try { await execPromise(`sudo ${cmd}`); } catch { }
141
+ try {
142
+ await execPromise(`sudo ${cmd}`);
143
+ } catch { }
123
144
  }
124
145
 
125
146
  // Set LD_LIBRARY_PATH so Puppeteer's Chromium can find libs
126
- process.env.LD_LIBRARY_PATH = '/usr/lib:/usr/lib64:' + (process.env.LD_LIBRARY_PATH || '');
127
-
147
+ process.env.LD_LIBRARY_PATH = `/usr/lib:/usr/lib64:${process.env.LD_LIBRARY_PATH || ''}`;
128
148
  return systemChromium;
129
149
  }
130
150
 
131
-
132
151
  if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}.`);
133
152
  return null;
134
153
  } catch (error) {
package/src/melcloud.js CHANGED
@@ -345,9 +345,11 @@ class MelCloud extends EventEmitter {
345
345
  const puppeteerPath = puppeteer.executablePath();
346
346
  if (puppeteerPath && fs.existsSync(puppeteerPath)) {
347
347
  chromiumPath = puppeteerPath;
348
- if (this.logDebug) this.emit('debug', `Using bundled Chromium from Puppeteer at ${chromiumPath}`);
348
+ if (this.logDebug) this.emit('debug', `Using puppeteer Chromium at ${chromiumPath}`);
349
349
  }
350
350
  } catch { }
351
+ } else {
352
+ if (this.logDebug) this.emit('debug', `Using system Chromium at ${chromiumPath}`);
351
353
  }
352
354
 
353
355
  if (!chromiumPath) {
@@ -23,7 +23,6 @@ class MelCloudAta extends EventEmitter {
23
23
 
24
24
  //set default values
25
25
  this.deviceData = {};
26
- this.headers = {};
27
26
 
28
27
  //lock flags
29
28
  this.locks = {
@@ -60,7 +59,7 @@ class MelCloudAta extends EventEmitter {
60
59
  return null;
61
60
  }
62
61
  const deviceData = devicesData.find(device => device.DeviceID === this.deviceId);
63
- this.headers = deviceData.Headers;
62
+
64
63
  if (this.accountType === 'melcloudhome') {
65
64
  deviceData.SerialNumber = deviceData.DeviceID || '4.0.0';
66
65
  deviceData.Device.FirmwareAppVersion = deviceData.ConnectedInterfaceIdentifier || '4.0.0';
@@ -75,7 +74,12 @@ class MelCloudAta extends EventEmitter {
75
74
  deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
76
75
  deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
77
76
  }
78
- if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
77
+
78
+ const safeConfig = {
79
+ ...deviceData,
80
+ headers: 'removed',
81
+ };
82
+ if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(safeConfig, null, 2)}`);
79
83
 
80
84
  //device
81
85
  const serialNumber = deviceData.SerialNumber;
@@ -144,7 +148,7 @@ class MelCloudAta extends EventEmitter {
144
148
  method: 'POST',
145
149
  baseURL: ApiUrls.BaseURL,
146
150
  timeout: 10000,
147
- headers: this.headers,
151
+ headers: deviceData.headers,
148
152
  withCredentials: true
149
153
  });
150
154
 
@@ -183,7 +187,7 @@ class MelCloudAta extends EventEmitter {
183
187
  method: 'PUT',
184
188
  baseURL: ApiUrlsHome.BaseURL,
185
189
  timeout: 10000,
186
- headers: this.headers,
190
+ headers: deviceData.headers,
187
191
  withCredentials: true
188
192
  });
189
193
 
@@ -23,7 +23,6 @@ class MelCloudAtw extends EventEmitter {
23
23
 
24
24
  //set default values
25
25
  this.devicesData = {};
26
- this.headers = {};
27
26
 
28
27
  //lock flags
29
28
  this.locks = {
@@ -55,19 +54,21 @@ class MelCloudAtw extends EventEmitter {
55
54
  try {
56
55
  //read device info from file
57
56
  const devicesData = await this.functions.readData(this.devicesFile, true);
58
-
59
57
  if (!Array.isArray(devicesData)) {
60
58
  if (this.logWarn) this.emit('warn', `Device data not found`);
61
59
  return null;
62
60
  }
63
61
  const deviceData = devicesData.find(device => device.DeviceID === this.deviceId);
64
- this.headers = deviceData.Headers;
65
62
 
66
63
  if (this.accountType === 'melcloudhome') {
67
64
  deviceData.SerialNumber = deviceData.DeviceID || '4.0.0';
68
65
  deviceData.Device.FirmwareAppVersion = deviceData.ConnectedInterfaceIdentifier || '4.0.0';
69
66
  }
70
- if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
67
+ const safeConfig = {
68
+ ...deviceData,
69
+ headers: 'removed',
70
+ };
71
+ if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(safeConfig, null, 2)}`);
71
72
 
72
73
  //device
73
74
  const serialNumber = deviceData.SerialNumber;
@@ -152,7 +153,7 @@ class MelCloudAtw extends EventEmitter {
152
153
  method: 'POST',
153
154
  baseURL: ApiUrls.BaseURL,
154
155
  timeout: 10000,
155
- headers: this.headers,
156
+ headers: deviceData.headers,
156
157
  withCredentials: true
157
158
  });
158
159
 
@@ -190,7 +191,7 @@ class MelCloudAtw extends EventEmitter {
190
191
  method: 'PUT',
191
192
  baseURL: ApiUrlsHome.BaseURL,
192
193
  timeout: 10000,
193
- headers: this.headers,
194
+ headers: deviceData.headers,
194
195
  withCredentials: true
195
196
  });
196
197
 
@@ -23,7 +23,6 @@ class MelCloudErv extends EventEmitter {
23
23
 
24
24
  //set default values
25
25
  this.devicesData = {};
26
- this.headers = {};
27
26
 
28
27
  //lock flags
29
28
  this.locks = {
@@ -55,14 +54,12 @@ class MelCloudErv extends EventEmitter {
55
54
  try {
56
55
  //read device info from file
57
56
  const devicesData = await this.functions.readData(this.devicesFile, true);
58
-
59
57
  if (!Array.isArray(devicesData)) {
60
58
  if (this.logWarn) this.emit('warn', `Device data not found`);
61
59
  return null;
62
60
  }
63
61
  const deviceData = devicesData.find(device => device.DeviceID === this.deviceId);
64
- this.headers = deviceData.Headers;
65
-
62
+
66
63
  if (this.accountType === 'melcloudhome') {
67
64
  deviceData.SerialNumber = deviceData.DeviceID || '4.0.0';
68
65
  deviceData.Device.FirmwareAppVersion = deviceData.ConnectedInterfaceIdentifier || '4.0.0';
@@ -72,7 +69,11 @@ class MelCloudErv extends EventEmitter {
72
69
  deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
73
70
  deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
74
71
  }
75
- if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
72
+ const safeConfig = {
73
+ ...deviceData,
74
+ headers: 'removed',
75
+ };
76
+ if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(safeConfig, null, 2)}`);
76
77
 
77
78
  //presets
78
79
  const serialNumber = deviceData.SerialNumber;
@@ -141,7 +142,7 @@ class MelCloudErv extends EventEmitter {
141
142
  method: 'POST',
142
143
  baseURL: ApiUrls.BaseURL,
143
144
  timeout: 10000,
144
- headers: this.headers,
145
+ headers: deviceData.headers,
145
146
  withCredentials: true
146
147
  });
147
148
 
@@ -194,7 +195,7 @@ class MelCloudErv extends EventEmitter {
194
195
  method: 'PUT',
195
196
  baseURL: ApiUrlsHome.BaseURL,
196
197
  timeout: 10000,
197
- headers: this.headers,
198
+ headers: deviceData.headers,
198
199
  withCredentials: true
199
200
  });
200
201