mclimate-payload-helper 1.2.0 → 1.2.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.
@@ -1588,10 +1588,24 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1588
1588
  {
1589
1589
  try {
1590
1590
  let data
1591
- command_len = 3
1592
- let state = parseInt(commands[i + 1], 16)
1593
- let time = (parseInt(commands[i + 2], 16) << 8) | parseInt(commands[i + 3], 16)
1594
- data = { relayTimerInSeconds: { state, time } }
1591
+ if (deviceType === DeviceType.Vicki) {
1592
+ command_len = 2;
1593
+ let notificationByte = parseInt(commands[i + 1], 16);
1594
+ // Extract notification flags from bits
1595
+ let temperatureRestoredAfterManualBoost = !!(notificationByte & 0x01); // Bit 0
1596
+ let temperatureChangedByHeatingSchedule = !!(notificationByte & 0x02); // Bit 1
1597
+ data = {
1598
+ notifications: {
1599
+ temperatureRestoredAfterManualBoost: temperatureRestoredAfterManualBoost,
1600
+ temperatureChangedByHeatingSchedule: temperatureChangedByHeatingSchedule
1601
+ }
1602
+ };
1603
+ }else{
1604
+ command_len = 3
1605
+ let state = parseInt(commands[i + 1], 16)
1606
+ let time = (parseInt(commands[i + 2], 16) << 8) | parseInt(commands[i + 3], 16)
1607
+ data = { relayTimerInSeconds: { state, time } }
1608
+ }
1595
1609
  Object.assign(resultToPass, { ...resultToPass }, { ...data })
1596
1610
  } catch (e) {
1597
1611
  throw new CustomError({
@@ -1656,6 +1670,9 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1656
1670
  if (deviceType === DeviceType.FanCoilThermostat) {
1657
1671
  command_len = 1
1658
1672
  data = { fanSpeedNotOccupied: parseInt(commands[i + 1], 16) }
1673
+ }else{
1674
+ command_len = 1
1675
+ data = { manualChangeRelayState: parseInt(commands[i + 1], 16) }
1659
1676
  }
1660
1677
  Object.assign(resultToPass, { ...resultToPass }, { ...data })
1661
1678
  } catch (e) {
@@ -1701,6 +1718,11 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1701
1718
  data = {
1702
1719
  overheatingEvents: { events: parseInt(commands[i + 1], 16), temperature: parseInt(commands[i + 2], 16) },
1703
1720
  }
1721
+ } else if (deviceType === DeviceType.Vicki) {
1722
+ command_len = 1;
1723
+ let offsetByte = parseInt(commands[i + 1], 16);
1724
+ let offsetHours = (offsetByte & 0x80) ? (offsetByte - 256) : offsetByte;
1725
+ data = { deviceTimeZone: offsetHours };
1704
1726
  }
1705
1727
  Object.assign(resultToPass, { ...resultToPass }, { ...data })
1706
1728
  } catch (e) {
@@ -1754,6 +1776,10 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1754
1776
  current: (parseInt(commands[i + 2], 16) << 8) | parseInt(commands[i + 3], 16),
1755
1777
  },
1756
1778
  }
1779
+ }else{
1780
+ command_len = 1;
1781
+ let timeValue = parseInt(commands[i + 1], 16);
1782
+ data = { autoSetpointRestoreStatus: timeValue === 0 ? 0 : timeValue * 10 };
1757
1783
  }
1758
1784
  Object.assign(resultToPass, { ...resultToPass }, { ...data })
1759
1785
  } catch (e) {
@@ -1893,6 +1919,9 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1893
1919
  if (deviceType === DeviceType.FanCoilThermostat) {
1894
1920
  command_len = 1
1895
1921
  data = { frostProtectionStatus: parseInt(commands[i + 1], 16) }
1922
+ }else{
1923
+ command_len = 1;
1924
+ data = { timeRequestByMACcommand: parseInt(commands[i + 1], 16) };
1896
1925
  }
1897
1926
  Object.assign(resultToPass, { ...resultToPass }, { ...data })
1898
1927
  } catch (e) {
@@ -2307,23 +2336,6 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
2307
2336
  }
2308
2337
  }
2309
2338
  break
2310
- case '58': {
2311
- command_len = 1;
2312
- let notificationByte = parseInt(commands[i + 1], 16);
2313
-
2314
- // Extract notification flags from bits
2315
- let temperatureRestoredAfterManualBoost = !!(notificationByte & 0x01); // Bit 0
2316
- let temperatureChangedByHeatingSchedule = !!(notificationByte & 0x02); // Bit 1
2317
-
2318
- let data = {
2319
- notifications: {
2320
- temperatureRestoredAfterManualBoost: temperatureRestoredAfterManualBoost,
2321
- temperatureChangedByHeatingSchedule: temperatureChangedByHeatingSchedule
2322
- }
2323
- };
2324
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2325
- }
2326
- break;
2327
2339
  case '5a':
2328
2340
  {
2329
2341
  try {
@@ -2441,24 +2453,6 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
2441
2453
  }
2442
2454
  }
2443
2455
  break
2444
- case '5d':
2445
- {
2446
- try {
2447
- command_len = 1
2448
- let data = { manualChangeRelayState: parseInt(commands[i + 1], 16) }
2449
-
2450
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2451
- } catch (e) {
2452
- throw new CustomError({
2453
- message: `Failed to process command '5d'`,
2454
- hexData,
2455
- command,
2456
- deviceType,
2457
- originalError: e as Error,
2458
- })
2459
- }
2460
- }
2461
- break
2462
2456
  case '5e':
2463
2457
  {
2464
2458
  try {
@@ -2487,116 +2481,142 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
2487
2481
  })
2488
2482
  }
2489
2483
  }
2490
- break
2491
- case '60': {
2492
- command_len = 1;
2493
- let offsetByte = parseInt(commands[i + 1], 16);
2494
- let offsetHours = (offsetByte & 0x80) ? (offsetByte - 256) : offsetByte;
2495
- let data = { deviceTimeZone: offsetHours };
2496
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2497
- }
2498
2484
  break;
2499
- case '62': {
2500
- command_len = 1;
2501
- let timeValue = parseInt(commands[i + 1], 16);
2502
- let data = { autoSetpointRestoreStatus: timeValue === 0 ? 0 : timeValue * 10 };
2503
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2504
- }
2505
- break;
2506
-
2507
2485
  case '64': {
2508
- command_len = 1;
2509
- let ledDurationValue = parseInt(commands[i + 1], 16);
2510
- let durationInSeconds = ledDurationValue / 2; // As per the docs, value is divided by 2 to get seconds
2486
+ try {
2487
+ command_len = 1;
2488
+ let ledDurationValue = parseInt(commands[i + 1], 16);
2489
+ let durationInSeconds = ledDurationValue / 2; // As per the docs, value is divided by 2 to get seconds
2511
2490
 
2512
- let data = {
2513
- ledIndicationDuration: durationInSeconds
2514
- };
2515
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2491
+ let data = {
2492
+ ledIndicationDuration: durationInSeconds
2493
+ };
2494
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
2495
+ } catch (e) {
2496
+ throw new CustomError({
2497
+ message: `Failed to process command '64'`,
2498
+ hexData,
2499
+ command,
2500
+ deviceType,
2501
+ originalError: e as Error,
2502
+ })
2503
+ }
2516
2504
  }
2517
2505
  break;
2518
-
2519
2506
  case '66': {
2520
- command_len = 2;
2507
+ try {
2508
+ command_len = 2;
2521
2509
 
2522
- let tempHigh = parseInt(commands[i + 1], 16);
2523
- let tempLow = parseInt(commands[i + 2], 16);
2524
- let targetTemp = (tempHigh << 8) | tempLow;
2525
- let data = {
2526
- offlineTargetTemperature: targetTemp === 0 ? 0 : targetTemp / 10
2527
- };
2528
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2510
+ let tempHigh = parseInt(commands[i + 1], 16);
2511
+ let tempLow = parseInt(commands[i + 2], 16);
2512
+ let targetTemp = (tempHigh << 8) | tempLow;
2513
+ let data = {
2514
+ offlineTargetTemperature: targetTemp === 0 ? 0 : targetTemp / 10
2515
+ };
2516
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
2517
+ } catch (e) {
2518
+ throw new CustomError({
2519
+ message: `Failed to process command '66'`,
2520
+ hexData,
2521
+ command,
2522
+ deviceType,
2523
+ originalError: e as Error,
2524
+ })
2525
+ }
2529
2526
  }
2530
2527
  break;
2531
2528
  case '68': {
2532
- command_len = 1;
2533
- let data = { internalAlgoTemporaryState: parseInt(commands[i + 1], 16) === 0 ? true : false };
2534
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2529
+ try {
2530
+ command_len = 1;
2531
+ let data = { internalAlgoTemporaryState: parseInt(commands[i + 1], 16) === 0 ? true : false };
2532
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
2533
+ } catch (e) {
2534
+ throw new CustomError({
2535
+ message: `Failed to process command '68'`,
2536
+ hexData,
2537
+ command,
2538
+ deviceType,
2539
+ originalError: e as Error,
2540
+ })
2541
+ }
2535
2542
  }
2536
2543
  break;
2537
2544
  case '6a': {
2538
- command_len = 12;
2539
- let temperatureLevels: { [key: string]: number } = {};
2545
+ try {
2546
+ command_len = 12;
2547
+ let temperatureLevels: { [key: string]: number } = {};
2540
2548
 
2541
- // Process 6 scale levels (0-5), each with a 2-byte temperature value
2542
- for (let level = 0; level < 6; level++) {
2543
- let tempHighByte = parseInt(commands[i + 1 + (level * 2)], 16);
2544
- let tempLowByte = parseInt(commands[i + 2 + (level * 2)], 16);
2545
- let tempValue = (tempHighByte << 8) | tempLowByte;
2549
+ // Process 6 scale levels (0-5), each with a 2-byte temperature value
2550
+ for (let level = 0; level < 6; level++) {
2551
+ let tempHighByte = parseInt(commands[i + 1 + (level * 2)], 16);
2552
+ let tempLowByte = parseInt(commands[i + 2 + (level * 2)], 16);
2553
+ let tempValue = (tempHighByte << 8) | tempLowByte;
2546
2554
 
2547
- // The temperature values are pre-multiplied by 10
2548
- temperatureLevels['level' + level] = tempValue / 10;
2549
- }
2555
+ // The temperature values are pre-multiplied by 10
2556
+ temperatureLevels['level' + level] = tempValue / 10;
2557
+ }
2550
2558
 
2551
- let data = {
2552
- temperatureLevels: temperatureLevels
2553
- };
2559
+ let data = {
2560
+ temperatureLevels: temperatureLevels
2561
+ };
2554
2562
 
2555
- Object.assign(resultToPass, { ...resultToPass }, { ...data })
2563
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
2564
+ } catch (e) {
2565
+ throw new CustomError({
2566
+ message: `Failed to process command '6a'`,
2567
+ hexData,
2568
+ command,
2569
+ deviceType,
2570
+ originalError: e as Error,
2571
+ })
2572
+ }
2556
2573
  }
2557
2574
  break;
2558
2575
  case '6c': {
2559
- command_len = 4;
2560
- let eventsHighByte = parseInt(commands[i + 2], 16); // Events 19-16
2561
- let eventsMidByte = parseInt(commands[i + 3], 16); // Events 15-8
2562
- let eventsLowByte = parseInt(commands[i + 4], 16); // Events 7-0
2576
+ try {
2577
+ command_len = 4;
2578
+ let eventsHighByte = parseInt(commands[i + 2], 16); // Events 19-16
2579
+ let eventsMidByte = parseInt(commands[i + 3], 16); // Events 15-8
2580
+ let eventsLowByte = parseInt(commands[i + 4], 16); // Events 7-0
2563
2581
 
2564
- // Create a more structured and readable format for heating events
2565
- let heatingEventStates: { [key: string]: boolean } = {};
2582
+ // Create a more structured and readable format for heating events
2583
+ let heatingEventStates: { [key: string]: boolean } = {};
2566
2584
 
2567
- // Process all 20 events (0-19) in a single loop
2568
- for (let eventIdx = 0; eventIdx < 20; eventIdx++) {
2569
- // Calculate which bit to check
2570
- let bitPosition;
2571
- if (eventIdx >= 16) {
2572
- // Events 16-19 in high byte
2573
- bitPosition = eventIdx - 16;
2574
- heatingEventStates[eventIdx] = !!(eventsHighByte & (1 << bitPosition));
2575
- } else if (eventIdx >= 8) {
2576
- // Events 8-15 in mid byte
2577
- bitPosition = eventIdx - 8;
2578
- heatingEventStates[eventIdx] = !!(eventsMidByte & (1 << bitPosition));
2579
- } else {
2580
- // Events 0-7 in low byte
2581
- bitPosition = eventIdx;
2582
- heatingEventStates[eventIdx] = !!(eventsLowByte & (1 << bitPosition));
2585
+ // Process all 20 events (0-19) in a single loop
2586
+ for (let eventIdx = 0; eventIdx < 20; eventIdx++) {
2587
+ // Calculate which bit to check
2588
+ let bitPosition;
2589
+ if (eventIdx >= 16) {
2590
+ // Events 16-19 in high byte
2591
+ bitPosition = eventIdx - 16;
2592
+ heatingEventStates[eventIdx] = !!(eventsHighByte & (1 << bitPosition));
2593
+ } else if (eventIdx >= 8) {
2594
+ // Events 8-15 in mid byte
2595
+ bitPosition = eventIdx - 8;
2596
+ heatingEventStates[eventIdx] = !!(eventsMidByte & (1 << bitPosition));
2597
+ } else {
2598
+ // Events 0-7 in low byte
2599
+ bitPosition = eventIdx;
2600
+ heatingEventStates[eventIdx] = !!(eventsLowByte & (1 << bitPosition));
2601
+ }
2583
2602
  }
2584
- }
2585
2603
 
2586
- let data = {
2587
- heatingEventStates: heatingEventStates
2588
- };
2604
+ let data = {
2605
+ heatingEventStates: heatingEventStates
2606
+ };
2589
2607
 
2590
- Object.assign(resultToPass, { ...resultToPass }, { ...data });
2591
- break;
2592
- }
2593
- case '6e': {
2594
- command_len = 1;
2595
- let data = { timeRequestByMACcommand: parseInt(commands[i + 1], 16) };
2596
- Object.assign(resultToPass, { ...resultToPass }, { ...data });
2608
+ Object.assign(resultToPass, { ...resultToPass }, { ...data });
2609
+ } catch (e) {
2610
+ throw new CustomError({
2611
+ message: `Failed to process command '6c'`,
2612
+ hexData,
2613
+ command,
2614
+ deviceType,
2615
+ originalError: e as Error,
2616
+ })
2617
+ }
2597
2618
  break;
2598
2619
  }
2599
-
2600
2620
  case '9b':
2601
2621
  {
2602
2622
  try {
@@ -418,7 +418,13 @@ export class FanCoilThermostatCommands extends GeneralCommands {
418
418
  try {
419
419
  DeviceCommandSchemas.FanCoilThermostatCommandSchemas.setTempSensorCompensation.parse(params)
420
420
  const { compensation, temperature } = params
421
- return new BaseCommand('SetTempSensorCompensation', 0x5a, decToHex(compensation), decToHex(temperature * 10))
421
+ // TODO: check if below is accurate
422
+ //
423
+ // 00: Positive compensation, 01: Negative compensation
424
+ const compensationByte = temperature >= 0 ? 0 : 1
425
+ // Use absolute value of temperature * 10 for the temperature byte
426
+ const temperatureByte = Math.abs(temperature * 10)
427
+ return new BaseCommand('SetTempSensorCompensation', 0x5a, decToHex(compensationByte), decToHex(temperatureByte))
422
428
  } catch (e) {
423
429
  if (e instanceof ZodError) {
424
430
  throw new CustomError({
@@ -137,11 +137,18 @@ export const FanCoilThermostatEnums = {
137
137
 
138
138
  export const TValveEnums = {
139
139
  setValveState: {
140
- 0: 'Open',
141
- 1: 'Close',
140
+ 0: 'Close',
141
+ 1: 'Open',
142
142
  } satisfies NumberEnum<0 | 1>,
143
143
  } as const
144
144
 
145
+ export const FloodEnums = {
146
+ setFloodEventUplinkType: {
147
+ '00': 'Unconfirmed',
148
+ '01': 'Confirmed',
149
+ } satisfies StringEnum<'00' | '01'>,
150
+ } as const
151
+
145
152
  export const WirelessThermostatEnums = {
146
153
  setHumidityVisibility: {
147
154
  0: 'Hide',
@@ -178,6 +185,11 @@ export const ButtonEnums = {
178
185
  1: 'Send later when allowed',
179
186
  0: "Don't send later when allowed",
180
187
  } satisfies NumberEnum<0 | 1>,
188
+ clearPressEventCounter: {
189
+ 1: 'Single',
190
+ 2: 'Double',
191
+ 3: 'Triple',
192
+ } satisfies NumberEnum<1 | 2 | 3>,
181
193
  } as const
182
194
 
183
195
  /* ---------------------------------------GENERAL COMMANDS--------------------------------------- */
@@ -286,7 +298,7 @@ const DisplayCommandSchemas = {
286
298
  }),
287
299
  getDisplayRefreshPeriod: z.object({}),
288
300
  setDeepSleepMode: z.object({
289
- state: z.number(),
301
+ state: z.number().min(1).max(1),
290
302
  }),
291
303
  setHumidityVisibility: z.object({
292
304
  state: z.number().min(0).max(1), // 0: hide, 1: show
@@ -361,7 +373,7 @@ const VickiCommandSchemas = {
361
373
  setBatteryRangesOverVoltage: z.object({
362
374
  Range1: z.number().int().min(0).max(255),
363
375
  Range2: z.number().int().min(0).max(255),
364
- Range3: z.number().int().min(0).max(65535),
376
+ Range3: z.number().int().min(0).max(255),
365
377
  }),
366
378
  setOvac: z.object({
367
379
  ovac: z.number().int().min(0).max(255),
@@ -1186,7 +1198,7 @@ const HTSensorCommandSchemas = {
1186
1198
  ...GeneralCommandSchemas,
1187
1199
  setTemperatureCompensation: z.object({
1188
1200
  negativeCompensation: z.boolean(), // true: negative compensation, false: positive compensation
1189
- compensation: z.number().min(0).max(255).multipleOf(0.1),
1201
+ compensation: z.number().min(0).max(25.5).multipleOf(0.1),
1190
1202
  }),
1191
1203
  getTemperatureCompensation: z.object({}),
1192
1204
  setHumidityCompensation: z.object({
@@ -1296,7 +1308,7 @@ const ButtonCommandSchemas = {
1296
1308
  }),
1297
1309
  getSendEventLater: z.object({}),
1298
1310
  clearPressEventCounter: z.object({
1299
- value: z.number(),
1311
+ value: z.number().min(1).max(3),
1300
1312
  }),
1301
1313
  restartDevice: z.object({}),
1302
1314
  getSinglePressEventCounter: z.object({}),
@@ -1314,7 +1326,7 @@ const HTPirLiteCommandSchemas = {
1314
1326
  ...GeneralCommandSchemas,
1315
1327
  ...PIRCommandSchemas,
1316
1328
  setUplinkSendingOnButtonPress: z.object({
1317
- value: z.number(),
1329
+ value: z.number().min(0).max(1),
1318
1330
  }),
1319
1331
  getUplinkSendingOnButtonPress: z.object({}),
1320
1332
  restartDevice: z.object({}),
@@ -1331,7 +1343,7 @@ const Co2PirLiteCommandSchemas = {
1331
1343
  ...GeneralCommandSchemas,
1332
1344
  ...PIRCommandSchemas,
1333
1345
  setUplinkSendingOnButtonPress: z.object({
1334
- value: z.number(),
1346
+ value: z.number().min(0).max(1),
1335
1347
  }),
1336
1348
  getUplinkSendingOnButtonPress: z.object({}),
1337
1349
  restartDevice: z.object({}),
package/src/index.ts CHANGED
@@ -31,5 +31,6 @@ export {
31
31
  WirelessThermostatEnums,
32
32
  ButtonEnums,
33
33
  GeneralCommandsEnums,
34
+ FloodEnums,
34
35
  } from '@/encoders/types'
35
36
  export { CustomError } from '@/utils'