dsc-itv2-client 1.0.28 → 1.0.30

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsc-itv2-client",
3
3
  "author": "fajitacat",
4
- "version": "1.0.28",
4
+ "version": "1.0.30",
5
5
  "description": "Reverse engineered DSC ITV2 Protocol Client Library for TL280R Communicator - Monitor and control DSC alarm panels",
6
6
  "main": "src/index.js",
7
7
  "type": "module",
package/src/ITV2Client.js CHANGED
@@ -333,6 +333,19 @@ export class ITV2Client extends EventEmitter {
333
333
  this._sendPacket(packet);
334
334
  }
335
335
 
336
+ /**
337
+ * Send a raw command directly (not wrapped in 0x0800)
338
+ * Useful for testing different query formats
339
+ * @param {number} command - Command code (e.g., 0x0812)
340
+ * @param {Buffer} [payload] - Optional payload data
341
+ */
342
+ sendDirect(command, payload = null) {
343
+ if (!this._checkEstablished()) return;
344
+ const packet = this.session.buildCommand(command, payload);
345
+ this._logMinimal(`[Direct] Sending command 0x${command.toString(16).padStart(4, '0')} ${payload ? 'with ' + payload.length + ' bytes payload' : 'no payload'}`);
346
+ this._sendPacket(packet);
347
+ }
348
+
336
349
  // ==================== Authentication Methods ====================
337
350
 
338
351
  /**
@@ -943,13 +956,13 @@ export class ITV2Client extends EventEmitter {
943
956
  _log(message) {
944
957
  if (this.logLevel === 'verbose') {
945
958
  const timestamp = new Date().toISOString();
946
- console.log(`[${timestamp}] ${message}`);
959
+ console.log(`dsc itv2: [${timestamp}] ${message}`);
947
960
  }
948
961
  }
949
962
 
950
963
  _logMinimal(message) {
951
964
  if (this.logLevel === 'minimal' || this.logLevel === 'verbose') {
952
- console.log(message);
965
+ console.log(`dsc itv2: ${message}`);
953
966
  }
954
967
  }
955
968
 
@@ -966,7 +979,7 @@ export class ITV2Client extends EventEmitter {
966
979
  .join('');
967
980
 
968
981
  const offset = i.toString(16).padStart(4, '0').toUpperCase();
969
- console.log(`${offset} ${hex.padEnd(48)} |${ascii}|`);
982
+ console.log(`dsc itv2: ${offset} ${hex.padEnd(48)} |${ascii}|`);
970
983
  }
971
984
  }
972
985
  }
@@ -756,31 +756,11 @@ export class ITv2Session {
756
756
  return this.buildCommand(CMD.REQUEST_ACCESS, payload);
757
757
  }
758
758
 
759
- // ==================== VarBytes Encoding/Decoding ====================
760
-
761
- /**
762
- * Decode a VarBytes value from a buffer at the given offset
763
- * Format: [length byte][value bytes in big-endian]
764
- * @param {Buffer} buffer - Buffer to read from
765
- * @param {number} offset - Starting offset in buffer
766
- * @returns {{ value: number, bytesRead: number }} - Decoded value and total bytes consumed
767
- */
768
- static decodeVarBytes(buffer, offset) {
769
- const length = buffer[offset];
770
- let value = 0;
771
- for (let i = 0; i < length; i++) {
772
- value = (value << 8) | buffer[offset + 1 + i];
773
- }
774
- return { value, bytesRead: 1 + length };
775
- }
759
+ // ==================== VarBytes Encoding ====================
776
760
 
777
761
  /**
778
762
  * Encode a value as VarBytes (DSC protocol variable-length integer)
779
- * Format: [length byte][value bytes in big-endian]
780
- *
781
- * SDK reference: StoreVarBytesValue → CUInt32Map::StoreValue with IsLittleEndian flag
782
- * On little-endian systems, StoreValue reverses bytes → big-endian wire format
783
- *
763
+ * Format: [length byte][value bytes in little-endian]
784
764
  * @param {number} value - Value to encode
785
765
  * @returns {Buffer} - VarBytes encoded buffer
786
766
  */
@@ -789,14 +769,14 @@ export class ITv2Session {
789
769
  // 1 byte value
790
770
  return Buffer.from([0x01, value & 0xFF]);
791
771
  } else if (value < 0x10000) {
792
- // 2 byte value (big-endian)
793
- return Buffer.from([0x02, (value >> 8) & 0xFF, value & 0xFF]);
772
+ // 2 byte value (little-endian)
773
+ return Buffer.from([0x02, value & 0xFF, (value >> 8) & 0xFF]);
794
774
  } else if (value < 0x1000000) {
795
- // 3 byte value (big-endian)
796
- return Buffer.from([0x03, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]);
775
+ // 3 byte value (little-endian)
776
+ return Buffer.from([0x03, value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF]);
797
777
  } else {
798
- // 4 byte value (big-endian)
799
- return Buffer.from([0x04, (value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]);
778
+ // 4 byte value (little-endian)
779
+ return Buffer.from([0x04, value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]);
800
780
  }
801
781
  }
802
782
 
@@ -849,16 +829,14 @@ export class ITv2Session {
849
829
  const appSeq = this.appSequence;
850
830
  this.appSequence = (this.appSequence + 1) & 0xFF;
851
831
 
852
- // Build 0x0800 payload: [AppSeqNum][CommandToRequest 2B BE][ByteArray(InnerPayload)]
853
- // SDK uses StoreByteArray with sizeMode=-1 (VarBytes length prefix)
854
- const wrappedPayload = this.encodeByteArray(innerPayload);
855
- const payload = Buffer.alloc(3 + wrappedPayload.length);
832
+ // Build 0x0800 payload: [AppSeqNum][CommandToRequest LE][InnerPayload]
833
+ const payload = Buffer.alloc(3 + innerPayload.length);
856
834
  payload[0] = appSeq;
857
- payload.writeUInt16BE(innerCommand, 1); // Command in big-endian (per SDK CUInt16Map::StoreValue)
858
- wrappedPayload.copy(payload, 3);
835
+ payload.writeUInt16LE(innerCommand, 1); // Command in little-endian
836
+ innerPayload.copy(payload, 3);
859
837
 
860
838
  this.log(`[Session] Building 0x0800 Command Request: innerCmd=0x${innerCommand.toString(16)}, appSeq=${appSeq}`);
861
- this.log(`[Session] Inner payload: ${innerPayload.toString('hex')}, wrapped: ${wrappedPayload.toString('hex')}`);
839
+ this.log(`[Session] Inner payload: ${innerPayload.toString('hex')}`);
862
840
 
863
841
  return this.buildCommand(CMD.STATUS_REQUEST, payload);
864
842
  }
@@ -1091,41 +1069,33 @@ export class ITv2Session {
1091
1069
 
1092
1070
  // ============ Arm/Disarm Commands ============
1093
1071
 
1094
- // Wire protocol arm mode bytes (from CSWTCH.68 in SDK binary)
1095
- // Maps DSC_PartitionArmedState enum → wire byte
1096
- static ARM_MODE = {
1097
- STAY: 0x01, // StayArmed (enum 2)
1098
- AWAY: 0x02, // AwayArmed (enum 3)
1099
- STAY_NO_ENTRY_DELAY: 0x08, // StayArmedWithNoEntryDelay (enum 4)
1100
- AWAY_NO_ENTRY_DELAY: 0x03, // AwayArmedWithNoEntryDelay (enum 5)
1101
- NIGHT: 0x04, // NightModeArmed (enum 6)
1102
- INTERIOR: 0x20, // InteriorArmed (enum 7)
1103
- USER: 0x06, // UserArmed (enum 8)
1104
- };
1105
-
1106
1072
  /**
1107
1073
  * Build PARTITION_ARM command
1108
1074
  * @param {number} partition - Partition number (1-8)
1109
- * @param {number} armMode - Wire protocol arm mode byte (use ITv2Session.ARM_MODE constants)
1075
+ * @param {number} armMode - Arming mode:
1076
+ * 0 = Stay (perimeter only)
1077
+ * 1 = Away (full arm)
1078
+ * 2 = No Entry Delay
1079
+ * 3 = Force Arm (bypass open zones)
1110
1080
  * @param {string} accessCode - Master/user code (e.g., "5555")
1111
1081
  */
1112
1082
  buildPartitionArm(partition, armMode, accessCode) {
1113
1083
  const codeStr = accessCode.toString();
1114
1084
 
1115
1085
  // Payload format from SDK decompiled OnGetRawData (ITV2_Cmd_0900):
1116
- // [Partition VarBytes][ArmMode 1B][UserCode ByteArray]
1086
+ // [Partition VarBytes][ArmMode 1B][UserCode raw BCD bytes]
1117
1087
  //
1118
- // VarBytes: [numBytes 1B][value bytes... big-endian]
1119
- // ByteArray: [length VarBytes][data bytes...]
1088
+ // SDK uses StoreByteArray with flag=0 for UserCode, meaning NO VarBytes
1089
+ // length prefix — just raw BCD bytes to end of packet.
1120
1090
 
1121
1091
  // BCD encoding: each pair of digits as one byte
1122
1092
  const bcdBytes = this._encodeBcd(codeStr);
1123
1093
 
1124
- // Build payload: [partition VarBytes][mode 1B][userCode ByteArray]
1094
+ // Build payload: [partition VarBytes][mode 1B][userCode raw BCD]
1125
1095
  const payload = Buffer.concat([
1126
1096
  this.encodeVarBytes(partition),
1127
1097
  Buffer.from([armMode]),
1128
- this.encodeByteArray(bcdBytes)
1098
+ bcdBytes
1129
1099
  ]);
1130
1100
 
1131
1101
  this.log(`[Session] PARTITION_ARM: partition=${partition}, mode=${armMode}, code=${codeStr}`);
@@ -1143,18 +1113,18 @@ export class ITv2Session {
1143
1113
  const codeStr = accessCode.toString();
1144
1114
 
1145
1115
  // Payload format from SDK decompiled OnGetRawData (ITV2_Cmd_0901):
1146
- // [Partition VarBytes][UserCode ByteArray]
1116
+ // [Partition VarBytes][UserCode raw BCD bytes]
1147
1117
  //
1148
- // VarBytes: [numBytes 1B][value bytes... big-endian]
1149
- // ByteArray: [length VarBytes][data bytes...]
1118
+ // SDK uses StoreByteArray with flag=0 for UserCode, meaning NO VarBytes
1119
+ // length prefix — just raw BCD bytes to end of packet.
1150
1120
 
1151
1121
  // BCD encoding: each pair of digits as one byte
1152
1122
  const bcdBytes = this._encodeBcd(codeStr);
1153
1123
 
1154
- // Build payload: [partition VarBytes][userCode ByteArray]
1124
+ // Build payload: [partition VarBytes][userCode raw BCD]
1155
1125
  const payload = Buffer.concat([
1156
1126
  this.encodeVarBytes(partition),
1157
- this.encodeByteArray(bcdBytes)
1127
+ bcdBytes
1158
1128
  ]);
1159
1129
 
1160
1130
  this.log(`[Session] PARTITION_DISARM: partition=${partition}, code=${codeStr}`);