Skip to content

Commit ce12e1c

Browse files
committed
Release 3.0
2 parents 3cf3653 + 5635272 commit ce12e1c

File tree

68 files changed

+10088
-1362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+10088
-1362
lines changed

README.md

Lines changed: 89 additions & 38 deletions
Large diffs are not rendered by default.

examples/Arduino/HomeAssistant-MQTT/HomeAssistant-MQTT.ino

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* HomeAssistant-MQTT 1.4 (Arduino with Ethernet)
2+
* HomeAssistant-MQTT 1.5 (Arduino with Ethernet)
33
*
44
* Processes the security system status and allows for control using Home Assistant via MQTT.
55
*
@@ -156,6 +156,7 @@ entity: alarm_control_panel.security_partition_1
156156
* Closed: "0"
157157
*
158158
* Release notes
159+
* 1.5 - Added DSC Classic series support
159160
* 1.4 - Added PGM outputs 1-14 status
160161
* 1.2 - Added night arm (arming with no entry delay)
161162
* Added status update on initial MQTT connection and reconnection
@@ -177,7 +178,14 @@ entity: alarm_control_panel.security_partition_1
177178
* DSC Green ---- 15k ohm resistor ---|
178179
* +--- 10k ohm resistor --- Ground
179180
*
180-
* Virtual keypad (optional):
181+
* Classic series only, PGM configured for PC-16 output:
182+
* DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+)
183+
* |
184+
* | +--- dscPC16Pin (Arduino Uno: 2-12)
185+
* +-- 15k ohm resistor ---|
186+
* +--- 10k ohm resistor --- Ground
187+
*
188+
* Virtual keypad (optional):
181189
* DSC Green ---- NPN collector --\
182190
* |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12)
183191
* Ground --- NPN emitter --/
@@ -193,24 +201,27 @@ entity: alarm_control_panel.security_partition_1
193201
* This example code is in the public domain.
194202
*/
195203

204+
// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md)
205+
//#define dscClassicSeries
206+
196207
#include <SPI.h>
197208
#include <Ethernet.h>
198209
#include <PubSubClient.h>
199210
#include <dscKeybusInterface.h>
200211

201212
// Settings
202213
byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network
203-
const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration.
214+
const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration.
204215
const char* mqttServer = ""; // MQTT server domain name or IP address
205216
const int mqttPort = 1883; // MQTT server port
206217
const char* mqttUsername = ""; // Optional, leave blank if not required
207218
const char* mqttPassword = ""; // Optional, leave blank if not required
208219

209220
// MQTT topics - match to Home Assistant's configuration.yaml
210221
const char* mqttClientName = "dscKeybusInterface";
211-
const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition8
212-
const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone64
213-
const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire8
222+
const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition4
223+
const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone32
224+
const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire4
214225
const char* mqttPgmTopic = "dsc/Get/PGM"; // Sends PGM status per PGM: dsc/Get/PGM1 ... dsc/Get/PGM14
215226
const char* mqttTroubleTopic = "dsc/Get/Trouble"; // Sends trouble status
216227
const char* mqttStatusTopic = "dsc/Status"; // Sends online/offline status
@@ -221,11 +232,16 @@ const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to w
221232
// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the
222233
// virtual keypad.
223234
#define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3
235+
#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12
224236
#define dscReadPin 5 // Arduino Uno: 2-12
225237
#define dscWritePin 6 // Arduino Uno: 2-12
226238

227239
// Initialize components
240+
#ifndef dscClassicSeries
228241
dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin);
242+
#else
243+
dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode);
244+
#endif
229245
EthernetClient ipClient;
230246
PubSubClient mqtt(mqttServer, mqttPort, ipClient);
231247
unsigned long mqttPreviousTime;
@@ -266,7 +282,7 @@ void loop() {
266282
dsc.statusChanged = false; // Reset the status tracking flag
267283

268284
// If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call
269-
// loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h
285+
// loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h
270286
if (dsc.bufferOverflow) {
271287
Serial.println(F("Keybus buffer overflow"));
272288
dsc.bufferOverflow = false;
@@ -279,7 +295,7 @@ void loop() {
279295
else mqtt.publish(mqttStatusTopic, mqttLwtMessage, true);
280296
}
281297

282-
// Sends the access code when needed by the panel for arming
298+
// Sends the access code when needed by the panel for arming or command outputs
283299
if (dsc.accessCodePrompt) {
284300
dsc.accessCodePrompt = false;
285301
dsc.write(accessCode);

examples/Arduino/Homebridge-MQTT/Homebridge-MQTT.ino

Lines changed: 86 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Homebridge-MQTT 1.4 (Arduino with Ethernet)
2+
* Homebridge-MQTT 1.7 (Arduino with Ethernet)
33
*
44
* Processes the security system status and allows for control using Apple HomeKit, including the iOS Home app,
55
* Siri, and Google Home. This uses MQTT to interface with Homebridge and the homebridge-mqttthing plugin for
@@ -146,6 +146,9 @@
146146
* Closed: "0"
147147
*
148148
* Release notes:
149+
* 1.7 - Fixed exit delay states while multiple partitions are arming
150+
* 1.6 - Added DSC Classic series support
151+
* 1.5 - Support switching armed modes while armed
149152
* 1.4 - Added PGM outputs 1-14 status
150153
* Added notes on Google Home integration
151154
* 1.2 - Resolved handling HomeKit target states
@@ -168,7 +171,14 @@
168171
* DSC Green ---- 15k ohm resistor ---|
169172
* +--- 10k ohm resistor --- Ground
170173
*
171-
* Virtual keypad (optional):
174+
* Classic series only, PGM configured for PC-16 output:
175+
* DSC PGM ---+-- 1k ohm resistor --- DSC Aux(+)
176+
* |
177+
* | +--- dscPC16Pin (Arduino Uno: 2-12)
178+
* +-- 15k ohm resistor ---|
179+
* +--- 10k ohm resistor --- Ground
180+
*
181+
* Virtual keypad (optional):
172182
* DSC Green ---- NPN collector --\
173183
* |-- NPN base --- 1k ohm resistor --- dscWritePin (Arduino Uno: 2-12)
174184
* Ground --- NPN emitter --/
@@ -184,39 +194,47 @@
184194
* This example code is in the public domain.
185195
*/
186196

197+
// DSC Classic series: uncomment for PC1500/PC1550 support (requires PC16-OUT configuration per README.md)
198+
//#define dscClassicSeries
199+
187200
#include <SPI.h>
188201
#include <Ethernet.h>
189202
#include <PubSubClient.h>
190203
#include <dscKeybusInterface.h>
191204

192205
// Settings
193206
byte mac[] = { 0xAA, 0x61, 0x0A, 0x00, 0x00, 0x01 }; // Set a MAC address unique to the local network
194-
const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm based on panel configuration.
207+
const char* accessCode = ""; // An access code is required to disarm/night arm and may be required to arm or enable command outputs based on panel configuration.
195208
const char* mqttServer = ""; // MQTT server domain name or IP address
196209
const int mqttPort = 1883; // MQTT server port
197210
const char* mqttUsername = ""; // Optional, leave blank if not required
198211
const char* mqttPassword = ""; // Optional, leave blank if not required
199212

200213
// MQTT topics - match to Homebridge's config.json
201214
const char* mqttClientName = "dscKeybusInterface";
202-
const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition8
203-
const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone64
204-
const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire8
215+
const char* mqttPartitionTopic = "dsc/Get/Partition"; // Sends armed and alarm status per partition: dsc/Get/Partition1 ... dsc/Get/Partition4
216+
const char* mqttZoneTopic = "dsc/Get/Zone"; // Sends zone status per zone: dsc/Get/Zone1 ... dsc/Get/Zone32
217+
const char* mqttFireTopic = "dsc/Get/Fire"; // Sends fire status per partition: dsc/Get/Fire1 ... dsc/Get/Fire4
205218
const char* mqttPgmTopic = "dsc/Get/PGM"; // Sends PGM status per PGM: dsc/Get/PGM1 ... dsc/Get/PGM14
206219
const char* mqttSubscribeTopic = "dsc/Set"; // Receives messages to write to the panel
207220

208221
// Configures the Keybus interface with the specified pins - dscWritePin is optional, leaving it out disables the
209222
// virtual keypad.
210223
#define dscClockPin 3 // Arduino Uno hardware interrupt pin: 2,3
224+
#define dscPC16Pin 4 // DSC Classic Series only, Arduino Uno: 2-12
211225
#define dscReadPin 5 // Arduino Uno: 2-12
212226
#define dscWritePin 6 // Arduino Uno: 2-12
213227

214228
// Initialize components
229+
#ifndef dscClassicSeries
215230
dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin);
231+
#else
232+
dscClassicInterface dsc(dscClockPin, dscReadPin, dscPC16Pin, dscWritePin, accessCode);
233+
#endif
216234
EthernetClient ipClient;
217235
PubSubClient mqtt(mqttServer, mqttPort, ipClient);
218236
unsigned long mqttPreviousTime;
219-
char exitState;
237+
char exitState[4];
220238

221239

222240
void setup() {
@@ -254,13 +272,13 @@ void loop() {
254272
dsc.statusChanged = false; // Reset the status tracking flag
255273

256274
// If the Keybus data buffer is exceeded, the sketch is too busy to process all Keybus commands. Call
257-
// loop() more often, or increase dscBufferSize in the library: src/dscKeybusInterface.h
275+
// loop() more often, or increase dscBufferSize in the library: src/dscKeybus.h or src/dscClassic.h
258276
if (dsc.bufferOverflow) {
259277
Serial.println(F("Keybus buffer overflow"));
260278
dsc.bufferOverflow = false;
261279
}
262280

263-
// Sends the access code when needed by the panel for arming
281+
// Sends the access code when needed by the panel for arming or command outputs
264282
if (dsc.accessCodePrompt) {
265283
dsc.accessCodePrompt = false;
266284
dsc.write(accessCode);
@@ -275,7 +293,7 @@ void loop() {
275293
// Publishes armed/disarmed status
276294
if (dsc.armedChanged[partition]) {
277295
if (dsc.armed[partition]) {
278-
exitState = 0;
296+
exitState[partition] = 0;
279297

280298
// Night armed away
281299
if (dsc.armedAway[partition] && dsc.noEntryDelay[partition]) {
@@ -310,21 +328,21 @@ void loop() {
310328
if (dsc.exitDelay[partition]) {
311329

312330
// Sets the arming target state if the panel is armed externally
313-
if (exitState == 0 || dsc.exitStateChanged[partition]) {
331+
if (exitState[partition] == 0 || dsc.exitStateChanged[partition]) {
314332
dsc.exitStateChanged[partition] = 0;
315333
switch (dsc.exitState[partition]) {
316334
case DSC_EXIT_STAY: {
317-
exitState = 'S';
335+
exitState[partition] = 'S';
318336
publishState(mqttPartitionTopic, partition, "S", 0);
319337
break;
320338
}
321339
case DSC_EXIT_AWAY: {
322-
exitState = 'A';
340+
exitState[partition] = 'A';
323341
publishState(mqttPartitionTopic, partition, "A", 0);
324342
break;
325343
}
326344
case DSC_EXIT_NO_ENTRY_DELAY: {
327-
exitState = 'N';
345+
exitState[partition] = 'N';
328346
publishState(mqttPartitionTopic, partition, "N", 0);
329347
break;
330348
}
@@ -334,7 +352,7 @@ void loop() {
334352

335353
// Disarmed during exit delay
336354
else if (!dsc.armed[partition]) {
337-
exitState = 0;
355+
exitState[partition] = 0;
338356
publishState(mqttPartitionTopic, partition, "D", "D");
339357
}
340358
}
@@ -439,18 +457,63 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
439457
payloadIndex = 1;
440458
}
441459

442-
// Resets the HomeKit target state if attempting to change the armed mode while armed or not ready
460+
// Sets night arm (no entry delay) while armed
461+
if (payload[payloadIndex] == 'N' && dsc.armed[partition]) {
462+
dsc.writePartition = partition + 1; // Sets writes to the partition number
463+
dsc.write('n'); // Keypad no entry delay
464+
publishState(mqttPartitionTopic, partition, "N", 0);
465+
exitState[partition] = 'N';
466+
return;
467+
}
468+
469+
// Disables night arm while armed stay
470+
if (payload[payloadIndex] == 'S' && dsc.armedStay[partition] && dsc.noEntryDelay[partition]) {
471+
dsc.writePartition = partition + 1; // Sets writes to the partition number
472+
dsc.write('n'); // Keypad no entry delay
473+
publishState(mqttPartitionTopic, partition, "S", 0);
474+
exitState[partition] = 'S';
475+
return;
476+
}
477+
478+
// Disables night arm while armed away
479+
if (payload[payloadIndex] == 'A' && dsc.armedAway[partition] && dsc.noEntryDelay[partition]) {
480+
dsc.writePartition = partition + 1; // Sets writes to the partition number
481+
dsc.write('n'); // Keypad no entry delay
482+
publishState(mqttPartitionTopic, partition, "A", 0);
483+
exitState[partition] = 'A';
484+
return;
485+
}
486+
487+
// Changes from arm away to arm stay after the exit delay
488+
if (payload[payloadIndex] == 'S' && dsc.armedAway[partition]) {
489+
dsc.writePartition = partition + 1; // Sets writes to the partition number
490+
dsc.write("s");
491+
publishState(mqttPartitionTopic, partition, "S", 0);
492+
exitState[partition] = 'S';
493+
return;
494+
}
495+
496+
// Changes from arm stay to arm away after the exit delay
497+
if (payload[payloadIndex] == 'A' && dsc.armedStay[partition]) {
498+
dsc.writePartition = partition + 1; // Sets writes to the partition number
499+
dsc.write("w");
500+
publishState(mqttPartitionTopic, partition, "A", 0);
501+
exitState[partition] = 'A';
502+
return;
503+
}
504+
505+
// Resets the HomeKit target state if attempting to change the armed mode while not ready
443506
if (payload[payloadIndex] != 'D' && !dsc.ready[partition]) {
444507
dsc.armedChanged[partition] = true;
445508
dsc.statusChanged = true;
446509
return;
447510
}
448511

449512
// Resets the HomeKit target state if attempting to change the arming mode during the exit delay
450-
if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState != 0) {
451-
if (exitState == 'S') publishState(mqttPartitionTopic, partition, "S", 0);
452-
else if (exitState == 'A') publishState(mqttPartitionTopic, partition, "A", 0);
453-
else if (exitState == 'N') publishState(mqttPartitionTopic, partition, "N", 0);
513+
if (payload[payloadIndex] != 'D' && dsc.exitDelay[partition] && exitState[partition] != 0) {
514+
if (exitState[partition] == 'S') publishState(mqttPartitionTopic, partition, "S", 0);
515+
else if (exitState[partition] == 'A') publishState(mqttPartitionTopic, partition, "A", 0);
516+
else if (exitState[partition] == 'N') publishState(mqttPartitionTopic, partition, "N", 0);
454517
}
455518

456519

@@ -459,7 +522,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
459522
dsc.writePartition = partition + 1; // Sets writes to the partition number
460523
dsc.write('s'); // Keypad stay arm
461524
publishState(mqttPartitionTopic, partition, "S", 0);
462-
exitState = 'S';
525+
exitState[partition] = 'S';
463526
return;
464527
}
465528

@@ -468,7 +531,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
468531
dsc.writePartition = partition + 1; // Sets writes to the partition number
469532
dsc.write('w'); // Keypad away arm
470533
publishState(mqttPartitionTopic, partition, "A", 0);
471-
exitState = 'A';
534+
exitState[partition] = 'A';
472535
return;
473536
}
474537

@@ -477,7 +540,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
477540
dsc.writePartition = partition + 1; // Sets writes to the partition number
478541
dsc.write('n'); // Keypad arm with no entry delay
479542
publishState(mqttPartitionTopic, partition, "N", 0);
480-
exitState = 'N';
543+
exitState[partition] = 'N';
481544
return;
482545
}
483546

0 commit comments

Comments
 (0)