dsc-itv2-client 2.0.6 → 2.0.7
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 +72 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsc-itv2-client",
|
|
3
3
|
"author": "fajitacat",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.7",
|
|
5
5
|
"description": "Reverse engineered DSC ITV2 Protocol Client Library for TL280/TL280E - Monitor and control DSC Neo alarm panels with real-time zone/partition status, arming, and trouble detail",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"type": "module",
|
package/src/ITV2Client.js
CHANGED
|
@@ -758,6 +758,9 @@ export class ITV2Client extends EventEmitter {
|
|
|
758
758
|
case CMD.SINGLE_ZONE_BYPASS_STATUS:
|
|
759
759
|
this._handleZoneBypassNotification(parsed);
|
|
760
760
|
break;
|
|
761
|
+
case CMD.ZONE_ALARM_STATUS:
|
|
762
|
+
this._handleZoneAlarmStatus(parsed);
|
|
763
|
+
break;
|
|
761
764
|
case CMD.MULTIPLE_MESSAGE:
|
|
762
765
|
this._handleMultipleMessagePacket(parsed);
|
|
763
766
|
break;
|
|
@@ -1480,6 +1483,75 @@ export class ITV2Client extends EventEmitter {
|
|
|
1480
1483
|
this._ack();
|
|
1481
1484
|
}
|
|
1482
1485
|
|
|
1486
|
+
_handleZoneAlarmStatus(parsed) {
|
|
1487
|
+
// 0x0840 ModuleStatus_Zone_Alarm_Status (from decompiled SDK):
|
|
1488
|
+
// [Partition CompactInt][Count CompactInt][repeated: Zone CompactInt, AlarmType 1B, AlarmState 1B]
|
|
1489
|
+
//
|
|
1490
|
+
// AlarmType: 1=Unknown, 2=Burglary, 3=24HR_Supervisory, 4=Fire, 5=Fire_Supervisory,
|
|
1491
|
+
// 6=CO, 7=Gas, 8=HighTemp, 9=LowTemp, 10=Medical, 11=Panic, 12=Waterflow,
|
|
1492
|
+
// 13=Water_Leakage, 14=Pendant, 15=Tamper, 16=RF_Jam, 17=Hardware_Fault,
|
|
1493
|
+
// 18=Duress, 19=Personal_Emergency, 20=Holdup, 21=Sprinkler
|
|
1494
|
+
// AlarmState: bit 0 = 0 → NotInAlarm, bit 0 = 1 → InAlarm
|
|
1495
|
+
const fullPayload = this._reconstructPayload(parsed);
|
|
1496
|
+
if (!fullPayload || fullPayload.length < 6) {
|
|
1497
|
+
this._ack();
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
const ALARM_TYPES = {
|
|
1502
|
+
1: 'Unknown', 2: 'Burglary', 3: '24HR_Supervisory', 4: 'Fire',
|
|
1503
|
+
5: 'Fire_Supervisory', 6: 'CO', 7: 'Gas', 8: 'HighTemp',
|
|
1504
|
+
9: 'LowTemp', 10: 'Medical', 11: 'Panic', 12: 'Waterflow',
|
|
1505
|
+
13: 'Water_Leakage', 14: 'Pendant', 15: 'Tamper', 16: 'RF_Jam',
|
|
1506
|
+
17: 'Hardware_Fault', 18: 'Duress', 19: 'Personal_Emergency',
|
|
1507
|
+
20: 'Holdup', 21: 'Sprinkler',
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
try {
|
|
1511
|
+
let offset = 0;
|
|
1512
|
+
const partition = ITv2Session.decodeVarBytes(fullPayload, offset);
|
|
1513
|
+
offset += partition.bytesRead;
|
|
1514
|
+
|
|
1515
|
+
const count = ITv2Session.decodeVarBytes(fullPayload, offset);
|
|
1516
|
+
offset += count.bytesRead;
|
|
1517
|
+
|
|
1518
|
+
while (offset < fullPayload.length) {
|
|
1519
|
+
const zone = ITv2Session.decodeVarBytes(fullPayload, offset);
|
|
1520
|
+
offset += zone.bytesRead;
|
|
1521
|
+
if (offset >= fullPayload.length) break;
|
|
1522
|
+
const alarmType = fullPayload[offset++];
|
|
1523
|
+
if (offset >= fullPayload.length) break;
|
|
1524
|
+
const alarmState = fullPayload[offset++];
|
|
1525
|
+
|
|
1526
|
+
const inAlarm = !!(alarmState & 0x01);
|
|
1527
|
+
const alarmTypeName = ALARM_TYPES[alarmType] || `Unknown(${alarmType})`;
|
|
1528
|
+
|
|
1529
|
+
this._logMinimal(`[Zone ${zone.value}] Alarm: ${inAlarm ? 'ACTIVE' : 'CLEARED'} (${alarmTypeName}, partition ${partition.value})`);
|
|
1530
|
+
|
|
1531
|
+
const alarmData = {
|
|
1532
|
+
zone: zone.value,
|
|
1533
|
+
partition: partition.value,
|
|
1534
|
+
alarmType,
|
|
1535
|
+
alarmTypeName,
|
|
1536
|
+
inAlarm,
|
|
1537
|
+
timestamp: new Date(),
|
|
1538
|
+
};
|
|
1539
|
+
|
|
1540
|
+
if (inAlarm) {
|
|
1541
|
+
this.eventHandler.handleZoneAlarm(zone.value, alarmTypeName);
|
|
1542
|
+
this.emit('zone:alarm', alarmData);
|
|
1543
|
+
this.emit('partition:alarm', alarmData);
|
|
1544
|
+
} else {
|
|
1545
|
+
this.emit('zone:alarm:restored', alarmData);
|
|
1546
|
+
this.emit('partition:alarm:restored', alarmData);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
} catch (e) {
|
|
1550
|
+
this._log(`[Zone Alarm Status] Parse error: ${e.message}`);
|
|
1551
|
+
}
|
|
1552
|
+
this._ack();
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1483
1555
|
_handleExitDelayNotification(parsed) {
|
|
1484
1556
|
// 0x0230 NotificationExitDelay (from neohub):
|
|
1485
1557
|
// [CompactInt:Partition][DelayFlags:1B][CompactInt:DurationInSeconds]
|