homebridge-melcloud-control 4.1.2-beta.9 → 4.1.2

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.
@@ -105,7 +105,6 @@
105
105
  return;
106
106
  }
107
107
 
108
- this.accountIndex = 0;
109
108
  const accounts = pluginConfig[0].accounts || [];
110
109
  const accountsCount = accounts.length;
111
110
 
@@ -117,17 +116,22 @@
117
116
  container.style.alignItems = 'center';
118
117
 
119
118
  const formElements = {
119
+ accountName: document.getElementById('accountName'),
120
+ info: document.getElementById('info'),
121
+ info1: document.getElementById('info1'),
122
+ info2: document.getElementById('info2'),
120
123
  name: document.getElementById('name'),
121
124
  user: document.getElementById('user'),
122
125
  passwd: document.getElementById('passwd'),
123
126
  language: document.getElementById('language'),
124
127
  accountType: document.getElementById('accountType'),
125
- logIn: document.getElementById('logIn'),
126
- accountName: document.getElementById('accountName')
128
+ logIn: document.getElementById('logIn')
127
129
  };
128
130
 
129
131
  // Tworzenie przycisków
130
132
  accounts.forEach((account, i) => {
133
+ this.account = account;
134
+
131
135
  const button = document.createElement("button");
132
136
  button.type = "button";
133
137
  button.id = `button${i}`;
@@ -137,7 +141,7 @@
137
141
  container.appendChild(button);
138
142
 
139
143
  button.addEventListener('click', async () => {
140
- this.accountIndex = i;
144
+ this.account = account;
141
145
 
142
146
  // Zmieniamy klasę wszystkich przycisków
143
147
  accounts.forEach((_, j) => {
@@ -146,6 +150,9 @@
146
150
 
147
151
  // Ustawiamy formularz
148
152
  formElements.accountName.innerText = account.name || '';
153
+ formElements.info.innerText = ``;
154
+ formElements.info1.innerText = '';
155
+ formElements.info2.innerText = '';
149
156
  formElements.name.value = account.name || '';
150
157
  formElements.user.value = account.user || '';
151
158
  formElements.passwd.value = account.passwd || '';
@@ -160,7 +167,7 @@
160
167
 
161
168
  // Jeden listener input dla całego formularza
162
169
  document.getElementById('configForm').addEventListener('input', async () => {
163
- const account = accounts[this.accountIndex];
170
+ const account = this.account;
164
171
  if (!account) return;
165
172
 
166
173
  account.name = formElements.name.value;
@@ -224,10 +231,13 @@
224
231
 
225
232
  document.getElementById('logIn').addEventListener('click', async () => {
226
233
  document.getElementById(`logIn`).className = "btn btn-primary";
234
+ updateInfo('info', '', 'white');
235
+ updateInfo('info1', '', 'white');
236
+ updateInfo('info2', '', 'white');
227
237
  homebridge.showSpinner();
228
238
 
229
239
  try {
230
- const account = pluginConfig[0].accounts[this.accountIndex];
240
+ const account = this.account;
231
241
  const response = await homebridge.request('/connect', account);
232
242
  if (!response.State) {
233
243
  homebridge.hideSpinner();
@@ -254,45 +264,81 @@
254
264
  const removedErv = removeStaleDevices(account.ervDevices, devicesByType.erv);
255
265
 
256
266
  const handleDevices = (devicesInMelCloud, devicesInConfig, typeString, newArr, newPresets) => {
257
- devicesInMelCloud.forEach(device => {
258
- const deviceObj = {
259
- id: device.DeviceID,
260
- type: device.Type,
261
- typeString,
262
- displayType: 0,
263
- name: device.DeviceName,
264
- presets: device.Presets ?? [],
265
- buttonsSensors: []
266
- };
267
-
268
- if (!devicesInConfig.some(device => String(device.id) === deviceObj.id)) {
269
- devicesInConfig.push(deviceObj);
270
- newArr.push(deviceObj);
271
- }
272
-
273
- const presetsKey = account.type === 'melcloud' ? 'Presets' : 'Schedule';
274
- const presetsIdKey = account.type === 'melcloud' ? 'ID' : 'Id';
275
- const presets = device[presetsKey] || [];
276
- presets.forEach((preset, index) => {
277
- const presetObj = {
278
- id: preset[presetsIdKey],
279
- displayType: 0,
280
- name: preset.NumberDescription || `Preset ${index}`,
281
- namePrefix: false
282
- };
283
-
284
- const deviceInConfig = devicesInConfig.find(device => String(device.id) === deviceObj.id);
285
- if (deviceInConfig && !deviceInConfig.presets.some(preset => String(preset.id) === presetObj.id)) {
286
- deviceInConfig.presets.push(presetObj);
287
- newPresets.push(presetObj);
267
+ try {
268
+ const configDevicesMap = new Map(devicesInConfig.map(dev => [String(dev.id), dev]));
269
+ const isMelcloud = account.type === 'melcloud';
270
+
271
+ const idKey = isMelcloud ? 'ID' : 'Id';
272
+ const typeKey = isMelcloud ? 'Presets' : 'Schedule';
273
+ const typeKey1 = isMelcloud ? 'presets' : 'schedules';
274
+
275
+ devicesInMelCloud.forEach(device => {
276
+ const deviceId = String(device.DeviceID);
277
+ let deviceInConfig = configDevicesMap.get(deviceId);
278
+
279
+ // === Create device if missing ===
280
+ if (!deviceInConfig) {
281
+ deviceInConfig = {
282
+ id: device.DeviceID,
283
+ type: device.Type,
284
+ typeString,
285
+ displayType: 0,
286
+ name: device.DeviceName,
287
+ presets: [],
288
+ schedules: [],
289
+ buttonsSensors: []
290
+ };
291
+ devicesInConfig.push(deviceInConfig);
292
+ newArr.push(deviceInConfig);
293
+ configDevicesMap.set(deviceId, deviceInConfig);
294
+ }
295
+
296
+ // === Process presets/schedules ===
297
+ const presets = device[typeKey] || [];
298
+ const presetsInConfig = deviceInConfig[typeKey1] || [];
299
+ const presetIds = new Set(presetsInConfig.map(p => String(p.id)));
300
+
301
+ let addedNewPreset = false;
302
+
303
+ presets.forEach((preset, index) => {
304
+ const presetId = String(preset[idKey]);
305
+ if (!presetIds.has(presetId)) {
306
+ const presetObj = {
307
+ id: presetId,
308
+ displayType: 0,
309
+ name: preset.NumberDescription || `Schedule ${index}`,
310
+ namePrefix: false
311
+ };
312
+ presetsInConfig.push(presetObj);
313
+ newPresets.push(presetObj);
314
+ presetIds.add(presetId);
315
+ addedNewPreset = true;
316
+ }
317
+ });
318
+
319
+ // === Remove placeholder presets/schedules (id === '0') if new ones were added ===
320
+ if (addedNewPreset) {
321
+ const beforeCount = presetsInConfig.length;
322
+ deviceInConfig[typeKey1] = presetsInConfig.filter(p => String(p.id) !== '0');
323
+ const removedCount = beforeCount - deviceInConfig[typeKey1].length;
324
+
325
+ if (removedCount > 0 && removedCount < beforeCount) {
326
+ updateInfo('info2', `Removed ${removedCount} placeholder ${typeKey1} from device ${device.DeviceID}`, 'yellow');
327
+ }
288
328
  }
289
329
  });
290
- });
330
+
331
+ // Return filtered devicesInConfig to make sure upstream code uses it
332
+ const filteredDevices = devicesInConfig.filter(d => String(d.id) !== '0');
333
+ return filteredDevices;
334
+ } catch (error) {
335
+ updateInfo('info', `Error while processing device: ${JSON.stringify(error)}`, 'red');
336
+ }
291
337
  };
292
338
 
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);
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);
296
342
 
297
343
  const newDevicesCount = newDevices.ata.length + newDevices.atw.length + newDevices.erv.length;
298
344
  const newPresetsCount = newDevices.ataPresets.length + newDevices.atwPresets.length + newDevices.ervPresets.length;
@@ -304,20 +350,18 @@
304
350
  if (newDevicesCount)
305
351
  updateInfo('info', `Found new devices: ATA: ${newDevices.ata.length}, ATW: ${newDevices.atw.length}, ERV: ${newDevices.erv.length}.`, 'green');
306
352
  if (newPresetsCount)
307
- updateInfo('info1', `Found new presets: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
353
+ updateInfo('info1', `Found new ${account.type === 'melcloud' ? 'presets' : 'schedules'}: ATA: ${newDevices.ataPresets.length}, ATW: ${newDevices.atwPresets.length}, ERV: ${newDevices.ervPresets.length}.`, 'green');
308
354
  if (removedDevicesCount)
309
355
  updateInfo('info2', `Removed devices: ATA: ${removedAta.length}, ATW: ${removedAtw.length}, ERV: ${removedErv.length}.`, 'orange');
310
356
  }
311
357
 
312
358
  await homebridge.updatePluginConfig(pluginConfig);
313
359
  await homebridge.savePluginConfig(pluginConfig);
314
- document.getElementById('logIn').className = "btn btn-secondary";
315
-
316
360
  } catch (error) {
317
- updateInfo('info', 'Connect error.', 'yellow');
318
- updateInfo('info1', `${JSON.stringify(error)}`, 'red');
361
+ updateInfo('info', `Connect error ${JSON.stringify(error)}`, 'red');
319
362
  document.getElementById('logIn').className = "btn btn-secondary";
320
363
  } finally {
364
+ document.getElementById('logIn').className = "btn btn-secondary";
321
365
  homebridge.hideSpinner();
322
366
  }
323
367
  });
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.9",
4
+ "version": "4.1.2",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -39,7 +39,7 @@
39
39
  "async-mqtt": "^2.6.3",
40
40
  "axios": "^1.13.2",
41
41
  "express": "^5.1.0",
42
- "puppeteer-core": "^24.29.1"
42
+ "puppeteer": "^24.29.1"
43
43
  },
44
44
  "keywords": [
45
45
  "homebridge",
package/src/constants.js CHANGED
@@ -21,8 +21,12 @@ export const ApiUrlsHome = {
21
21
  BaseURL: "https://melcloudhome.com",
22
22
  GetUserContext: "/api/user/context",
23
23
  SetAta: "/api/ataunit/deviceid",
24
+ GetAtaSchedule: "/ata/deviceid/schedule",
24
25
  SetAtw: "/api/atwunit/deviceid",
26
+ GetAtwSchedule: "/atw/deviceid/schedule",
25
27
  SetErv: "/api/ervunit/deviceid",
28
+ GetErvSchedule: "/erv/deviceid/schedule",
29
+ SetSchedule: "/api/cloudschedule/deviceid/enabled",
26
30
  };
27
31
 
28
32
  export const DeviceType = [
package/src/deviceata.js CHANGED
@@ -37,7 +37,8 @@ class DeviceAta extends EventEmitter {
37
37
  this.heatDryFanMode = device.heatDryFanMode || 1; //NONE, HEAT, DRY, FAN
38
38
  this.coolDryFanMode = device.coolDryFanMode || 1; //NONE, COOL, DRY, FAN
39
39
  this.autoDryFanMode = device.autoDryFanMode || 1; //NONE, AUTO, DRY, FAN
40
- this.presets = (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0);
40
+ this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
41
+ this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
41
42
  this.buttons = (device.buttonsSensors || []).filter(sensor => (sensor.displayType ?? 0) > 0);
42
43
  this.deviceId = device.id;
43
44
  this.deviceName = device.name;
@@ -61,6 +62,14 @@ class DeviceAta extends EventEmitter {
61
62
  preset.previousSettings = {};
62
63
  }
63
64
 
65
+ //schedules configured
66
+ for (const schedule of this.schedules) {
67
+ schedule.name = schedule.name || 'Schedule'
68
+ schedule.serviceType = [null, Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][schedule.displayType];
69
+ schedule.characteristicType = [null, Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][schedule.displayType];
70
+ schedule.state = false;
71
+ }
72
+
64
73
  //buttons configured
65
74
  for (const button of this.buttons) {
66
75
  button.name = button.name || 'Button'
@@ -244,6 +253,7 @@ class DeviceAta extends EventEmitter {
244
253
  const deviceName = this.deviceName;
245
254
  const accountName = this.accountName;
246
255
  const presetsOnServer = this.accessory.presets;
256
+ const schedulesOnServer = this.accessory.schedules;
247
257
  const supportsHeat = this.accessory.supportsHeat;
248
258
  const supportsDry = this.accessory.supportsDry;
249
259
  const supportsCool = this.accessory.supportsCool;
@@ -683,9 +693,8 @@ class DeviceAta extends EventEmitter {
683
693
  if (this.presets.length > 0) {
684
694
  if (this.logDebug) this.emit('debug', `Prepare presets services`);
685
695
  this.presetsServices = [];
686
- const presetsIdKey = this.accountType === 'melcloud' ? 'ID' : 'Id';
687
696
  this.presets.forEach((preset, i) => {
688
- const presetData = presetsOnServer.find(p => p[presetsIdKey] === preset.id);
697
+ const presetData = presetsOnServer.find(p => p.ID === preset.id);
689
698
 
690
699
  //get preset name
691
700
  const name = preset.name;
@@ -706,8 +715,6 @@ class DeviceAta extends EventEmitter {
706
715
  })
707
716
  .onSet(async (state) => {
708
717
  try {
709
- const setTempKey = this.accountType === 'melcloud' ? 'SetTemperature' : 'SetPoint';
710
- const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
711
718
  switch (state) {
712
719
  case true:
713
720
  preset.previousSettings = deviceData.Device;
@@ -716,7 +723,7 @@ class DeviceAta extends EventEmitter {
716
723
  deviceData.Device.SetTemperature = presetData.SetTemperature;
717
724
  deviceData.Device.VaneHorizontalDirection = presetData.VaneHorizontalDirection;
718
725
  deviceData.Device.VaneVerticalDirection = presetData.VaneVerticalDirection;
719
- deviceData.Device[fanKey] = presetData[fanKey];
726
+ deviceData.Device.SetFanSpeed = presetData.SetFanSpeed;
720
727
  break;
721
728
  case false:
722
729
  deviceData.Device.Power = preset.previousSettings.Power;
@@ -724,7 +731,7 @@ class DeviceAta extends EventEmitter {
724
731
  deviceData.Device.SetTemperature = preset.previousSettings.SetTemperature;
725
732
  deviceData.Device.VaneHorizontalDirection = preset.previousSettings.VaneHorizontalDirection;
726
733
  deviceData.Device.VaneVerticalDirection = preset.previousSettings.VaneVerticalDirection;
727
- deviceData.Device[fanKey] = preset.previousSettings[fanKey];
734
+ deviceData.Device.SetFanSpeed = preset.previousSettings.SetFanSpeed;
728
735
  break;
729
736
  };
730
737
 
@@ -739,6 +746,42 @@ class DeviceAta extends EventEmitter {
739
746
  });
740
747
  };
741
748
 
749
+ //schedules services
750
+ if (this.schedules.length > 0) {
751
+ if (this.logDebug) this.emit('debug', `Prepare schedules services`);
752
+ this.schedulesServices = [];
753
+ this.schedules.forEach((schedule, i) => {
754
+ //get preset name
755
+ const name = schedule.name;
756
+
757
+ //get preset name prefix
758
+ const namePrefix = schedule.namePrefix;
759
+
760
+ const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
761
+ const serviceType = schedule.serviceType;
762
+ const characteristicType = schedule.characteristicType;
763
+ const scheduleService = new serviceType(serviceName, `Schedule ${deviceId} ${i}`);
764
+ scheduleService.addOptionalCharacteristic(Characteristic.ConfiguredName);
765
+ scheduleService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
766
+ scheduleService.getCharacteristic(characteristicType)
767
+ .onGet(async () => {
768
+ const state = schedule.state;
769
+ return state;
770
+ })
771
+ .onSet(async (state) => {
772
+ try {
773
+ deviceData.ScheduleEnabled = state;
774
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'scheduleset');
775
+ if (this.logInfo) this.emit('info', `${state ? 'Set:' : 'Unset:'} ${name}`);
776
+ } catch (error) {
777
+ if (this.logWarn) this.emit('warn', `Set schedule error: ${error}`);
778
+ };
779
+ });
780
+ this.schedulesServices.push(scheduleService);
781
+ accessory.addService(scheduleService);
782
+ });
783
+ };
784
+
742
785
  //buttons services
743
786
  if (this.buttons.length > 0) {
744
787
  if (this.logDebug) this.emit('debug', `Prepare buttons/sensors services`);
@@ -1017,7 +1060,6 @@ class DeviceAta extends EventEmitter {
1017
1060
  this.deviceData = deviceData;
1018
1061
 
1019
1062
  //keys
1020
- const presetsKey = this.accountType === 'melcloud' ? 'Presets' : 'Schedule';
1021
1063
  const presetsIdKey = this.accountType === 'melcloud' ? 'ID' : 'Id';
1022
1064
  const setTempKey = this.accountType === 'melcloud' ? 'SetTemperature' : 'SetPoint';
1023
1065
  const fanKey = this.accountType === 'melcloud' ? 'FanSpeed' : 'SetFanSpeed';
@@ -1032,7 +1074,10 @@ class DeviceAta extends EventEmitter {
1032
1074
  const supportCoolKey = this.accountType === 'melcloud' ? 'ModelSupportsCool' : 'HasCoolOperationMode';
1033
1075
 
1034
1076
  //presets schedule
1035
- const presetsOnServer = deviceData[presetsKey] ?? [];
1077
+ const scheduleEnabled = deviceData.ScheduleEnabled;
1078
+ const schedulesOnServer = deviceData.Schedule ?? [];
1079
+ const presetsOnServer = deviceData.Presets ?? [];
1080
+
1036
1081
 
1037
1082
  //protection
1038
1083
  const frostProtectionEnabled = deviceData.FrostProtection?.Enabled;
@@ -1041,7 +1086,6 @@ class DeviceAta extends EventEmitter {
1041
1086
  const overheatProtectionActive = deviceData.OverheatProtection?.Active;
1042
1087
  const holidayModeEnabled = deviceData.HolidayMode?.Enabled;
1043
1088
  const holidayModeActive = deviceData.HolidayMode?.Active;
1044
- const scheduleEnabled = deviceData.ScheduleEnabled;
1045
1089
 
1046
1090
  //device control
1047
1091
  const hideVaneControls = deviceData.HideVaneControls ?? false;
@@ -1093,6 +1137,7 @@ class DeviceAta extends EventEmitter {
1093
1137
  //accessory
1094
1138
  const obj = {
1095
1139
  presets: presetsOnServer,
1140
+ schedules: schedulesOnServer,
1096
1141
  supportsAutomaticFanSpeed: supportsAutomaticFanSpeed,
1097
1142
  supportsAirDirectionFunction: supportsAirDirectionFunction,
1098
1143
  supportsSwingFunction: supportsSwingFunction,
@@ -1288,17 +1333,28 @@ class DeviceAta extends EventEmitter {
1288
1333
  const presetData = presetsOnServer.find(p => p[presetsIdKey] === preset.id);
1289
1334
 
1290
1335
  preset.state = presetData ? (presetData.Power === power
1291
- && presetData[setTempKey] === setTemperature
1336
+ && presetData.SetTemperature === setTemperature
1292
1337
  && presetData.OperationMode === operationMode
1293
1338
  && presetData.VaneHorizontalDirection === vaneHorizontalDirection
1294
1339
  && presetData.VaneVerticalDirection === vaneVerticalDirection
1295
- && presetData[fanKey] === setFanSpeed) : false;
1340
+ && presetData.FanSpeed === setFanSpeed) : false;
1296
1341
 
1297
1342
  const characteristicType = preset.characteristicType;
1298
1343
  this.presetsServices?.[i]?.updateCharacteristic(characteristicType, preset.state);
1299
1344
  });
1300
1345
  };
1301
1346
 
1347
+ //update schedules state
1348
+ if (this.schedules.length > 0) {
1349
+ this.schedules.forEach((schedule, i) => {
1350
+ const scheduleData = schedulesOnServer.find(s => s[presetsIdKey] === schedule.id);
1351
+ schedule.state = scheduleEnabled; //scheduleData.Enabled : false;
1352
+
1353
+ const characteristicType = schedule.characteristicType;
1354
+ this.schedulesServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1355
+ });
1356
+ };
1357
+
1302
1358
  //update buttons state
1303
1359
  if (this.buttons.length > 0) {
1304
1360
  this.buttons.forEach((button, i) => {
package/src/deviceatw.js CHANGED
@@ -38,7 +38,8 @@ class DeviceAtw extends EventEmitter {
38
38
  this.temperatureFlowZone2Sensor = device.temperatureFlowZone2Sensor || false;
39
39
  this.temperatureReturnZone2Sensor = device.temperatureReturnZone2Sensor || false;
40
40
  this.errorSensor = device.errorSensor || false;
41
- this.presets = (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0);
41
+ this.presets = this.accountType === 'melcloud' ? (device.presets || []).filter(preset => (preset.displayType ?? 0) > 0 && preset.id !== '0') : [];
42
+ this.schedules = this.accountType === 'melcloudhome' ? (device.schedules || []).filter(schedule => (schedule.displayType ?? 0) > 0 && schedule.id !== '0') : [];
42
43
  this.buttons = (device.buttonsSensors || []).filter(button => (button.displayType ?? 0) > 0);
43
44
  this.deviceId = device.id;
44
45
  this.deviceName = device.name;
@@ -62,6 +63,14 @@ class DeviceAtw extends EventEmitter {
62
63
  preset.previousSettings = {};
63
64
  }
64
65
 
66
+ //schedules configured
67
+ for (const schedule of this.schedules) {
68
+ schedule.name = schedule.name || 'Schedule'
69
+ schedule.serviceType = [null, Service.Outlet, Service.Switch, Service.MotionSensor, Service.OccupancySensor, Service.ContactSensor][schedule.displayType];
70
+ schedule.characteristicType = [null, Characteristic.On, Characteristic.On, Characteristic.MotionDetected, Characteristic.OccupancyDetected, Characteristic.ContactSensorState][schedule.displayType];
71
+ schedule.state = false;
72
+ }
73
+
65
74
  //buttons configured
66
75
  for (const button of this.buttons) {
67
76
  button.name = button.name || 'Button'
@@ -261,6 +270,7 @@ class DeviceAtw extends EventEmitter {
261
270
  const deviceName = this.deviceName;
262
271
  const accountName = this.accountName;
263
272
  const presetsOnServer = this.accessory.presets;
273
+ const schedulesOnServer = this.accessory.schedules;
264
274
  const zonesCount = this.accessory.zonesCount;
265
275
  const caseHeatPump = this.accessory.caseHeatPump;
266
276
  const caseZone1 = this.accessory.caseZone1;
@@ -1121,6 +1131,42 @@ class DeviceAtw extends EventEmitter {
1121
1131
  });
1122
1132
  };
1123
1133
 
1134
+ //schedules services
1135
+ if (this.schedules.length > 0) {
1136
+ if (this.logDebug) this.emit('debug', `Prepare schedules services`);
1137
+ this.schedulesServices = [];
1138
+ this.schedules.forEach((schedule, i) => {
1139
+ //get preset name
1140
+ const name = schedule.name;
1141
+
1142
+ //get preset name prefix
1143
+ const namePrefix = schedule.namePrefix;
1144
+
1145
+ const serviceName = namePrefix ? `${accessoryName} ${name}` : name;
1146
+ const serviceType = schedule.serviceType;
1147
+ const characteristicType = schedule.characteristicType;
1148
+ const scheduleService = new serviceType(serviceName, `Schedule ${deviceId} ${i}`);
1149
+ scheduleService.addOptionalCharacteristic(Characteristic.ConfiguredName);
1150
+ scheduleService.setCharacteristic(Characteristic.ConfiguredName, serviceName);
1151
+ scheduleService.getCharacteristic(characteristicType)
1152
+ .onGet(async () => {
1153
+ const state = schedule.state;
1154
+ return state;
1155
+ })
1156
+ .onSet(async (state) => {
1157
+ try {
1158
+ deviceData.ScheduleEnabled = state;
1159
+ await this.melCloudAta.send(this.accountType, this.displayType, deviceData, 'scheduleset');
1160
+ if (this.logInfo) this.emit('info', `${state ? 'Set:' : 'Unset:'} ${name}`);
1161
+ } catch (error) {
1162
+ if (this.logWarn) this.emit('warn', `Set schedule error: ${error}`);
1163
+ };
1164
+ });
1165
+ this.schedulesServices.push(scheduleService);
1166
+ accessory.addService(scheduleService);
1167
+ });
1168
+ };
1169
+
1124
1170
  //buttons services
1125
1171
  if (this.buttons.length > 0) {
1126
1172
  if (this.logDebug) this.emit('debug', `Prepare buttons services`);
@@ -1336,8 +1382,10 @@ class DeviceAtw extends EventEmitter {
1336
1382
  const tempStepKey = this.accountType === 'melcloud' ? 'TemperatureIncrement' : 'HasHalfDegreeIncrements';
1337
1383
  const errorKey = this.accountType === 'melcloud' ? 'HasError' : 'IsInError';
1338
1384
 
1339
- //presets
1340
- const presetsOnServer = deviceData[presetsKey] ?? [];
1385
+ //presets schedule
1386
+ const scheduleEnabled = deviceData.ScheduleEnabled;
1387
+ const schedulesOnServer = deviceData.Schedule ?? [];
1388
+ const presetsOnServer = deviceData.Presets ?? [];
1341
1389
 
1342
1390
  //device info
1343
1391
  const hasHeatPump = ![1, 2, 3, 4, 5, 6, 7, 15].includes(this.hideZone);
@@ -1416,6 +1464,7 @@ class DeviceAtw extends EventEmitter {
1416
1464
  //accessory
1417
1465
  const obj = {
1418
1466
  presets: presetsOnServer,
1467
+ schedules: schedulesOnServer,
1419
1468
  power: power ? 1 : 0,
1420
1469
  unitStatus: unitStatus,
1421
1470
  idleZone1: idleZone1,
@@ -1439,6 +1488,7 @@ class DeviceAtw extends EventEmitter {
1439
1488
  useFahrenheit: this.useFahrenheit,
1440
1489
  temperatureUnit: TemperatureDisplayUnits[this.useFahrenheit],
1441
1490
  isInError: isInError,
1491
+ scheduleEnabled: scheduleEnabled,
1442
1492
  zones: [],
1443
1493
  zonesSensors: []
1444
1494
  };
@@ -1839,6 +1889,17 @@ class DeviceAtw extends EventEmitter {
1839
1889
  });
1840
1890
  };
1841
1891
 
1892
+ //update schedules state
1893
+ if (this.schedules.length > 0) {
1894
+ this.schedules.forEach((schedule, i) => {
1895
+ const scheduleData = schedulesOnServer.find(s => s[presetsIdKey] === schedule.id);
1896
+ schedule.state = scheduleEnabled; //scheduleData.Enabled : false;
1897
+
1898
+ const characteristicType = schedule.characteristicType;
1899
+ this.schedulesServices?.[i]?.updateCharacteristic(characteristicType, schedule.state);
1900
+ });
1901
+ };
1902
+
1842
1903
  //update buttons state
1843
1904
  if (this.buttons.length > 0) {
1844
1905
  this.buttons.forEach((button, i) => {