dsc-itv2-client 1.0.30 → 2.0.0
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/README.md +143 -221
- package/package.json +4 -3
- package/src/ITV2Client.js +1408 -985
- package/src/constants.js +1 -1
- package/src/event-handler.js +8 -5
- package/src/examples/arm-disarm-example.js +1 -1
- package/src/examples/basic-monitoring.js +16 -6
- package/src/examples/interactive-cli.js +267 -1033
- package/src/itv2-session.js +133 -75
- package/src/response-parsers.js +34 -25
package/src/itv2-session.js
CHANGED
|
@@ -91,11 +91,13 @@ export const CMD = {
|
|
|
91
91
|
ZONE_STATUS: 0x0811,
|
|
92
92
|
PARTITION_STATUS: 0x0812,
|
|
93
93
|
ZONE_BYPASS_STATUS: 0x0813,
|
|
94
|
-
SYSTEM_TROUBLE_STATUS: 0x0814
|
|
94
|
+
SYSTEM_TROUBLE_STATUS: 0x0822, // New trouble status (0x0814 is deprecated)
|
|
95
|
+
SYSTEM_TROUBLE_STATUS_OLD: 0x0814, // Deprecated
|
|
95
96
|
ALARM_MEMORY_INFO: 0x0815,
|
|
96
97
|
BUS_STATUS: 0x0816,
|
|
97
|
-
TROUBLE_DETAIL:
|
|
98
|
+
TROUBLE_DETAIL: 0x0823,
|
|
98
99
|
DOOR_CHIME_STATUS: 0x0819,
|
|
100
|
+
MULTIPLE_MESSAGE: 0x0623,
|
|
99
101
|
// Module Control commands
|
|
100
102
|
PARTITION_ARM: 0x0900,
|
|
101
103
|
PARTITION_DISARM: 0x0901,
|
|
@@ -298,6 +300,22 @@ function buildHeader(senderSeq, receiverSeq, command = null, appSeq = null, comm
|
|
|
298
300
|
* - Type 2: Uses Access Code (32 hex digits) → 16 bytes directly
|
|
299
301
|
*/
|
|
300
302
|
export class ITv2Session {
|
|
303
|
+
/**
|
|
304
|
+
* Arming mode values (from neohub ArmingMode enum)
|
|
305
|
+
*/
|
|
306
|
+
static ARM_MODE = {
|
|
307
|
+
DISARM: 0,
|
|
308
|
+
STAY: 1,
|
|
309
|
+
AWAY: 2,
|
|
310
|
+
NO_ENTRY_DELAY: 3,
|
|
311
|
+
NIGHT: 4,
|
|
312
|
+
QUICK: 5,
|
|
313
|
+
USER: 6,
|
|
314
|
+
STAY_NO_ENTRY: 7,
|
|
315
|
+
AWAY_NO_ENTRY: 8,
|
|
316
|
+
NIGHT_NO_ENTRY: 9,
|
|
317
|
+
};
|
|
318
|
+
|
|
301
319
|
constructor(integrationId, accessCode, log = console.log) {
|
|
302
320
|
this.integrationId = integrationId; // 12-digit ID for packet header
|
|
303
321
|
this.accessCode = accessCode; // 8-digit (Type 1) or 32-hex (Type 2) for encryption
|
|
@@ -368,12 +386,13 @@ export class ITv2Session {
|
|
|
368
386
|
this.log(`[Session] Encrypted framed: ${framed.toString('hex')}`);
|
|
369
387
|
}
|
|
370
388
|
|
|
371
|
-
// Byte stuff
|
|
389
|
+
// Byte stuff header and payload (neohub stuffs both independently)
|
|
390
|
+
const stuffedHeader = stuffBytes(Buffer.from(this.integrationId, 'ascii'));
|
|
372
391
|
const stuffedPayload = stuffBytes(framed);
|
|
373
392
|
|
|
374
|
-
//
|
|
375
|
-
// The panel sends WITH integration ID, but SDK sends WITHOUT it
|
|
393
|
+
// Full packet: [stuffed integration ID][0x7E][stuffed payload][0x7F]
|
|
376
394
|
return Buffer.concat([
|
|
395
|
+
stuffedHeader,
|
|
377
396
|
Buffer.from([0x7E]),
|
|
378
397
|
stuffedPayload,
|
|
379
398
|
Buffer.from([0x7F]),
|
|
@@ -513,21 +532,10 @@ export class ITv2Session {
|
|
|
513
532
|
|
|
514
533
|
/**
|
|
515
534
|
* Build OPEN_SESSION message (echo back panel's session info)
|
|
516
|
-
*
|
|
535
|
+
* OpenSession IS a CommandMessageBase in neohub - it has a CommandSequence byte
|
|
517
536
|
*/
|
|
518
537
|
buildOpenSessionResponse(openSessionData) {
|
|
519
|
-
this.
|
|
520
|
-
|
|
521
|
-
// OPEN_SESSION format: [sender][receiver][cmd 2B][payload...] - NO app sequence!
|
|
522
|
-
const message = buildHeader(
|
|
523
|
-
this.localSequence,
|
|
524
|
-
this.remoteSequence,
|
|
525
|
-
CMD.OPEN_SESSION,
|
|
526
|
-
null, // NO app sequence for OPEN_SESSION
|
|
527
|
-
openSessionData
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
return this.buildPacket(message);
|
|
538
|
+
return this.buildCommand(CMD.OPEN_SESSION, openSessionData);
|
|
531
539
|
}
|
|
532
540
|
|
|
533
541
|
/**
|
|
@@ -759,25 +767,44 @@ export class ITv2Session {
|
|
|
759
767
|
// ==================== VarBytes Encoding ====================
|
|
760
768
|
|
|
761
769
|
/**
|
|
762
|
-
* Encode a value as
|
|
763
|
-
* Format: [length byte][value bytes in
|
|
770
|
+
* Encode a value as CompactInteger (DSC protocol variable-length integer)
|
|
771
|
+
* Format: [length byte][value bytes in big-endian]
|
|
772
|
+
* Matches neohub CompactIntegerSerializer: strips leading zero bytes, stores BE
|
|
764
773
|
* @param {number} value - Value to encode
|
|
765
|
-
* @returns {Buffer} -
|
|
774
|
+
* @returns {Buffer} - CompactInteger encoded buffer
|
|
766
775
|
*/
|
|
767
776
|
encodeVarBytes(value) {
|
|
768
777
|
if (value < 0x100) {
|
|
769
|
-
// 1 byte value
|
|
770
778
|
return Buffer.from([0x01, value & 0xFF]);
|
|
771
779
|
} else if (value < 0x10000) {
|
|
772
|
-
|
|
773
|
-
return Buffer.from([0x02, value & 0xFF, (value >> 8) & 0xFF]);
|
|
780
|
+
return Buffer.from([0x02, (value >> 8) & 0xFF, value & 0xFF]);
|
|
774
781
|
} else if (value < 0x1000000) {
|
|
775
|
-
|
|
776
|
-
return Buffer.from([0x03, value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF]);
|
|
782
|
+
return Buffer.from([0x03, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]);
|
|
777
783
|
} else {
|
|
778
|
-
|
|
779
|
-
|
|
784
|
+
return Buffer.from([0x04, (value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Decode a CompactInteger from a buffer
|
|
790
|
+
* Format: [length byte][value bytes in big-endian]
|
|
791
|
+
* @param {Buffer} buffer - Buffer to read from
|
|
792
|
+
* @param {number} offset - Starting offset
|
|
793
|
+
* @returns {{ value: number, bytesRead: number }}
|
|
794
|
+
*/
|
|
795
|
+
static decodeVarBytes(buffer, offset = 0) {
|
|
796
|
+
if (offset >= buffer.length) {
|
|
797
|
+
throw new Error('Buffer too short for CompactInteger');
|
|
798
|
+
}
|
|
799
|
+
const length = buffer[offset];
|
|
800
|
+
if (offset + 1 + length > buffer.length) {
|
|
801
|
+
throw new Error(`Buffer too short for CompactInteger value (need ${length}, have ${buffer.length - offset - 1})`);
|
|
802
|
+
}
|
|
803
|
+
let value = 0;
|
|
804
|
+
for (let i = 0; i < length; i++) {
|
|
805
|
+
value = (value << 8) | buffer[offset + 1 + i];
|
|
780
806
|
}
|
|
807
|
+
return { value, bytesRead: 1 + length };
|
|
781
808
|
}
|
|
782
809
|
|
|
783
810
|
/**
|
|
@@ -818,24 +845,20 @@ export class ITv2Session {
|
|
|
818
845
|
|
|
819
846
|
/**
|
|
820
847
|
* Build a 0x0800 Command Request wrapper
|
|
821
|
-
*
|
|
822
|
-
*
|
|
848
|
+
* Neohub wire format: [CommandSequence 1B][InnerCommand 2B BE][InnerPayload...]
|
|
849
|
+
* CommandSequence is provided by buildCommand as the appSequence byte - NOT duplicated here.
|
|
823
850
|
* @param {number} innerCommand - The command code to request (e.g., 0x0811)
|
|
824
851
|
* @param {Buffer} innerPayload - The payload for the inner command
|
|
825
852
|
* @returns {Buffer} - Complete packet ready to send
|
|
826
853
|
*/
|
|
827
854
|
buildCommandRequest(innerCommand, innerPayload = Buffer.alloc(0)) {
|
|
828
|
-
//
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const payload = Buffer.alloc(3 + innerPayload.length);
|
|
834
|
-
payload[0] = appSeq;
|
|
835
|
-
payload.writeUInt16LE(innerCommand, 1); // Command in little-endian
|
|
836
|
-
innerPayload.copy(payload, 3);
|
|
855
|
+
// Build payload: [InnerCommand 2B BE][InnerPayload]
|
|
856
|
+
// The CommandSequence is added by buildCommand as the appSequence byte
|
|
857
|
+
const payload = Buffer.alloc(2 + innerPayload.length);
|
|
858
|
+
payload.writeUInt16BE(innerCommand, 0);
|
|
859
|
+
innerPayload.copy(payload, 2);
|
|
837
860
|
|
|
838
|
-
this.log(`[Session] Building 0x0800 Command Request: innerCmd=0x${innerCommand.toString(16)}
|
|
861
|
+
this.log(`[Session] Building 0x0800 Command Request: innerCmd=0x${innerCommand.toString(16)}`);
|
|
839
862
|
this.log(`[Session] Inner payload: ${innerPayload.toString('hex')}`);
|
|
840
863
|
|
|
841
864
|
return this.buildCommand(CMD.STATUS_REQUEST, payload);
|
|
@@ -843,6 +866,24 @@ export class ITv2Session {
|
|
|
843
866
|
|
|
844
867
|
// ==================== Status Query Methods (0x0800 Wrapped) ====================
|
|
845
868
|
|
|
869
|
+
/**
|
|
870
|
+
* Build system capabilities request (0x0613 wrapped in 0x0800)
|
|
871
|
+
* Neohub queries this FIRST on connect to get max zones/partitions/etc.
|
|
872
|
+
*/
|
|
873
|
+
buildCapabilitiesRequest() {
|
|
874
|
+
// ConnectionSystemCapabilities: 6 CompactInteger fields, all default 0
|
|
875
|
+
// Each CompactInt(0) = [0x01, 0x00]
|
|
876
|
+
const payload = Buffer.from([
|
|
877
|
+
0x01, 0x00, // MaxZones = 0
|
|
878
|
+
0x01, 0x00, // MaxUsers = 0
|
|
879
|
+
0x01, 0x00, // MaxPartitions = 0
|
|
880
|
+
0x01, 0x00, // MaxFOBs = 0
|
|
881
|
+
0x01, 0x00, // MaxProxTags = 0
|
|
882
|
+
0x01, 0x00, // MaxOutputs = 0
|
|
883
|
+
]);
|
|
884
|
+
return this.buildCommandRequest(CMD.SYSTEM_CAPABILITIES, payload);
|
|
885
|
+
}
|
|
886
|
+
|
|
846
887
|
/**
|
|
847
888
|
* Build global status request (0x0810 wrapped in 0x0800)
|
|
848
889
|
*/
|
|
@@ -857,7 +898,8 @@ export class ITv2Session {
|
|
|
857
898
|
* @param {number} numZones - Number of zones to query (default 128)
|
|
858
899
|
*/
|
|
859
900
|
buildZoneStatusRequest(startZone = 0, numZones = 128) {
|
|
860
|
-
// 0x0811 payload: [
|
|
901
|
+
// 0x0811 request payload: [ZoneStart CompactInt][ZoneCount CompactInt]
|
|
902
|
+
// StatusSizeInBytes and ZoneStatusBytes are response-only fields
|
|
861
903
|
const zoneBytes = this.encodeVarBytes(startZone);
|
|
862
904
|
const countBytes = this.encodeVarBytes(numZones);
|
|
863
905
|
const payload = Buffer.concat([zoneBytes, countBytes]);
|
|
@@ -869,11 +911,12 @@ export class ITv2Session {
|
|
|
869
911
|
* @param {number} partition - Partition number (0 = all partitions)
|
|
870
912
|
* @param {number} numPartitions - Number of partitions to query (default 8)
|
|
871
913
|
*/
|
|
872
|
-
buildPartitionStatusRequest(partition =
|
|
873
|
-
// 0x0812 payload
|
|
914
|
+
buildPartitionStatusRequest(partition = 1) {
|
|
915
|
+
// 0x0812 payload (from neohub ModulePartitionStatus):
|
|
916
|
+
// [Partition CompactInt][PartitionStatus LeadingLengthArray(empty)]
|
|
874
917
|
const partBytes = this.encodeVarBytes(partition);
|
|
875
|
-
const
|
|
876
|
-
const payload = Buffer.concat([partBytes,
|
|
918
|
+
const emptyArray = Buffer.from([0x00]); // LeadingLengthArray with 0 length
|
|
919
|
+
const payload = Buffer.concat([partBytes, emptyArray]);
|
|
877
920
|
return this.buildCommandRequest(CMD.PARTITION_STATUS, payload);
|
|
878
921
|
}
|
|
879
922
|
|
|
@@ -964,16 +1007,22 @@ export class ITv2Session {
|
|
|
964
1007
|
|
|
965
1008
|
/**
|
|
966
1009
|
* Build POLL command for keepalive
|
|
1010
|
+
* ConnectionPoll is IMessageData (no CommandSequence) — just command code, no payload
|
|
967
1011
|
*/
|
|
968
1012
|
buildPoll() {
|
|
969
|
-
|
|
1013
|
+
this.localSequence = (this.localSequence + 1) & 0xFF;
|
|
1014
|
+
const message = buildHeader(this.localSequence, this.remoteSequence, CMD.POLL);
|
|
1015
|
+
return this.buildPacket(message);
|
|
970
1016
|
}
|
|
971
1017
|
|
|
972
1018
|
/**
|
|
973
1019
|
* Build END_SESSION command to gracefully close the session
|
|
1020
|
+
* No CommandSequence, no payload — just the command code
|
|
974
1021
|
*/
|
|
975
1022
|
buildEndSession() {
|
|
976
|
-
|
|
1023
|
+
this.localSequence = (this.localSequence + 1) & 0xFF;
|
|
1024
|
+
const message = buildHeader(this.localSequence, this.remoteSequence, CMD.END_SESSION);
|
|
1025
|
+
return this.buildPacket(message);
|
|
977
1026
|
}
|
|
978
1027
|
|
|
979
1028
|
/**
|
|
@@ -993,25 +1042,33 @@ export class ITv2Session {
|
|
|
993
1042
|
}
|
|
994
1043
|
|
|
995
1044
|
const partition = data[0];
|
|
996
|
-
|
|
1045
|
+
// From neohub ModulePartitionStatus PartitionStatus1/2/3:
|
|
1046
|
+
const status1 = data[1];
|
|
1047
|
+
const status2 = data.length > 2 ? data[2] : 0;
|
|
1048
|
+
const status3 = data.length > 3 ? data[3] : 0;
|
|
997
1049
|
|
|
998
|
-
|
|
1050
|
+
const armedValue = status1 & 0x1F;
|
|
999
1051
|
const status = {
|
|
1000
1052
|
partition,
|
|
1001
|
-
armed: !!(
|
|
1002
|
-
stayArmed:
|
|
1003
|
-
awayArmed:
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1053
|
+
armed: !!(status1 & 0x01),
|
|
1054
|
+
stayArmed: armedValue === 0x03,
|
|
1055
|
+
awayArmed: armedValue === 0x05,
|
|
1056
|
+
nightArmed: armedValue === 0x09,
|
|
1057
|
+
exitDelay: !!(status1 & 0x20),
|
|
1058
|
+
entryDelay: !!(status1 & 0x40),
|
|
1059
|
+
quickExit: !!(status1 & 0x80),
|
|
1060
|
+
alarm: !!(status2 & 0x01),
|
|
1061
|
+
trouble: !!(status2 & 0x02),
|
|
1062
|
+
bypass: !!(status2 & 0x04),
|
|
1063
|
+
alarmMemory: !!(status2 & 0x10),
|
|
1064
|
+
audibleBell: !!(status2 & 0x40),
|
|
1065
|
+
buzzer: !!(status2 & 0x80),
|
|
1066
|
+
firePreAlert: !!(status3 & 0x01),
|
|
1067
|
+
rawStatus: status1,
|
|
1010
1068
|
};
|
|
1011
1069
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
status.additionalData = data.slice(2);
|
|
1070
|
+
if (data.length > 4) {
|
|
1071
|
+
status.additionalData = data.slice(4);
|
|
1015
1072
|
}
|
|
1016
1073
|
|
|
1017
1074
|
return status;
|
|
@@ -1029,17 +1086,17 @@ export class ITv2Session {
|
|
|
1029
1086
|
const zone = data[0];
|
|
1030
1087
|
const statusByte = data[1];
|
|
1031
1088
|
|
|
1032
|
-
// Zone status bit meanings:
|
|
1089
|
+
// Zone status bit meanings (from neohub ModuleZoneStatus.ZoneStatusEnum):
|
|
1033
1090
|
const status = {
|
|
1034
1091
|
zone,
|
|
1035
|
-
open: !!(statusByte & 0x01),
|
|
1036
|
-
tamper: !!(statusByte & 0x02),
|
|
1037
|
-
fault: !!(statusByte & 0x04),
|
|
1038
|
-
lowBattery: !!(statusByte & 0x08),
|
|
1039
|
-
|
|
1040
|
-
alarm: !!(statusByte & 0x20),
|
|
1041
|
-
|
|
1042
|
-
|
|
1092
|
+
open: !!(statusByte & 0x01), // Bit 0: Open
|
|
1093
|
+
tamper: !!(statusByte & 0x02), // Bit 1: Tamper
|
|
1094
|
+
fault: !!(statusByte & 0x04), // Bit 2: Fault
|
|
1095
|
+
lowBattery: !!(statusByte & 0x08), // Bit 3: Low Battery
|
|
1096
|
+
delinquency: !!(statusByte & 0x10), // Bit 4: Delinquency
|
|
1097
|
+
alarm: !!(statusByte & 0x20), // Bit 5: Alarm
|
|
1098
|
+
alarmInMemory: !!(statusByte & 0x40), // Bit 6: Alarm Memory
|
|
1099
|
+
bypassed: !!(statusByte & 0x80), // Bit 7: Bypass
|
|
1043
1100
|
rawStatus: statusByte,
|
|
1044
1101
|
};
|
|
1045
1102
|
|
|
@@ -1072,11 +1129,12 @@ export class ITv2Session {
|
|
|
1072
1129
|
/**
|
|
1073
1130
|
* Build PARTITION_ARM command
|
|
1074
1131
|
* @param {number} partition - Partition number (1-8)
|
|
1075
|
-
* @param {number} armMode - Arming mode:
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1132
|
+
* @param {number} armMode - Arming mode (use ITv2Session.ARM_MODE):
|
|
1133
|
+
* 1 = Stay (perimeter only)
|
|
1134
|
+
* 2 = Away (full arm)
|
|
1135
|
+
* 3 = No Entry Delay
|
|
1136
|
+
* 4 = Night
|
|
1137
|
+
* 5 = Quick Arm
|
|
1080
1138
|
* @param {string} accessCode - Master/user code (e.g., "5555")
|
|
1081
1139
|
*/
|
|
1082
1140
|
buildPartitionArm(partition, armMode, accessCode) {
|
package/src/response-parsers.js
CHANGED
|
@@ -46,41 +46,50 @@ export function formatZoneStatus(zone) {
|
|
|
46
46
|
* Status is encoded across multiple bytes using bit fields
|
|
47
47
|
*/
|
|
48
48
|
export function parsePartitionStatus(statusBytes) {
|
|
49
|
-
if (!statusBytes || statusBytes.length <
|
|
49
|
+
if (!statusBytes || statusBytes.length < 1) {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return byteOffset < statusBytes.length && (statusBytes[byteOffset] & bitMask) !== 0;
|
|
58
|
-
};
|
|
53
|
+
// From neohub ModulePartitionStatus PartitionStatus1/2/3 enums:
|
|
54
|
+
const status1 = statusBytes[0] || 0;
|
|
55
|
+
const status2 = statusBytes.length > 1 ? statusBytes[1] : 0;
|
|
56
|
+
const status3 = statusBytes.length > 2 ? statusBytes[2] : 0;
|
|
59
57
|
|
|
60
|
-
//
|
|
61
|
-
|
|
58
|
+
// Status1 byte: armed state is a composite value, not individual bits
|
|
59
|
+
// Armed=0x01, StayArmed=0x03, ArmedAway=0x05, NightArmed=0x09,
|
|
60
|
+
// ArmedNoEntry=0x11, ExitDelay=0x20, EntryDelay=0x40, QuickExit=0x80
|
|
61
|
+
const armedValue = status1 & 0x1F; // Lower 5 bits for armed state
|
|
62
62
|
const armedStates = {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
0x00: 'Disarmed',
|
|
64
|
+
0x01: 'Armed',
|
|
65
|
+
0x03: 'Armed Stay',
|
|
66
|
+
0x05: 'Armed Away',
|
|
67
|
+
0x09: 'Armed Night',
|
|
68
|
+
0x11: 'Armed (No Entry Delay)',
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
return {
|
|
72
72
|
partitionNumber: null, // Set by caller
|
|
73
|
-
armedState: armedStates[armedValue] || `Unknown (${armedValue})`,
|
|
73
|
+
armedState: armedStates[armedValue] || `Unknown (0x${armedValue.toString(16)})`,
|
|
74
74
|
armedValue: armedValue,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
armed: !!(status1 & 0x01),
|
|
76
|
+
stayArmed: armedValue === 0x03,
|
|
77
|
+
awayArmed: armedValue === 0x05,
|
|
78
|
+
nightArmed: armedValue === 0x09,
|
|
79
|
+
exitDelay: !!(status1 & 0x20),
|
|
80
|
+
entryDelay: !!(status1 & 0x40),
|
|
81
|
+
quickExit: !!(status1 & 0x80),
|
|
82
|
+
// Status2 byte flags
|
|
83
|
+
alarm: !!(status2 & 0x01),
|
|
84
|
+
trouble: !!(status2 & 0x02),
|
|
85
|
+
bypass: !!(status2 & 0x04),
|
|
86
|
+
programmingMode: !!(status2 & 0x08),
|
|
87
|
+
alarmMemory: !!(status2 & 0x10),
|
|
88
|
+
doorChime: !!(status2 & 0x20),
|
|
89
|
+
audibleBell: !!(status2 & 0x40),
|
|
90
|
+
buzzer: !!(status2 & 0x80),
|
|
91
|
+
// Status3 byte flags
|
|
92
|
+
firePreAlert: !!(status3 & 0x01),
|
|
84
93
|
rawBytes: Buffer.from(statusBytes).toString('hex')
|
|
85
94
|
};
|
|
86
95
|
}
|