homebridge-melcloud-control 4.4.1-beta.5 → 4.4.1-beta.50

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/src/functions.js CHANGED
@@ -56,162 +56,157 @@ class Functions extends EventEmitter {
56
56
 
57
57
  async ensureChromiumInstalled() {
58
58
  try {
59
- // Detect OS
60
- const { stdout: osOut } = await execPromise("uname -s");
59
+ const { stdout: osOut } = await execPromise('uname -s');
61
60
  const osName = osOut.trim();
62
- const { stdout: archOut } = await execPromise("uname -m");
63
- const arch = archOut.trim();
64
61
 
65
- const isARM = arch.startsWith("arm") || arch.startsWith("aarch64") || arch.startsWith("aarch");
66
- const isMac = osName === "Darwin";
67
- const isLinux = osName === "Linux";
62
+ const { stdout: archOut } = await execPromise('uname -m');
63
+ let arch = archOut.trim() || 'unknown';
64
+
65
+ // Normalizacja architektury
66
+ if (arch.startsWith('arm') || arch.startsWith('aarch')) arch = 'arm';
67
+ else if (arch.includes('64')) arch = 'x64';
68
+ else arch = 'x86';
69
+
70
+ const isARM = arch === 'arm';
71
+ const isMac = osName === 'Darwin';
72
+ const isLinux = osName === 'Linux';
73
+ const isQnap = fs.existsSync('/etc/config/uLinux.conf') || fs.existsSync('/etc/config/qpkg.conf');
68
74
 
69
75
  // Detect Docker
70
76
  let isDocker = false;
71
77
  try {
72
- await access("/.dockerenv");
78
+ await access('/.dockerenv');
73
79
  isDocker = true;
74
80
  } catch { }
81
+
75
82
  try {
76
- const { stdout } = await execPromise("cat /proc/1/cgroup || true");
77
- if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
83
+ const { stdout } = await execPromise('cat /proc/1/cgroup');
84
+ if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
78
85
  } catch { }
79
86
 
80
- // macOS
87
+ const result = { path: null, arch, system: 'unknown' };
88
+
89
+ /* ===================== macOS ===================== */
81
90
  if (isMac) {
82
- const macCandidates = [
83
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
84
- "/Applications/Chromium.app/Contents/MacOS/Chromium"
85
- ];
86
- for (const p of macCandidates) {
91
+ const macCandidates = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/Applications/Chromium.app/Contents/MacOS/Chromium'];
92
+ for (const path of macCandidates) {
87
93
  try {
88
- await access(p, fs.constants.X_OK);
89
- return p;
94
+ await access(path, fs.constants.X_OK);
95
+ result.path = path;
96
+ result.system = 'macOS';
97
+ return result;
90
98
  } catch { }
91
99
  }
92
- return null;
100
+ return result;
93
101
  }
94
102
 
95
- // ARM / Raspberry Pi
96
- if (isARM && isLinux) {
97
- const armCandidates = [
98
- "/usr/bin/chromium-browser",
99
- "/usr/bin/chromium",
100
- "/snap/bin/chromium"
101
- ];
102
-
103
- // Try existing
104
- for (const p of armCandidates) {
103
+ /* ===================== QNAP ===================== */
104
+ if (isQnap) {
105
+ const qnapCandidates = ['/opt/bin/chromium', '/opt/bin/chromium-browser'];
106
+ for (const path of qnapCandidates) {
105
107
  try {
106
- await access(p, fs.constants.X_OK);
107
- return p;
108
+ await access(path, fs.constants.X_OK);
109
+ result.path = path;
110
+ result.system = 'Qnap';
111
+ return result;
108
112
  } catch { }
109
113
  }
110
114
 
111
- // If not in Docker, try apt installation
112
- if (!isDocker) {
115
+ try {
116
+ await access('/opt/bin/opkg', fs.constants.X_OK);
117
+ await execPromise('/opt/bin/opkg update');
118
+ await execPromise('opkg install chromium nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib');
119
+ process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
120
+ } catch (error) {
121
+ if (this.logError) this.emit('error', `Install package for Qnap error: ${error}`);
122
+ }
123
+
124
+ for (const path of qnapCandidates) {
113
125
  try {
114
- await execPromise("sudo apt-get update -y");
126
+ await access(path, fs.constants.X_OK);
127
+ result.path = path;
128
+ result.system = 'Qnap';
129
+ return result;
115
130
  } catch { }
131
+ }
132
+ return result;
133
+ }
134
+
135
+ /* ===================== Linux ARM ===================== */
136
+ if (isLinux && isARM) {
137
+ const armCandidates = ['/usr/bin/chromium-browser', '/usr/bin/chromium', '/snap/bin/chromium'];
138
+ for (const path of armCandidates) {
116
139
  try {
117
- await execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true");
140
+ await access(path, fs.constants.X_OK);
141
+ result.path = path;
142
+ result.system = 'Linux';
143
+ return result;
118
144
  } catch { }
145
+ }
146
+
147
+ if (!isDocker) {
119
148
  try {
120
- await execPromise("sudo apt-get install -y chromium || true");
121
- } catch { }
149
+ await execPromise('sudo apt update -y');
150
+ await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2');
151
+ await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg');
152
+ } catch (error) {
153
+ if (this.logError) this.emit('error', `Install package for Linux ARM error: ${error}`);
154
+ }
122
155
  }
123
156
 
124
- // Retry after installation
125
- for (const p of armCandidates) {
157
+ for (const path of armCandidates) {
126
158
  try {
127
- await access(p, fs.constants.X_OK);
128
- return p;
159
+ await access(path, fs.constants.X_OK);
160
+ result.path = path;
161
+ result.system = 'Linux';
162
+ return result;
129
163
  } catch { }
130
164
  }
131
-
132
- return null;
133
- }
134
-
135
- // QNAP / Entware
136
- let entwareExists = false;
137
- try {
138
- await access("/opt/bin/opkg", fs.constants.X_OK);
139
- entwareExists = true;
140
- } catch { }
141
-
142
- if (entwareExists) {
143
- try {
144
- await execPromise("/opt/bin/opkg update");
145
- await execPromise("/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib");
146
- process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ""}`;
147
- } catch { }
165
+ return result;
148
166
  }
149
167
 
150
- // Synology DSM 7
151
- const synoCandidates = [
152
- "/var/packages/Chromium/target/usr/bin/chromium",
153
- "/usr/local/chromium/bin/chromium"
154
- ];
155
- for (const p of synoCandidates) {
156
- try {
157
- await access(p, fs.constants.X_OK);
158
- return p;
159
- } catch { }
160
- }
161
-
162
- // Linux x64
168
+ /* ===================== Linux x64 ===================== */
163
169
  if (isLinux) {
164
- const linuxCandidates = [
165
- "/usr/bin/chromium",
166
- "/usr/bin/chromium-browser",
167
- "/usr/bin/google-chrome",
168
- "/snap/bin/chromium",
169
- "/usr/local/bin/chromium"
170
- ];
171
-
170
+ const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
172
171
  try {
173
- const { stdout } = await execPromise("which chromium || which chromium-browser || which google-chrome || true");
174
- const found = stdout.trim();
175
- if (found) return found;
172
+ const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome');
173
+ if (stdout.trim()) {
174
+ result.path = stdout.trim();
175
+ return result;
176
+ }
176
177
  } catch { }
177
178
 
178
- for (const p of linuxCandidates) {
179
+ for (const path of linuxCandidates) {
179
180
  try {
180
- await access(p, fs.constants.X_OK);
181
- return p;
181
+ await access(path, fs.constants.X_OK);
182
+ result.path = path;
183
+ result.system = 'Linux';
184
+ return result;
182
185
  } catch { }
183
186
  }
184
187
 
185
- // Docker: try installing chromium inside container (if allowed)
186
188
  if (isDocker) {
187
189
  try {
188
- await execPromise("apt-get update -y && apt-get install -y chromium || true");
189
- } catch { }
190
- try {
191
- await access("/usr/bin/chromium", fs.constants.X_OK);
192
- return "/usr/bin/chromium";
193
- } catch { }
194
- }
195
-
196
- // Install missing libraries
197
- const depCommands = [
198
- "apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2 || true",
199
- "yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib || true",
200
- "apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib || true"
201
- ];
202
- for (const cmd of depCommands) {
203
- try {
204
- await execPromise(`sudo ${cmd}`);
205
- } catch { }
190
+ await execPromise('apt update -y && apt install -y chromium');
191
+ } catch (error) {
192
+ if (this.logError) this.emit('error', `Install package for Linux Docker error: ${error}`);
193
+ }
194
+
195
+ for (const path of linuxCandidates) {
196
+ try {
197
+ await access(path, fs.constants.X_OK);
198
+ result.path = path;
199
+ result.system = 'Linux Docker';
200
+ return result;
201
+ } catch { }
202
+ }
206
203
  }
207
-
208
- return null;
209
204
  }
210
205
 
211
- return null;
212
- } catch (err) {
213
- if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
214
- return null;
206
+ return result;
207
+ } catch (error) {
208
+ if (this.logError) this.emit('error', `Chromium detection error: ${error.message}`);
209
+ return { path: null, arch: 'unknown', system: 'unknown' };
215
210
  }
216
211
  }
217
212
 
@@ -280,7 +275,6 @@ class Functions extends EventEmitter {
280
275
 
281
276
  return { min, max };
282
277
  }
283
-
284
278
  }
285
279
 
286
280
  export default Functions
package/src/melcloud.js CHANGED
@@ -54,9 +54,9 @@ class MelCloud extends EventEmitter {
54
54
 
55
55
  async checkDevicesList() {
56
56
  try {
57
- const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
57
+ const devicesList = { State: false, Info: null, Buildings: [], Devices: [], Scenes: [] }
58
58
  if (this.logDebug) this.emit('debug', `Scanning for devices...`);
59
- const listDevicesData = await this.client(ApiUrls.ListDevices, { method: 'GET', });
59
+ const listDevicesData = await this.client(ApiUrls.Get.ListDevices, { method: 'GET', });
60
60
 
61
61
  if (!listDevicesData || !listDevicesData.data) {
62
62
  devicesList.Info = 'Invalid or empty response from MELCloud API'
@@ -71,9 +71,6 @@ class MelCloud extends EventEmitter {
71
71
  return devicesList;
72
72
  }
73
73
 
74
- await this.functions.saveData(this.buildingsFile, buildingsList);
75
- if (this.logDebug) this.emit('debug', `Buildings list saved`);
76
-
77
74
  const devices = [];
78
75
  for (const building of buildingsList) {
79
76
  if (!building.Structure) {
@@ -108,7 +105,12 @@ class MelCloud extends EventEmitter {
108
105
 
109
106
  devicesList.State = true;
110
107
  devicesList.Info = `Found ${devicesCount} devices`;
108
+ devicesList.Buildings = buildingsList;
111
109
  devicesList.Devices = devices;
110
+
111
+ await this.functions.saveData(this.buildingsFile, devicesList);
112
+ if (this.logDebug) this.emit('debug', `Buildings list saved`);
113
+
112
114
  this.emit('devicesList', devicesList);
113
115
 
114
116
  return devicesList;
@@ -132,9 +134,9 @@ class MelCloud extends EventEmitter {
132
134
  CaptchaResponse: '',
133
135
  Persist: true
134
136
  };
135
- const accountData = await axios(ApiUrls.ClientLogin, {
137
+ const accountData = await axios(ApiUrls.Post.ClientLogin, {
136
138
  method: 'POST',
137
- baseURL: ApiUrls.BaseURL,
139
+ baseURL: ApiUrls.Base,
138
140
  timeout: 15000,
139
141
  data: payload
140
142
  });
@@ -164,7 +166,7 @@ class MelCloud extends EventEmitter {
164
166
  };
165
167
 
166
168
  this.client = axios.create({
167
- baseURL: ApiUrls.BaseURL,
169
+ baseURL: ApiUrls.Base,
168
170
  timeout: 30000,
169
171
  headers: headers
170
172
  });
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, AirConditioner } from './constants.js';
3
+ import { ApiUrls, AirConditioner } from './constants.js';
4
4
 
5
5
  class MelCloudAta extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -72,7 +72,34 @@ class MelCloudAta extends EventEmitter {
72
72
  }
73
73
  }
74
74
 
75
- if (this.logDebug) this.emit('debug', `WS update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
75
+ updateState = true;
76
+ break;
77
+ case 'ataUnitFrostProtectionTriggered':
78
+ deviceData.FrostProtection.Active = messageData.active;
79
+
80
+ //update device settings
81
+ for (const [key, value] of Object.entries(settings)) {
82
+ if (!this.functions.isValidValue(value)) continue;
83
+
84
+ if (key in deviceData.Device) {
85
+ deviceData.Device[key] = value;
86
+ }
87
+ }
88
+
89
+ updateState = true;
90
+ break;
91
+ case 'ataUnitOverheatProtectionTriggered':
92
+ deviceData.OverheatProtection.Active = messageData.active;
93
+
94
+ //update device settings
95
+ for (const [key, value] of Object.entries(settings)) {
96
+ if (!this.functions.isValidValue(value)) continue;
97
+
98
+ if (key in deviceData.Device) {
99
+ deviceData.Device[key] = value;
100
+ }
101
+ }
102
+
76
103
  updateState = true;
77
104
  break;
78
105
  case 'unitHolidayModeTriggered':
@@ -85,6 +112,10 @@ class MelCloudAta extends EventEmitter {
85
112
  deviceData.Rssi = messageData.rssi;
86
113
  updateState = true;
87
114
  break;
115
+ case 'unitCommunicationRestored':
116
+ timestamp = messageData.timestamp;
117
+ deviceData.Device.IsConnected = true;
118
+ break;
88
119
  default:
89
120
  if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
90
121
  return;
@@ -204,7 +235,7 @@ class MelCloudAta extends EventEmitter {
204
235
  case 'account':
205
236
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
206
237
  payload = { data: flagData.LoginData };
207
- path = ApiUrls.UpdateApplicationOptions;
238
+ path = ApiUrls.Post.UpdateApplicationOptions;
208
239
  await this.functions.saveData(this.accountFile, flagData);
209
240
  break;
210
241
  default:
@@ -230,7 +261,7 @@ class MelCloudAta extends EventEmitter {
230
261
  HideDryModeControl: deviceData.HideDryModeControl,
231
262
  HasPendingCommand: true
232
263
  };
233
- path = ApiUrls.SetAta;
264
+ path = ApiUrls.Post.Ata;
234
265
  update = true;
235
266
  break;
236
267
  }
@@ -254,7 +285,7 @@ class MelCloudAta extends EventEmitter {
254
285
  units: { ATA: [deviceData.DeviceID] }
255
286
  };
256
287
  method = 'POST';
257
- path = ApiUrlsHome.PostProtectionFrost;
288
+ path = ApiUrls.Home.Post.ProtectionFrost;
258
289
  update = true;
259
290
  break;
260
291
  case 'overheatprotection':
@@ -265,7 +296,7 @@ class MelCloudAta extends EventEmitter {
265
296
  units: { ATA: [deviceData.DeviceID] }
266
297
  };
267
298
  method = 'POST';
268
- path = ApiUrlsHome.PostProtectionOverheat;
299
+ path = ApiUrls.Home.Post.ProtectionOverheat;
269
300
  update = true;
270
301
  break;
271
302
  case 'holidaymode':
@@ -276,17 +307,17 @@ class MelCloudAta extends EventEmitter {
276
307
  units: { ATA: [deviceData.DeviceID] }
277
308
  };
278
309
  method = 'POST';
279
- path = ApiUrlsHome.PostHolidayMode;
310
+ path = ApiUrls.Home.Post.HolidayMode;
280
311
  break;
281
312
  case 'schedule':
282
313
  payload = { enabled: deviceData.ScheduleEnabled };
283
314
  method = 'PUT';
284
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
315
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
285
316
  update = true;
286
317
  break;
287
318
  case 'scene':
288
319
  method = 'PUT';
289
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
320
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
290
321
  break;
291
322
  default:
292
323
  if (displayType === 1 && deviceData.Device.OperationMode === 8) {
@@ -312,7 +343,7 @@ class MelCloudAta extends EventEmitter {
312
343
  inStandbyMode: null
313
344
  };
314
345
  method = 'PUT';
315
- path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
346
+ path = ApiUrls.Home.Put.Ata.replace('deviceid', deviceData.DeviceID);
316
347
  break;
317
348
  }
318
349
 
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
3
+ import { ApiUrls, HeatPump } from './constants.js';
4
4
 
5
5
  class MelCloudAtw extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -71,7 +71,19 @@ class MelCloudAtw extends EventEmitter {
71
71
  }
72
72
  }
73
73
 
74
- if (this.logDebug) this.emit('debug', `WS update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
74
+ updateState = true;
75
+ break;
76
+ case 'atwUnitFrostProtectionTriggered':
77
+ deviceData.FrostProtection.Active = messageData.active;
78
+
79
+ //update device settings
80
+ for (const [key, value] of Object.entries(settings)) {
81
+ if (!this.functions.isValidValue(value)) continue;
82
+
83
+ if (key in deviceData.Device) {
84
+ deviceData.Device[key] = value;
85
+ }
86
+ }
75
87
  updateState = true;
76
88
  break;
77
89
  case 'unitHolidayModeTriggered':
@@ -84,6 +96,10 @@ class MelCloudAtw extends EventEmitter {
84
96
  deviceData.Rssi = messageData.rssi;
85
97
  updateState = true;
86
98
  break;
99
+ case 'unitCommunicationRestored':
100
+ timestamp = messageData.timestamp;
101
+ deviceData.Device.IsConnected = true;
102
+ break;
87
103
  default:
88
104
  if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
89
105
  return;
@@ -204,7 +220,7 @@ class MelCloudAtw extends EventEmitter {
204
220
  case 'account':
205
221
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
206
222
  payload = { data: flagData.LoginData };
207
- path = ApiUrls.UpdateApplicationOptions;
223
+ path = ApiUrls.Post.UpdateApplicationOptions;
208
224
  await this.functions.saveData(this.accountFile, flagData);
209
225
  break;
210
226
  default:
@@ -231,7 +247,7 @@ class MelCloudAtw extends EventEmitter {
231
247
  ProhibitHotWater: deviceData.Device.ProhibitHotWater,
232
248
  HasPendingCommand: true
233
249
  }
234
- path = ApiUrls.SetAtw;
250
+ path = ApiUrls.Post.Atw;
235
251
  update = true;
236
252
  break;
237
253
  }
@@ -255,7 +271,7 @@ class MelCloudAtw extends EventEmitter {
255
271
  units: { ATA: [deviceData.DeviceID] }
256
272
  };
257
273
  method = 'POST';
258
- path = ApiUrlsHome.PostProtectionFrost;
274
+ path = ApiUrls.Home.Post.ProtectionFrost;
259
275
  update = true;
260
276
  break;
261
277
  case 'holidaymode':
@@ -266,17 +282,17 @@ class MelCloudAtw extends EventEmitter {
266
282
  units: { ATW: [deviceData.DeviceID] }
267
283
  };
268
284
  method = 'POST';
269
- path = ApiUrlsHome.PostHolidayMode;
285
+ path = ApiUrls.Home.Post.HolidayMode;
270
286
  break;
271
287
  case 'schedule':
272
288
  payload = { enabled: deviceData.ScheduleEnabled };
273
289
  method = 'PUT';
274
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
290
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
275
291
  update = true;
276
292
  break;
277
293
  case 'scene':
278
294
  method = 'PUT';
279
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
295
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
280
296
  break;
281
297
  default:
282
298
  payload = {
@@ -295,7 +311,7 @@ class MelCloudAtw extends EventEmitter {
295
311
  ecoHotWater: deviceData.Device.EcoHotWater,
296
312
  };
297
313
  method = 'PUT';
298
- path = ApiUrlsHome.PutAtw.replace('deviceid', deviceData.DeviceID);
314
+ path = ApiUrls.Home.Put.Atw.replace('deviceid', deviceData.DeviceID);
299
315
  break
300
316
  }
301
317
 
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import Functions from './functions.js';
3
- import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
3
+ import { ApiUrls, Ventilation } from './constants.js';
4
4
 
5
5
  class MelCloudErv extends EventEmitter {
6
6
  constructor(account, device, defaultTempsFile, accountFile, melcloud) {
@@ -71,7 +71,19 @@ class MelCloudErv extends EventEmitter {
71
71
  }
72
72
  }
73
73
 
74
- if (this.logDebug) this.emit('debug', `WS update settings: ${JSON.stringify(deviceData.Device, null, 2)}`);
74
+ updateState = true;
75
+ break;
76
+ case 'ervUnitFrostProtectionTriggered':
77
+ deviceData.FrostProtection.Active = messageData.active;
78
+
79
+ //update device settings
80
+ for (const [key, value] of Object.entries(settings)) {
81
+ if (!this.functions.isValidValue(value)) continue;
82
+
83
+ if (key in deviceData.Device) {
84
+ deviceData.Device[key] = value;
85
+ }
86
+ }
75
87
  updateState = true;
76
88
  break;
77
89
  case 'unitHolidayModeTriggered':
@@ -84,6 +96,10 @@ class MelCloudErv extends EventEmitter {
84
96
  deviceData.Rssi = messageData.rssi;
85
97
  updateState = true;
86
98
  break;
99
+ case 'unitCommunicationRestored':
100
+ timestamp = messageData.timestamp;
101
+ deviceData.Device.IsConnected = true;
102
+ break;
87
103
  default:
88
104
  if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
89
105
  return;
@@ -189,7 +205,7 @@ class MelCloudErv extends EventEmitter {
189
205
  case 'account':
190
206
  flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
191
207
  payload = { data: flagData.LoginData };
192
- path = ApiUrls.UpdateApplicationOptions;
208
+ path = ApiUrls.Post.UpdateApplicationOptions;
193
209
  await this.functions.saveData(this.accountFile, flagData);
194
210
  break;
195
211
  default:
@@ -231,7 +247,7 @@ class MelCloudErv extends EventEmitter {
231
247
  NightPurgeMode: deviceData.Device.NightPurgeMode,
232
248
  HasPendingCommand: true
233
249
  }
234
- path = ApiUrls.SetErv;
250
+ path = ApiUrls.Post.Erv;
235
251
  update = true;
236
252
  break;
237
253
  }
@@ -255,17 +271,17 @@ class MelCloudErv extends EventEmitter {
255
271
  units: { ERV: [deviceData.DeviceID] }
256
272
  };
257
273
  method = 'POST';
258
- path = ApiUrlsHome.PostHolidayMode;
274
+ path = ApiUrls.Home.Post.HolidayMode;
259
275
  break;
260
276
  case 'schedule':
261
277
  payload = { enabled: deviceData.ScheduleEnabled };
262
278
  method = 'PUT';
263
- path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
279
+ path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
264
280
  update = true;
265
281
  break;
266
282
  case 'scene':
267
283
  method = 'PUT';
268
- path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
284
+ path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
269
285
  break;
270
286
  default:
271
287
  if (displayType === 1 && deviceData.Device.VentilationMode === 2) {
@@ -288,7 +304,7 @@ class MelCloudErv extends EventEmitter {
288
304
  ventilationMode: Ventilation.VentilationModeMapEnumToString[deviceData.Device.VentilationMode],
289
305
  };
290
306
  method = 'PUT';
291
- path = ApiUrlsHome.PutErv.replace('deviceid', deviceData.DeviceID);
307
+ path = ApiUrls.Home.Put.Erv.replace('deviceid', deviceData.DeviceID);
292
308
  break
293
309
  }
294
310