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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. 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.6",
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]