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 +1 -1
- package/src/ITV2Client.js +16 -3
- package/src/itv2-session.js +28 -58
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsc-itv2-client",
|
|
3
3
|
"author": "fajitacat",
|
|
4
|
-
"version": "1.0.
|
|
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(
|
|
982
|
+
console.log(`dsc itv2: ${offset} ${hex.padEnd(48)} |${ascii}|`);
|
|
970
983
|
}
|
|
971
984
|
}
|
|
972
985
|
}
|
package/src/itv2-session.js
CHANGED
|
@@ -756,31 +756,11 @@ export class ITv2Session {
|
|
|
756
756
|
return this.buildCommand(CMD.REQUEST_ACCESS, payload);
|
|
757
757
|
}
|
|
758
758
|
|
|
759
|
-
// ==================== VarBytes Encoding
|
|
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
|
|
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 (
|
|
793
|
-
return Buffer.from([0x02,
|
|
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 (
|
|
796
|
-
return Buffer.from([0x03,
|
|
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 (
|
|
799
|
-
return Buffer.from([0x04,
|
|
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
|
|
853
|
-
|
|
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.
|
|
858
|
-
|
|
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')}
|
|
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 -
|
|
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
|
|
1086
|
+
// [Partition VarBytes][ArmMode 1B][UserCode raw BCD bytes]
|
|
1117
1087
|
//
|
|
1118
|
-
//
|
|
1119
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
1116
|
+
// [Partition VarBytes][UserCode raw BCD bytes]
|
|
1147
1117
|
//
|
|
1148
|
-
//
|
|
1149
|
-
//
|
|
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
|
|
1124
|
+
// Build payload: [partition VarBytes][userCode raw BCD]
|
|
1155
1125
|
const payload = Buffer.concat([
|
|
1156
1126
|
this.encodeVarBytes(partition),
|
|
1157
|
-
|
|
1127
|
+
bcdBytes
|
|
1158
1128
|
]);
|
|
1159
1129
|
|
|
1160
1130
|
this.log(`[Session] PARTITION_DISARM: partition=${partition}, code=${codeStr}`);
|