dsc-itv2-client
v2.0.10
Published
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
Maintainers
Readme
DSC ITV2 Client Library
Reverse engineered, completely unofficial Node.js library for communicating with DSC alarm panels (Neo series) using the ITV2 protocol over TL280/TL280E communicators. Zero external dependencies - uses only Node.js built-ins.
Not affiliated with DSC, Johnson Controls, or Tyco in any way. Use at your own risk.
Features
- Real-Time Zone Monitoring - Open/close, tamper, alarm, bypass events via push notifications
- Partition Control - Arm stay/away/night, disarm with real-time confirmation
- Async Status Querying - Query zone status, partition status, system capabilities on demand
- Trouble Detail Parsing - Human-readable trouble notifications from official SDK enums
- Encrypted Communication - AES-128-ECB with automatic Type 1/Type 2 key exchange
- Event-Driven Architecture - Subscribe to events with EventEmitter
- Automatic Session Setup - Capabilities query, initial status pull, heartbeat keepalive
Quick Start
Installation
npm install dsc-itv2-clientBasic Usage
import { ITV2Client } from 'dsc-itv2-client';
const client = new ITV2Client({
integrationId: '250228754543', // Section [851][422]
accessCode: '28754543', // Section [851][423]
masterCode: '5555', // User code for arm/disarm
port: 3072, // TCP port (panel connects to us)
});
client.on('session:established', () => console.log('Connected!'));
client.on('zone:open', (zone) => console.log(`Zone ${zone} opened`));
client.on('zone:closed', (zone) => console.log(`Zone ${zone} closed`));
client.on('partition:arming', ({ partition, modeName }) => {
console.log(`Partition ${partition}: ${modeName}`);
});
// Status is automatically queried on connect
client.on('status:ready', async ({ zones, partition }) => {
console.log(`${zones.length} zones loaded`);
// Query on demand anytime
const current = await client.queryZoneStatus();
const part = await client.queryPartitionStatus(1);
});
await client.start();Arm/Disarm
client.armStay(1, '5555'); // Arm partition 1 in stay mode
client.armAway(1, '5555'); // Arm partition 1 in away mode
client.armNight(1, '5555'); // Arm partition 1 in night mode
client.disarm(1, '5555'); // Disarm partition 1Examples
npm run example:basic # Zone monitoring
npm run example:control # Arm/disarm demo
npm run example:cli # Interactive CLI with queryingAPI Reference
Constructor
const client = new ITV2Client(options);| Option | Type | Default | Description |
|--------|------|---------|-------------|
| integrationId | string | required | 12-digit panel integration ID (section [851][422]) |
| accessCode | string | required | 8-digit code (section [851][423]) or 32-hex (section [851][700]) |
| masterCode | string | '5555' | User code for arm/disarm operations |
| port | number | 3072 | TCP port to listen on (panel connects to us) |
| logLevel | string | 'minimal' | 'silent', 'minimal', or 'verbose' |
| encryptionType | number | null | null = auto-detect, 1 = Type 1, 2 = Type 2 |
Methods
| Method | Returns | Description |
|--------|---------|-------------|
| start() | Promise | Start TCP server, wait for panel connection |
| stop() | Promise | Send END_SESSION, close connection |
| armStay(partition, code) | void | Arm partition in stay mode |
| armAway(partition, code) | void | Arm partition in away mode |
| armNight(partition, code) | void | Arm partition in night mode |
| armNoEntryDelay(partition, code) | void | Arm with no entry delay |
| disarm(partition, code) | void | Disarm partition |
| queryCapabilities() | Promise<object> | Query system capabilities (max zones, partitions) |
| queryZoneStatus() | Promise<Array> | Query all zone statuses |
| queryPartitionStatus(n) | Promise<object> | Query partition status |
| queryTroubleStatus() | Promise<Array> | Query system troubles |
| getZones() | Array | Get cached zone states |
| getPartitions() | Array | Get cached partition states |
Events
Session
| Event | Payload | Description |
|-------|---------|-------------|
| listening | { address, port } | TCP server started |
| session:connecting | - | Panel initiated connection |
| session:established | { encryptionType, sendKey, recvKey } | Encrypted session active |
| session:ready | - | Initial notification burst complete |
| status:ready | { zones, partition, capabilities } | Initial status pull done |
| session:closed | - | Session ended |
Zone
| Event | Payload | Description |
|-------|---------|-------------|
| zone:open | zoneNumber | Zone opened |
| zone:closed | zoneNumber | Zone closed |
| zone:status | zoneNumber, statusObject | Full zone status update |
Zone status object:
{ open, tamper, fault, lowBattery, delinquency, alarm, alarmInMemory, bypassed, rawStatus }Partition
| Event | Payload | Description |
|-------|---------|-------------|
| partition:arming | { partition, armMode, modeName, method } | Arming state changed |
| partition:ready | { partition, isReady } | Partition ready state |
| partition:trouble | { partition, troubleFlags } | Partition trouble |
Trouble
| Event | Payload | Description |
|-------|---------|-------------|
| trouble:detail | Array<TroubleRecord> | Detailed trouble notification |
Trouble record:
{ deviceType, deviceTypeName, deviceNumber, troubleType, troubleTypeName, troubleState, troubleStateName }Error
| Event | Payload | Description |
|-------|---------|-------------|
| error | Error | General error |
| command:error | { failedCommand, nackCode, message } | Panel NACK response |
Panel Configuration
TL280/TL280E Setup
Reference: Johnson Controls TL280 Configuration Guide
1. Enable Alternate Communicator
Section 382 - Enable option 5 (Alternate Communicator). Use star key to toggle. Hash back once.
Section 300 - Receiver 1: set to Alternate Com Receiver 1. Hash back once.
Section 380 - Ensure comms are enabled (should be on by default). Hash once.
Section 310 - Enter a 4-digit system account code. Scroll right and enter the same matching code in the partition account code. Hash back once.
2. Configure Communicator (Section 851)
Network Settings:
- For static IP: enter sections 001, 002, 003, 007, and 008
- For DHCP: skip to section 005
851 > 005 - Enable option 3 (option 6 should already be enabled). Hash once.
851 > 010 - Enable video verification (good practice even if not using it).
851 > 100 - Turn on option 2.
851 > 104 - Verify it reads 0BF5.
3. Integration Settings
851 > 425 - Enable options 3 and 5.
851 > 426 - Turn on option 3 (required for real-time notifications).
851 > 428 - Enter your server IP address (the machine running this library).
851 > 422 - Read the 12-digit Integration ID (two groups of 6 digits, written in blocks of 3). Scroll 6 times right for the second group. This is your integrationId.
851 > 423 - Read the 8-digit Access Code. This is your accessCode.
851 > 101 - Enter a unique identifier.
4. Reboot Communicator
851 > 999 - Enter 55 to reboot the communicator. Hash out 3 times. A trouble condition on the keypad is normal during reboot.
Network Requirements
- TCP port 3072 must be reachable from the panel to your server
- Panel and server must be on the same network (or routable)
- Static IP recommended for the server
- The panel initiates the TCP connection to your server (outbound from panel)
Finding Your Credentials
If the panel is already configured, enter installer programming (*8 + installer code) and read:
- Section [851][422] — Integration ID (12 digits)
- Section [851][423] — Access Code (8 digits, Type 1)
- Section [851][700] — Integration Access Code (32 hex, Type 2)
Configuration
Use environment variables:
INTEGRATION_ID=250228754543 \
ACCESS_CODE=28754543 \
MASTER_CODE=5555 \
PORT=3072 \
LOG_LEVEL=minimal \
npm run example:cliTroubleshooting
Panel won't connect
- Verify server IP is set correctly in section [851][428]
- Ensure TCP port 3072 is open in firewall
- Check panel and server are on the same network
- Reboot communicator: section [851][999], enter 55
Session won't establish
- Verify
integrationIdmatches section [851][422] - Verify
accessCodematches section [851][423] - Enable
logLevel: 'verbose'for full protocol details
Zone queries fail
- System capabilities must be queried first (automatic on connect)
- Zone count must match panel's actual zone count (automatic when using
queryZoneStatus())
Arm/disarm not working
- Verify
masterCodeis a valid user code on the panel - Check partition number is correct (usually 1)
- Panel sends confirmation via
partition:armingevent
Tested On
- DSC Neo Panel (HS2032/HS2064/HS2128)
- TL280/TL280E Communicator
- Type 1 encryption (most common)
License
MIT
