mclimate-payload-helper 1.2.7 → 1.2.8

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.
Files changed (70) hide show
  1. package/dist/decoders/commandsReadingHelper.d.ts.map +1 -1
  2. package/dist/decoders/commandsReadingHelper.js +36 -0
  3. package/dist/decoders/commandsReadingHelper.js.map +1 -1
  4. package/dist/decoders/payloadParsers/co2PirLitePayloadParser.d.ts.map +1 -1
  5. package/dist/decoders/payloadParsers/co2PirLitePayloadParser.js +25 -23
  6. package/dist/decoders/payloadParsers/co2PirLitePayloadParser.js.map +1 -1
  7. package/dist/decoders/payloadParsers/htPirLitePayloadParser.d.ts.map +1 -1
  8. package/dist/decoders/payloadParsers/htPirLitePayloadParser.js +20 -20
  9. package/dist/decoders/payloadParsers/htPirLitePayloadParser.js.map +1 -1
  10. package/dist/encoders/Co2DisplayCommonCommands.d.ts +4 -0
  11. package/dist/encoders/Co2DisplayCommonCommands.d.ts.map +1 -1
  12. package/dist/encoders/Co2DisplayCommonCommands.js +38 -0
  13. package/dist/encoders/Co2DisplayCommonCommands.js.map +1 -1
  14. package/dist/encoders/Co2PirLiteCommands.d.ts +6 -2
  15. package/dist/encoders/Co2PirLiteCommands.d.ts.map +1 -1
  16. package/dist/encoders/Co2PirLiteCommands.js +66 -13
  17. package/dist/encoders/Co2PirLiteCommands.js.map +1 -1
  18. package/dist/encoders/HTPirLiteCommands.d.ts +6 -2
  19. package/dist/encoders/HTPirLiteCommands.d.ts.map +1 -1
  20. package/dist/encoders/HTPirLiteCommands.js +67 -12
  21. package/dist/encoders/HTPirLiteCommands.js.map +1 -1
  22. package/dist/encoders/types/schemas.d.ts +80 -170
  23. package/dist/encoders/types/schemas.d.ts.map +1 -1
  24. package/dist/encoders/types/schemas.js +29 -7
  25. package/dist/encoders/types/schemas.js.map +1 -1
  26. package/dist/test/payloadDecoders.test.js +26 -21
  27. package/dist/test/payloadDecoders.test.js.map +1 -1
  28. package/dist/test/payloadEncoders.test.js +8 -14
  29. package/dist/test/payloadEncoders.test.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/decoders/commandsReadingHelper.ts +34 -0
  32. package/src/decoders/payloadParsers/co2PirLitePayloadParser.ts +28 -23
  33. package/src/decoders/payloadParsers/htPirLitePayloadParser.ts +22 -20
  34. package/src/encoders/Co2PirLiteCommands.ts +66 -14
  35. package/src/encoders/HTPirLiteCommands.ts +67 -13
  36. package/src/encoders/types/schemas.ts +50 -12
  37. package/src/test/payloadDecoders.test.ts +26 -26
  38. package/src/test/payloadEncoders.test.ts +12 -22
  39. package/dist/encoders/FanCoilThermostat.d.ts +0 -61
  40. package/dist/encoders/FanCoilThermostat.d.ts.map +0 -1
  41. package/dist/encoders/FanCoilThermostat.js +0 -719
  42. package/dist/encoders/FanCoilThermostat.js.map +0 -1
  43. package/dist/encoders/SetAqiLed.d.ts +0 -6
  44. package/dist/encoders/SetAqiLed.d.ts.map +0 -1
  45. package/dist/encoders/SetAqiLed.js +0 -46
  46. package/dist/encoders/SetAqiLed.js.map +0 -1
  47. package/dist/encoders/types/enumValidation.d.ts +0 -6
  48. package/dist/encoders/types/enumValidation.d.ts.map +0 -1
  49. package/dist/encoders/types/enumValidation.js +0 -50
  50. package/dist/encoders/types/enumValidation.js.map +0 -1
  51. package/dist/encoders/types/schemaValidatedEnums.d.ts +0 -40
  52. package/dist/encoders/types/schemaValidatedEnums.d.ts.map +0 -1
  53. package/dist/encoders/types/schemaValidatedEnums.js +0 -181
  54. package/dist/encoders/types/schemaValidatedEnums.js.map +0 -1
  55. package/dist/types/deviceTypes.d.ts +0 -19
  56. package/dist/types/deviceTypes.d.ts.map +0 -1
  57. package/dist/types/deviceTypes.js +0 -23
  58. package/dist/types/deviceTypes.js.map +0 -1
  59. package/dist/types/index.d.ts +0 -2
  60. package/dist/types/index.d.ts.map +0 -1
  61. package/dist/types/index.js +0 -18
  62. package/dist/types/index.js.map +0 -1
  63. package/dist/types/types.d.ts +0 -13
  64. package/dist/types/types.d.ts.map +0 -1
  65. package/dist/types/types.js +0 -9
  66. package/dist/types/types.js.map +0 -1
  67. package/dist/validation.d.ts +0 -3
  68. package/dist/validation.d.ts.map +0 -1
  69. package/dist/validation.js +0 -8
  70. package/dist/validation.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mclimate-payload-helper",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "description": "Mclimate payload helper",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1162,6 +1162,23 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1162
1162
  }
1163
1163
  }
1164
1164
  break
1165
+ case '37':
1166
+ {
1167
+ try {
1168
+ command_len = 1
1169
+ const data = { pirSensorState: parseInt(commands[i + 1], 16) }
1170
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
1171
+ } catch (e) {
1172
+ throw new CustomError({
1173
+ message: `Failed to process command '37'`,
1174
+ hexData,
1175
+ command,
1176
+ deviceType,
1177
+ originalError: e as Error,
1178
+ })
1179
+ }
1180
+ }
1181
+ break
1165
1182
  case '38':
1166
1183
  {
1167
1184
  try {
@@ -1179,6 +1196,23 @@ export const commandsReadingHelper = (hexData: string, payloadLength: number, de
1179
1196
  }
1180
1197
  }
1181
1198
  break
1199
+ case '39':
1200
+ {
1201
+ try {
1202
+ command_len = 2
1203
+ const data = { occupancyTimeout: (parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16)}
1204
+ Object.assign(resultToPass, { ...resultToPass }, { ...data })
1205
+ } catch (e) {
1206
+ throw new CustomError({
1207
+ message: `Failed to process command '39'`,
1208
+ hexData,
1209
+ command,
1210
+ deviceType,
1211
+ originalError: e as Error,
1212
+ })
1213
+ }
1214
+ }
1215
+ break
1182
1216
  case '3a':
1183
1217
  {
1184
1218
  try {
@@ -8,6 +8,7 @@ interface CO2PirLiteData {
8
8
  relativeHumidity?: number
9
9
  batteryVoltage?: number
10
10
  CO2?: number
11
+ pirTriggerCount?: number
11
12
  pir?: boolean
12
13
  }
13
14
 
@@ -17,33 +18,37 @@ export const co2PirLitePayloadParser = (hexData: string) => {
17
18
  try {
18
19
  const handleKeepAliveData = (bytes: number[]) => {
19
20
  const keepaliveData: CO2PirLiteData = {}
20
- // Bytes 1-2: Internal temperature sensor data
21
- // Formula: t[°C] = (T[15:0]-400)/10
22
- const tempMsb = bytes[1] // bits 15:8
23
- const tempLsb = bytes[2] // bits 7:0
24
- const rawTemperature = (tempMsb << 8) | tempLsb
25
- keepaliveData.sensorTemperature = Number(((rawTemperature - 400) / 10).toFixed(2))
21
+
22
+ // Byte 1 bit 2: PIR flag
23
+ keepaliveData.pir = ((bytes[1] & 0x04) >> 2) === 1
24
+
25
+ // Byte 1 (bits 1:0) and Byte 2: Internal temperature sensor data
26
+ // Formula: t[°C] = (T[9:0] - 400) / 10
27
+ // Extract bits 1:0 from byte 1 for the higher bits (bits 9:8)
28
+ const tempHighBits = (bytes[1] & 0x03) << 8
29
+ // Use all bits from byte 2 for the lower bits (bits 7:0)
30
+ const tempLowBits = bytes[2]
31
+ // Combine to get the full 10-bit temperature value
32
+ const tempValue = tempHighBits | tempLowBits
33
+ keepaliveData.sensorTemperature = Number(((tempValue - 400) / 10).toFixed(2))
26
34
 
27
35
  // Byte 3: Relative Humidity data
28
- // Formula: RH[%] = (XX*100)/256
36
+ // Formula: RH[%] = (XX * 100) / 256
29
37
  keepaliveData.relativeHumidity = Number(((bytes[3] * 100) / 256).toFixed(2))
30
38
 
31
- // Bytes 4-5: Device battery voltage data
32
- // Battery voltage [mV]
33
- const batteryMsb = bytes[4] // bits 15:8
34
- const batteryLsb = bytes[5] // bits 7:0
35
- keepaliveData.batteryVoltage = Number((((batteryMsb << 8) | batteryLsb) / 1000).toFixed(2))
39
+ // Byte 4: Device battery voltage data
40
+ // Battery voltage [mV] = ((XX * 2200) / 255) + 1600
41
+ keepaliveData.batteryVoltage = Number(((((bytes[4] * 2200) / 255) + 1600) / 1000).toFixed(2))
36
42
 
37
- // CO2 calculation from data 6 and 7
38
- const co2Low = bytes[6] // Lower byte of CO2
39
- const co2High = (bytes[7] & 0xf8) >> 3 // Mask the upper 5 bits and shift them right
40
- keepaliveData.CO2 = (co2High << 8) | co2Low // Shift co2High left by 8 bits and combine with co2Low
43
+ // Bytes 5-6: CO2 value in ppm
44
+ // Byte 5: CO2 value lower bits [7:0]
45
+ // Byte 6 bits 7:3: CO2 value higher bits [12:8]
46
+ const co2LowBits = bytes[5]
47
+ const co2HighBits = ((bytes[6] & 0xF8) >> 3) << 8 // Mask upper 5 bits, shift right by 3 to get bits in position, then shift left by 8
48
+ keepaliveData.CO2 = co2HighBits | co2LowBits
41
49
 
42
- // Byte 8: PIR sensor status (only bit 0 is used)
43
- // 0 - No motion detected
44
- // 1 - Motion detected
45
- const pirValue = bytes[8] & 0x01 // Extract only the last bit
46
- keepaliveData.pir = pirValue === 1
50
+ // Byte 7: PIR trigger count
51
+ keepaliveData.pirTriggerCount = bytes[7]
47
52
  Object.assign(deviceData, { ...deviceData }, { ...keepaliveData })
48
53
  }
49
54
 
@@ -52,7 +57,7 @@ export const co2PirLitePayloadParser = (hexData: string) => {
52
57
  if (!byteArray) return
53
58
 
54
59
  // Route the message based on the command byte
55
- if (byteArray[0] == 1) {
60
+ if (byteArray[0] == 81) {
56
61
  // This is a keepalive message
57
62
  handleKeepAliveData(byteArray)
58
63
  } else {
@@ -69,7 +74,7 @@ export const co2PirLitePayloadParser = (hexData: string) => {
69
74
  // Handle the remaining keepalive data if present
70
75
  if (shouldKeepAlive) {
71
76
  // Extract the last 9 bytes which contain keepalive data for CO2-PIR-Lite
72
- const keepaliveData = hexData.slice(-18) // 9 bytes = 18 hex chars
77
+ const keepaliveData = hexData.slice(-16) // 8 bytes = 16 hex chars
73
78
  const dataToPass = byteArrayParser(keepaliveData)
74
79
  if (!dataToPass) return
75
80
  handleKeepAliveData(dataToPass)
@@ -7,6 +7,7 @@ interface HTPirLiteData {
7
7
  sensorTemperature?: number
8
8
  relativeHumidity?: number
9
9
  batteryVoltage?: number
10
+ pirTriggerCount?: number
10
11
  pir?: boolean
11
12
  }
12
13
 
@@ -17,28 +18,29 @@ export const htPirLitePayloadParser = (hexData: string) => {
17
18
  const handleKeepAliveData = (bytes: number[]) => {
18
19
  const keepaliveData: HTPirLiteData = {}
19
20
 
20
- // Bytes 1-2: Internal temperature sensor data
21
- // Formula: t[°C] = (T[15:0]-400)/10
22
- const tempMsb = bytes[1] // bits 15:8
23
- const tempLsb = bytes[2] // bits 7:0
24
- const rawTemperature = (tempMsb << 8) | tempLsb
25
- keepaliveData.sensorTemperature = Number(((rawTemperature - 400) / 10).toFixed(2))
21
+ // Byte 1 bit 2: Occupied flag
22
+ keepaliveData.pir = ((bytes[1] & 0x04) >> 2) === 1
23
+
24
+ // Byte 1 (bits 1:0) and Byte 2: Internal temperature sensor data
25
+ // Formula: t[°C] = (T[9:0] - 400) / 10
26
+ // Extract bits 1:0 from byte 1 for the higher bits (bits 9:8)
27
+ const tempHighBits = (bytes[1] & 0x03) << 8
28
+ // Use all bits from byte 2 for the lower bits (bits 7:0)
29
+ const tempLowBits = bytes[2]
30
+ // Combine to get the full 10-bit temperature value
31
+ const tempValue = tempHighBits | tempLowBits
32
+ keepaliveData.sensorTemperature = Number(((tempValue - 400) / 10).toFixed(2))
26
33
 
27
34
  // Byte 3: Relative Humidity data
28
- // Formula: RH[%] = (XX*100)/256
35
+ // Formula: RH[%] = (XX * 100) / 256
29
36
  keepaliveData.relativeHumidity = Number(((bytes[3] * 100) / 256).toFixed(2))
30
37
 
31
- // Bytes 4-5: Device battery voltage data
32
- // Battery voltage [mV]
33
- const batteryMsb = bytes[4] // bits 15:8
34
- const batteryLsb = bytes[5] // bits 7:0
35
- keepaliveData.batteryVoltage = Number((((batteryMsb << 8) | batteryLsb) / 1000).toFixed(2))
38
+ // Byte 4: Device battery voltage data
39
+ // Battery voltage [mV] = ((XX * 2200) / 255) + 1600
40
+ keepaliveData.batteryVoltage = Number(((((bytes[4] * 2200) / 255) + 1600) / 1000).toFixed(2))
36
41
 
37
- // Byte 6: PIR sensor status (only bit 0 is used, other bits are reserved)
38
- // 0 - No motion detected
39
- // 1 - Motion detected
40
- const pirValue = bytes[6] & 0x01 // Extract only the last bit
41
- keepaliveData.pir = pirValue === 1
42
+ // Byte 5: PIR trigger count
43
+ keepaliveData.pirTriggerCount = bytes[5]
42
44
 
43
45
  Object.assign(deviceData, { ...deviceData }, { ...keepaliveData })
44
46
  }
@@ -48,7 +50,7 @@ export const htPirLitePayloadParser = (hexData: string) => {
48
50
  if (!byteArray) return
49
51
 
50
52
  // Route the message based on the command byte
51
- if (byteArray[0] == 1) {
53
+ if (byteArray[0] == 81) {
52
54
  // This is a keepalive message
53
55
  handleKeepAliveData(byteArray)
54
56
  } else {
@@ -64,8 +66,8 @@ export const htPirLitePayloadParser = (hexData: string) => {
64
66
 
65
67
  // Handle the remaining keepalive data if present
66
68
  if (shouldKeepAlive) {
67
- // Extract the last 7 bytes which contain keepalive data for HT-PIR-Lite
68
- const keepaliveData = hexData.slice(-14) // 7 bytes = 14 hex chars
69
+ // Extract the last 6 bytes which contain keepalive data for HT-PIR-Lite
70
+ const keepaliveData = hexData.slice(-12) // 6 bytes = 12 hex chars
69
71
  const dataToPass = byteArrayParser(keepaliveData)
70
72
  if (!dataToPass) return
71
73
  handleKeepAliveData(dataToPass)
@@ -1,4 +1,4 @@
1
- import { GeneralCommands, PIRCommands, CO2SensorCommands } from '@/encoders'
1
+ import { GeneralCommands, CO2SensorCommands } from '@/encoders'
2
2
  import { applyMixins, delMethods } from '@/utils'
3
3
  import { Co2PirLiteCommandTypes, DeviceCommandSchemas } from '@/encoders/types'
4
4
  import { BaseCommand } from '@/encoders'
@@ -7,30 +7,84 @@ import { CustomError } from '@/utils'
7
7
  import { decToHex } from '@/utils'
8
8
 
9
9
  export class Co2PirLiteCommands extends GeneralCommands {
10
- static setUplinkSendingOnButtonPress(params: Co2PirLiteCommandTypes.SetUplinkSendingOnButtonPressParams) {
10
+ static setPIRSensorState(params: Co2PirLiteCommandTypes.SetPIRSensorStateParams) {
11
11
  try {
12
- DeviceCommandSchemas.Co2PirLiteCommandSchemas.setUplinkSendingOnButtonPress.parse(params)
13
- const { value } = params
14
- return new BaseCommand('SetUplinkSendingOnButtonPress', 0x2e, decToHex(value))
12
+ DeviceCommandSchemas.Co2PirLiteCommandSchemas.setPIRSensorState.parse(params)
13
+ const { state } = params
14
+ return new BaseCommand('SetPIRSensorState', 0x36, decToHex(state))
15
15
  } catch (e) {
16
16
  if (e instanceof ZodError) {
17
17
  throw new CustomError({
18
- message: 'Zod validation error during SetUplinkSendingOnButtonPress execution',
19
- command: 'SetUplinkSendingOnButtonPress',
18
+ message: 'Zod validation error during SetPIRSensorState execution',
19
+ command: 'SetPIRSensorState',
20
20
  originalError: e,
21
21
  })
22
22
  } else {
23
23
  throw new CustomError({
24
- message: 'Error during SetUplinkSendingOnButtonPress execution',
25
- command: 'SetUplinkSendingOnButtonPress',
24
+ message: 'Error during SetPIRSensorState execution',
25
+ command: 'SetPIRSensorState',
26
+ originalError: e as Error,
27
+ })
28
+ }
29
+ }
30
+ }
31
+ static getPIRSensorState() {
32
+ return new BaseCommand('GetPIRSensorState', 0x37)
33
+ }
34
+ static setPIRSensorSensitivity(params: Co2PirLiteCommandTypes.SetPIRSensorSensitivityParams) {
35
+ try {
36
+ DeviceCommandSchemas.Co2PirLiteCommandSchemas.setPIRSensorSensitivity.parse(params)
37
+ const { sensitivity } = params
38
+ return new BaseCommand('SetPIRSensorSensitivity', 0x3e, decToHex(sensitivity))
39
+ } catch (e) {
40
+ if (e instanceof ZodError) {
41
+ throw new CustomError({
42
+ message: 'Zod validation error during SetPIRSensorSensitivity execution',
43
+ command: 'SetPIRSensorSensitivity',
44
+ originalError: e,
45
+ })
46
+ } else {
47
+ throw new CustomError({
48
+ message: 'Error during SetPIRSensorSensitivity execution',
49
+ command: 'SetPIRSensorSensitivity',
50
+ originalError: e as Error,
51
+ })
52
+ }
53
+ }
54
+ }
55
+
56
+ static getPIRSensorSensitivity() {
57
+ return new BaseCommand('GetPIRSensorSensitivity', 0x3f)
58
+ }
59
+
60
+ static setOccupancyTimeout(params: Co2PirLiteCommandTypes.SetOccupancyTimeoutParams) {
61
+ try {
62
+ DeviceCommandSchemas.Co2PirLiteCommandSchemas.setOccupancyTimeout.parse(params)
63
+ const { timeout } = params
64
+ // Convert timeout to two bytes (high byte and low byte)
65
+ const highByte = (timeout >> 8) & 0xFF
66
+ const lowByte = timeout & 0xFF
67
+
68
+ return new BaseCommand('SetOccupancyTimeout', 0x38, decToHex(highByte) + decToHex(lowByte))
69
+ } catch (e) {
70
+ if (e instanceof ZodError) {
71
+ throw new CustomError({
72
+ message: 'Zod validation error during SetOccupancyTimeout execution',
73
+ command: 'SetOccupancyTimeout',
74
+ originalError: e,
75
+ })
76
+ } else {
77
+ throw new CustomError({
78
+ message: 'Error during SetOccupancyTimeout execution',
79
+ command: 'SetOccupancyTimeout',
26
80
  originalError: e as Error,
27
81
  })
28
82
  }
29
83
  }
30
84
  }
31
85
 
32
- static getUplinkSendingOnButtonPress() {
33
- return new BaseCommand('GetUplinkSendingOnButtonPress', 0x2f)
86
+ static getOccupancyTimeout() {
87
+ return new BaseCommand('GetOccupancyTimeout', 0x39)
34
88
  }
35
89
 
36
90
  static restartDevice() {
@@ -38,11 +92,9 @@ export class Co2PirLiteCommands extends GeneralCommands {
38
92
  }
39
93
  }
40
94
 
41
- applyMixins(Co2PirLiteCommands, [GeneralCommands, PIRCommands, CO2SensorCommands])
95
+ applyMixins(Co2PirLiteCommands, [GeneralCommands, CO2SensorCommands])
42
96
 
43
97
  delMethods(Co2PirLiteCommands, [
44
- 'setPIRInitPeriod',
45
- 'getPIRInitPeriod',
46
98
  'setNotifyPeriod',
47
99
  'getNotifyPeriod',
48
100
  'setBuzzerNotification',
@@ -1,36 +1,90 @@
1
1
  import { GeneralCommands, PIRCommands } from '@/encoders'
2
2
  import { applyMixins, delMethods } from '@/utils'
3
- import { HTPirLiteCommandTypes, DeviceCommandSchemas } from '@/encoders/types'
3
+ import { HTPirLiteCommandTypes, DeviceCommandSchemas, PIRCommandTypes } from '@/encoders/types'
4
4
  import { BaseCommand } from '@/encoders'
5
5
  import { ZodError } from 'zod'
6
6
  import { CustomError } from '@/utils'
7
7
  import { decToHex } from '@/utils'
8
8
 
9
9
  export class HTPirLiteCommands extends GeneralCommands {
10
- static setUplinkSendingOnButtonPress(params: HTPirLiteCommandTypes.SetUplinkSendingOnButtonPressParams) {
10
+ static setPIRSensorState(params: HTPirLiteCommandTypes.SetPIRSensorStateParams) {
11
11
  try {
12
- DeviceCommandSchemas.HTPirLiteCommandSchemas.setUplinkSendingOnButtonPress.parse(params)
13
- const { value } = params
14
- return new BaseCommand('SetUplinkSendingOnButtonPress', 0x2e, decToHex(value))
12
+ DeviceCommandSchemas.HTPirLiteCommandSchemas.setPIRSensorState.parse(params)
13
+ const { state } = params
14
+ return new BaseCommand('SetPIRSensorState', 0x36, decToHex(state))
15
15
  } catch (e) {
16
16
  if (e instanceof ZodError) {
17
17
  throw new CustomError({
18
- message: 'Zod validation error during SetUplinkSendingOnButtonPress execution',
19
- command: 'SetUplinkSendingOnButtonPress',
18
+ message: 'Zod validation error during SetPIRSensorState execution',
19
+ command: 'SetPIRSensorState',
20
20
  originalError: e,
21
21
  })
22
22
  } else {
23
23
  throw new CustomError({
24
- message: 'Error during SetUplinkSendingOnButtonPress execution',
25
- command: 'SetUplinkSendingOnButtonPress',
24
+ message: 'Error during SetPIRSensorState execution',
25
+ command: 'SetPIRSensorState',
26
+ originalError: e as Error,
27
+ })
28
+ }
29
+ }
30
+ }
31
+ static getPIRSensorState() {
32
+ return new BaseCommand('GetPIRSensorState', 0x37)
33
+ }
34
+ static setPIRSensorSensitivity(params: HTPirLiteCommandTypes.SetPIRSensorSensitivityParams) {
35
+ try {
36
+ DeviceCommandSchemas.HTPirLiteCommandSchemas.setPIRSensorSensitivity.parse(params)
37
+ const { sensitivity } = params
38
+ return new BaseCommand('SetPIRSensorSensitivity', 0x3e, decToHex(sensitivity))
39
+ } catch (e) {
40
+ if (e instanceof ZodError) {
41
+ throw new CustomError({
42
+ message: 'Zod validation error during SetPIRSensorSensitivity execution',
43
+ command: 'SetPIRSensorSensitivity',
44
+ originalError: e,
45
+ })
46
+ } else {
47
+ throw new CustomError({
48
+ message: 'Error during SetPIRSensorSensitivity execution',
49
+ command: 'SetPIRSensorSensitivity',
50
+ originalError: e as Error,
51
+ })
52
+ }
53
+ }
54
+ }
55
+
56
+ static getPIRSensorSensitivity() {
57
+ return new BaseCommand('GetPIRSensorSensitivity', 0x3f)
58
+ }
59
+
60
+ static setOccupancyTimeout(params: HTPirLiteCommandTypes.SetOccupancyTimeoutParams) {
61
+ try {
62
+ DeviceCommandSchemas.HTPirLiteCommandSchemas.setOccupancyTimeout.parse(params)
63
+ const { timeout } = params
64
+ // Convert timeout to two bytes (high byte and low byte)
65
+ const highByte = (timeout >> 8) & 0xFF
66
+ const lowByte = timeout & 0xFF
67
+
68
+ return new BaseCommand('SetOccupancyTimeout', 0x38, decToHex(highByte) + decToHex(lowByte))
69
+ } catch (e) {
70
+ if (e instanceof ZodError) {
71
+ throw new CustomError({
72
+ message: 'Zod validation error during SetOccupancyTimeout execution',
73
+ command: 'SetOccupancyTimeout',
74
+ originalError: e,
75
+ })
76
+ } else {
77
+ throw new CustomError({
78
+ message: 'Error during SetOccupancyTimeout execution',
79
+ command: 'SetOccupancyTimeout',
26
80
  originalError: e as Error,
27
81
  })
28
82
  }
29
83
  }
30
84
  }
31
85
 
32
- static getUplinkSendingOnButtonPress() {
33
- return new BaseCommand('GetUplinkSendingOnButtonPress', 0x2f)
86
+ static getOccupancyTimeout() {
87
+ return new BaseCommand('GetOccupancyTimeout', 0x39)
34
88
  }
35
89
 
36
90
  static restartDevice() {
@@ -38,6 +92,6 @@ export class HTPirLiteCommands extends GeneralCommands {
38
92
  }
39
93
  }
40
94
 
41
- applyMixins(HTPirLiteCommands, [GeneralCommands, PIRCommands])
95
+ applyMixins(HTPirLiteCommands, [GeneralCommands])
42
96
 
43
- delMethods(HTPirLiteCommands, ['setPIRInitPeriod', 'getPIRInitPeriod'])
97
+ delMethods(HTPirLiteCommands, [])
@@ -193,6 +193,20 @@ export const ButtonEnums = {
193
193
  } satisfies NumberEnum<1 | 2 | 3>,
194
194
  } as const
195
195
 
196
+ export const HTPirLiteEnums = {
197
+ setPIRSensorState: {
198
+ 0: 'Disabled',
199
+ 1: 'Enabled',
200
+ } satisfies NumberEnum<0 | 1>,
201
+ } as const
202
+
203
+ export const Co2PirLiteEnums = {
204
+ setPIRSensorState: {
205
+ 0: 'Disabled',
206
+ 1: 'Enabled',
207
+ } satisfies NumberEnum<0 | 1>,
208
+ } as const
209
+
196
210
  /* ---------------------------------------GENERAL COMMANDS--------------------------------------- */
197
211
 
198
212
  const GeneralCommandSchemas = {
@@ -457,7 +471,7 @@ const VickiCommandSchemas = {
457
471
  eventIndex: z.number().int().min(0).max(19),
458
472
  startHour: z.number().int().min(0).max(23),
459
473
  startMinute: z.number().int().min(0).max(59),
460
- targetTemperature: z.number().min(0.1).max(30.0).multipleOf(0.1),
474
+ targetTemperature: z.number().min(5.0).max(30.0).multipleOf(0.1),
461
475
  daysActive: z.object({
462
476
  monday: z.boolean(),
463
477
  tuesday: z.boolean(),
@@ -1325,28 +1339,52 @@ export namespace ButtonCommandTypes {
1325
1339
  /* --------------------------------------- HT PIR LITE COMMANDS --------------------------------------- */
1326
1340
  const HTPirLiteCommandSchemas = {
1327
1341
  ...GeneralCommandSchemas,
1328
- ...PIRCommandSchemas,
1329
1342
  setUplinkSendingOnButtonPress: z.object({
1330
1343
  value: z.number().min(0).max(1),
1331
1344
  }),
1332
1345
  getUplinkSendingOnButtonPress: z.object({}),
1333
1346
  restartDevice: z.object({}),
1347
+ setPIRSensorState: z.object({
1348
+ state: z.number().min(0).max(1), // 0: disabled, 1: enabled
1349
+ }),
1350
+ getPIRSensorState: z.object({}),
1351
+ setPIRSensorSensitivity: z.object({
1352
+ sensitivity: z.number().min(12).max(255),
1353
+ }),
1354
+ getPIRSensorSensitivity: z.object({}),
1355
+ setOccupancyTimeout: z.object({
1356
+ timeout: z.number().min(15).max(65535), // in seconds, between 15 and 65535
1357
+ }),
1358
+ getOccupancyTimeout: z.object({}),
1334
1359
  }
1335
1360
 
1336
1361
  export namespace HTPirLiteCommandTypes {
1337
1362
  export type SetUplinkSendingOnButtonPressParams = z.infer<typeof HTPirLiteCommandSchemas.setUplinkSendingOnButtonPress>
1338
1363
  export type GetUplinkSendingOnButtonPressParams = z.infer<typeof HTPirLiteCommandSchemas.getUplinkSendingOnButtonPress>
1339
1364
  export type RestartDeviceParams = z.infer<typeof HTPirLiteCommandSchemas.restartDevice>
1365
+ export type SetPIRSensorStateParams = z.infer<typeof HTPirLiteCommandSchemas.setPIRSensorState>
1366
+ export type GetPIRSensorStateParams = z.infer<typeof HTPirLiteCommandSchemas.getPIRSensorState>
1367
+ export type SetPIRSensorSensitivityParams = z.infer<typeof HTPirLiteCommandSchemas.setPIRSensorSensitivity>
1368
+ export type GetPIRSensorSensitivityParams = z.infer<typeof HTPirLiteCommandSchemas.getPIRSensorSensitivity>
1369
+ export type SetOccupancyTimeoutParams = z.infer<typeof HTPirLiteCommandSchemas.setOccupancyTimeout>
1370
+ export type GetOccupancyTimeoutParams = z.infer<typeof HTPirLiteCommandSchemas.getOccupancyTimeout>
1340
1371
  }
1341
1372
 
1342
1373
  /* --------------------------------------- CO2 PIR LITE COMMANDS --------------------------------------- */
1343
1374
  const Co2PirLiteCommandSchemas = {
1344
1375
  ...GeneralCommandSchemas,
1345
- ...PIRCommandSchemas,
1346
- setUplinkSendingOnButtonPress: z.object({
1347
- value: z.number().min(0).max(1),
1376
+ setPIRSensorState: z.object({
1377
+ state: z.number().min(0).max(1), // 0: disabled, 1: enabled
1348
1378
  }),
1349
- getUplinkSendingOnButtonPress: z.object({}),
1379
+ getPIRSensorState: z.object({}),
1380
+ setPIRSensorSensitivity: z.object({
1381
+ sensitivity: z.number().min(12).max(255),
1382
+ }),
1383
+ getPIRSensorSensitivity: z.object({}),
1384
+ setOccupancyTimeout: z.object({
1385
+ timeout: z.number().min(15).max(65535), // in seconds, between 15 and 65535
1386
+ }),
1387
+ getOccupancyTimeout: z.object({}),
1350
1388
  restartDevice: z.object({}),
1351
1389
  setCo2BoundaryLevels: z.object({
1352
1390
  good_medium: z.number().min(0).max(1500),
@@ -1370,12 +1408,12 @@ const Co2PirLiteCommandSchemas = {
1370
1408
  }
1371
1409
 
1372
1410
  export namespace Co2PirLiteCommandTypes {
1373
- export type SetUplinkSendingOnButtonPressParams = z.infer<
1374
- typeof Co2PirLiteCommandSchemas.setUplinkSendingOnButtonPress
1375
- >
1376
- export type GetUplinkSendingOnButtonPressParams = z.infer<
1377
- typeof Co2PirLiteCommandSchemas.getUplinkSendingOnButtonPress
1378
- >
1411
+ export type SetPIRSensorStateParams = z.infer<typeof Co2PirLiteCommandSchemas.setPIRSensorState>
1412
+ export type GetPIRSensorStateParams = z.infer<typeof Co2PirLiteCommandSchemas.getPIRSensorState>
1413
+ export type SetPIRSensorSensitivityParams = z.infer<typeof Co2PirLiteCommandSchemas.setPIRSensorSensitivity>
1414
+ export type GetPIRSensorSensitivityParams = z.infer<typeof Co2PirLiteCommandSchemas.getPIRSensorSensitivity>
1415
+ export type SetOccupancyTimeoutParams = z.infer<typeof Co2PirLiteCommandSchemas.setOccupancyTimeout>
1416
+ export type GetOccupancyTimeoutParams = z.infer<typeof Co2PirLiteCommandSchemas.getOccupancyTimeout>
1379
1417
  export type RestartDeviceParams = z.infer<typeof Co2PirLiteCommandSchemas.restartDevice>
1380
1418
  }
1381
1419
 
@@ -776,12 +776,6 @@ describe('ASPM payload decoder', () => {
776
776
  })
777
777
  })
778
778
  test('all settings (relay)', () => {
779
- console.log(
780
- uplinkPayloadParser(
781
- '041312120A19781B001D02181F5F46210113FA2310250E605F00011C034A241805D9E7195201',
782
- DeviceType.Relay16,
783
- ),
784
- )
785
779
  expect(
786
780
  uplinkPayloadParser(
787
781
  '041312120A19781B001D02181F5F46210113FA2310250E605F00011C034A241805D9E7195201',
@@ -884,41 +878,47 @@ describe('Melissa payload decoder', () => {
884
878
  })
885
879
  describe('CO2PirLite payload decoder', () => {
886
880
  test('simple keepalive', () => {
887
- expect(uplinkPayloadParser('0102A67F0D70D31000', DeviceType.CO2PirLite)).toStrictEqual({
888
- CO2: 723,
889
- pir: false,
890
- sensorTemperature: 27.8,
891
- relativeHumidity: 49.61,
892
- batteryVoltage: 3.44,
881
+ expect(uplinkPayloadParser('8106797EDA4718F1', DeviceType.CO2PirLite)).toStrictEqual({
882
+ pir: true,
883
+ sensorTemperature: 23.3,
884
+ relativeHumidity: 49.22,
885
+ batteryVoltage: 3.48,
886
+ CO2: 839,
887
+ pirTriggerCount: 241
893
888
  })
894
889
  })
895
890
  test('keepalive with response of preloaded code', () => {
896
- expect(uplinkPayloadParser('120A0102A67F0D70D31000', DeviceType.CO2PirLite)).toStrictEqual({
897
- CO2: 723,
898
- pir: false,
899
- sensorTemperature: 27.8,
900
- relativeHumidity: 49.61,
901
- batteryVoltage: 3.44,
891
+ expect(uplinkPayloadParser('39003C120A8102797BD95A1800', DeviceType.CO2PirLite)).toStrictEqual({
892
+ occupancyTimeout: 60,
902
893
  keepAliveTime: 10,
894
+ pir: false,
895
+ sensorTemperature: 23.3,
896
+ relativeHumidity: 48.05,
897
+ batteryVoltage: 3.47,
898
+ CO2: 858,
899
+ pirTriggerCount: 0
903
900
  })
904
901
  })
905
902
  })
906
903
  describe('HTPirLite payload decoder', () => {
907
904
  test('simple keepalive', () => {
908
- expect(uplinkPayloadParser('010288800A4500', DeviceType.HTPirLite)).toStrictEqual({
905
+ expect(uplinkPayloadParser('8102797DE201', DeviceType.HTPirLite)).toStrictEqual({
909
906
  pir: false,
910
- sensorTemperature: 24.8,
911
- relativeHumidity: 50,
912
- batteryVoltage: 2.63,
907
+ sensorTemperature: 23.3,
908
+ relativeHumidity: 48.83,
909
+ batteryVoltage: 3.55,
910
+ pirTriggerCount: 1,
913
911
  })
914
912
  })
915
913
  test('keepalive with response of preloaded code', () => {
916
- expect(uplinkPayloadParser('120A010288800A4500', DeviceType.HTPirLite)).toStrictEqual({
914
+ expect(uplinkPayloadParser('39003C120A8102797DE201', DeviceType.HTPirLite)).toStrictEqual({
917
915
  pir: false,
918
- sensorTemperature: 24.8,
919
- relativeHumidity: 50,
920
- batteryVoltage: 2.63,
916
+ sensorTemperature: 23.3,
917
+ relativeHumidity: 48.83,
918
+ batteryVoltage: 3.55,
919
+ pirTriggerCount: 1,
921
920
  keepAliveTime: 10,
921
+ occupancyTimeout: 60,
922
922
  })
923
923
  })
924
924
  })