Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
jimtng committed Jun 23, 2019
2 parents 2aac898 + 26cab6b commit 9509a40
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 88 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@
All notable changes to this project will be documented in this file.

## [Unreleased]
### Added
- MQTT Topic `panel-time` to get/set the alarm panel time
- MQTT Topic `partition-N-access-code` the access code is published whenever the alarm was armed/disarmed

### Changed
- Updated library dependency to use Homie-esp8266 v3.0.1
- Updated library dependency to use dscKeybusInterface v1.3 (develop branch)

### Fixed
- Writing to `keypad` topic no longer causes an unexpected behaviour due to a bug in the dscKeybusInterface library.

## [1.0.1] 2019-06-05
### Added
- MQTT Topic `maintenance` - See README.md. Added the ability to stop and start the dsc interface, needed to avoid crashing during config update
- MQTT Topic `maintenance` - See (README.md). Added the ability to stop and start the dsc interface, needed to avoid crashing during config update

### Removed
- MQTT Topic `reboot` has been removed. Its functionality has been incorporated into the new `maintenance` topic
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,24 @@ homie/device-id/alarm/
- `trouble`
- `power-trouble`
- `battery-trouble`
- `fire-alarm-keypad`
- `fire-alarm-keypad`
- `aux-alarm-keypad`
- `panic-alarm-keypad`
- `panel-time` provides the date/time stored in the alarm system formatted as YYYY-MM-DD HH:mm

### Partitions:
- `partition-1-away`: 0 (disarmed) | 1 (armed away)
- `partition-1-stay`: 0 (disarmed) | 1 (armed stay)
- `partition-1-alarm`: 0 (no alarm) | 1 (triggered)
- `partition-1-fire`: 0 (no alarm) | 1 (fire alarm)
- `partition-1-access-code`: The access code used to arm/disarm
- ...
- `partition-N-xxxx` as above

### To arm/disarm a partition, publish to:
- `partition-N-away/set`: 0 (disarm) | 1 (arm partition - away mode)
- `partition-N-stay/set`: 0 (disarm) | 1 (arm partition - stay mode)

### Zones:
- `openzone-1`: 0 (no movement) | 1 (movement detected)
- ...
Expand All @@ -51,6 +57,9 @@ homie/device-id/alarm/
### To request status refresh, publish to
- `refresh-status/set`: 1

### To set the panel date/time, publish to
- `panel-time/set`: "YYYY-MM-DD HH:mm"

### To reboot the device, publish to
- `maintenance/set`: "reboot" to reboot
- `maintenance/set`: "dsc-stop" to stop dsc keybus interrupts
Expand All @@ -60,11 +69,6 @@ The DSC Keybus interrupts seem to interfere with the OTA and Config update opera

So before sending a config update, stop the DSC interrupts by publishing to `maintenance/set`: "dsc-stop". Once the config update has been made, restart the DSC interface using `maintenance/set`: "dsc-start" or sending a reboot request.


### To arm/disarm a partition, publish to:
- `partition-N-away/set`: 0 (disarm) | 1 (arm partition - away mode)
- `partition-N-stay/set`: 0 (disarm) | 1 (arm partition - stay mode)

## Initial Setup
Homie needs to be configured before it can connect to your Wifi / MQTT server.
Standard Homie configuration methods are of course supported. However, the easiest is to follow these steps:
Expand Down Expand Up @@ -115,4 +119,6 @@ Note
- The last step may be unnecessary because the ESP8266 seems to crash and restart, but the config gets updated.
- You can update the wifi / mqtt connection details in the same manner


## Library Dependencies
- [Homie-esp8266 v3.x](https://github.com/homieiot/homie-esp8266.git#develop-v3)
- [dscKeybusReader v1.3](https://github.com/taligentx/dscKeybusInterface.git#develop)
9 changes: 7 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

;[env:nodemcu]
[env:d1_mini_pro]
platform = espressif8266
;board = nodemcu
board = d1_mini_pro
framework = arduino
lib_deps = dscKeybusInterface, Homie
lib_deps =
https://github.com/taligentx/dscKeybusInterface.git#develop
https://github.com/homieiot/homie-esp8266.git#develop-v3
build_flags = -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
monitor_speed = 115200
monitor_speed = 115200

149 changes: 71 additions & 78 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,18 @@
* Processes the security system status and implement the Homie convention.
*
* See README.md
*
* Wiring:
* DSC Aux(+) ---+--- esp8266 NodeMCU Vin pin
* |
* +--- 5v voltage regulator --- esp8266 Wemos D1 Mini 5v pin
*
* DSC Aux(-) --- esp8266 Ground
*
* +--- dscClockPin (esp8266: D1, D2, D8)
* DSC Yellow --- 15k ohm resistor ---|
* +--- 10k ohm resistor --- Ground
*
* +--- dscReadPin (esp8266: D1, D2, D8)
* DSC Green ---- 15k ohm resistor ---|
* +--- 10k ohm resistor --- Ground
*
* Virtual keypad (optional):
* DSC Green ---- NPN collector --\
* |-- NPN base --- 1k ohm resistor --- dscWritePin (esp8266: D1, D2, D8)
* Ground --- NPN emitter --/
*
* Virtual keypad uses an NPN transistor to pull the data line low - most small signal NPN transistors should
* be suitable, for example:
* -- 2N3904 (EBC)
* -- BC547, BC548, BC549
*
*
* For details on the wiring, please see:
* https://github.com/taligentx/dscKeybusInterface
*
*
*/

#include <Homie.h>
#include <dscKeybusInterface.h>
#include <time.h>

#define SOFTWARE_VERSION "1.0.1"
#define SOFTWARE_VERSION "1.1.0 Build 11"

// Configures the Keybus interface with the specified pins
// dscWritePin is optional, leaving it out disables the virtual keypad.
Expand All @@ -54,51 +31,27 @@ dscKeybusInterface dsc(dscClockPin, dscReadPin, dscWritePin);

enum ArmType { arm_away, arm_stay };

bool homieNodeInputHandler(const String& property, const HomieRange& range, const String& value);
bool homieNodeInputHandler(const HomieRange& range, const String& property, const String& value);

HomieNode homieNode("alarm", "alarm", homieNodeInputHandler);
HomieNode homieNode("alarm", "alarm", "alarm", false, 0, 0, homieNodeInputHandler);
HomieSetting<const char*> dscAccessCode("access-code", "Alarm access code");

unsigned long dscStopTime = 0; // the time (in millis) when the dsc-stop was last requested

void resetDscStatus() {
dsc.statusChanged = true;
dsc.keybusChanged = true;
dsc.troubleChanged = true;
dsc.powerChanged = true;
dsc.batteryChanged = true;
for (byte partition = 0; partition < dscPartitions; partition++) {
dsc.armedChanged[partition] = true;
dsc.alarmChanged[partition] = true;
dsc.exitDelayChanged[partition] = true;
dsc.entryDelayChanged[partition] = true;
dsc.fireChanged[partition] = true;
}
dsc.openZonesStatusChanged = true;
dsc.alarmZonesStatusChanged = true;
for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) {
dsc.openZonesChanged[zoneGroup] = dsc.alarmZonesChanged[zoneGroup] = 0xFF;
}
}

void dscEnd() {
timer1_detachInterrupt();
detachInterrupt(digitalPinToInterrupt(dscClockPin));
}

void setupHandler() {
dsc.begin();
}

void loopHandler() {
// autmatically restart dsc after 5 minutes if it was stopped
// automatically restart dsc after 5 minutes if it was stopped
if (dscStopTime > 0 && (millis() - dscStopTime) > 5*60*1000) {
dsc.begin();
dscStopTime = 0;
homieNode.setProperty("message").setRetained(false).send("DSC Interface restarted automatically");
}

if (!(dsc.handlePanel() && dsc.statusChanged)) {
dsc.loop();
if (!dsc.statusChanged) {
return;
}
dsc.statusChanged = false;
Expand All @@ -111,7 +64,7 @@ void loopHandler() {
}

// Sends the access code when needed by the panel for arming
if (dsc.accessCodePrompt && dsc.writeReady) {
if (dsc.accessCodePrompt) {
dsc.accessCodePrompt = false;
dsc.write(dscAccessCode.get());
}
Expand Down Expand Up @@ -146,6 +99,15 @@ void loopHandler() {
homieNode.setProperty("panic-alarm-keypad").send("1");
}

if (dsc.timestampChanged) {
dsc.timestampChanged = false;
if (!(dsc.year < 0 || dsc.year > 9999 || dsc.month < 1 || dsc.month > 12 || dsc.day < 1 || dsc.day > 31 || dsc.hour < 0 || dsc.hour > 23 || dsc.minute < 0 || dsc.minute > 59)) {
char panelTime[17]; // YYYY-MM-DD HH:mm
sprintf(panelTime, "%04d-%02d-%02d %02d:%02d", dsc.year, dsc.month, dsc.day, dsc.hour, dsc.minute);
homieNode.setProperty("panel-time").setRetained(false).send(panelTime);
}
}

String property;
// loop through partitions
for (byte partition = 0; partition < dscPartitions; partition++) {
Expand Down Expand Up @@ -175,6 +137,12 @@ void loopHandler() {
dsc.fireChanged[partition] = false;
homieNode.setProperty(property + "-fire").send(dsc.fire[partition] ? "1" : "0");
}

// Publish access code
if (dsc.accessCodeChanged[partition]) {
dsc.accessCodeChanged[partition] = false;
homieNode.setProperty(property + "-access-code").setRetained(false).send(String(dsc.accessCode[partition], DEC).c_str());
}
}

// Publish zones 1-64 status
Expand All @@ -188,11 +156,11 @@ void loopHandler() {
for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) {
for (byte zoneBit = 0; zoneBit < 8; zoneBit++) {
if (bitRead(dsc.openZonesChanged[zoneGroup], zoneBit)) { // Checks an individual open zone status flag
bitWrite(dsc.openZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual open zone status flag
property = "openzone-" + String(zoneBit + 1 + (zoneGroup * 8));
homieNode.setProperty(property).send(bitRead(dsc.openZones[zoneGroup], zoneBit) ? "1" : "0");
}
}
dsc.openZonesChanged[zoneGroup] = 0;
}
}

Expand All @@ -207,18 +175,24 @@ void loopHandler() {
for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) {
for (byte zoneBit = 0; zoneBit < 8; zoneBit++) {
if (bitRead(dsc.alarmZonesChanged[zoneGroup], zoneBit)) { // Checks an individual alarm zone status flag
bitWrite(dsc.alarmZonesChanged[zoneGroup], zoneBit, 0); // Resets the individual alarm zone status flag
property = "alarmzone-" + String(zoneBit + 1 + (zoneGroup * 8));
homieNode.setProperty(property).send(bitRead(dsc.alarmZones[zoneGroup], zoneBit) ? "1" : "0");
}
}
dsc.alarmZonesChanged[zoneGroup] = 0;
}
}
}

bool onKeypad(const HomieRange& range, const String& command) {
dsc.write(command.c_str());
return true;
static char commandBuffer[256];
if (command.length() > sizeof(commandBuffer)/sizeof(commandBuffer[0])) {
homieNode.setProperty("message").setRetained(false).send("Keypad data is too long");
} else {
strcpy(commandBuffer, command.c_str());
dsc.write(commandBuffer);
}
return true;
}

// Arm - the partition argument is 0 based
Expand Down Expand Up @@ -253,7 +227,8 @@ byte extractPrefixDigits(String &str) {
return prefix;
}

bool homieNodeInputHandler(const String& property, const HomieRange& range, const String& value) {
bool homieNodeInputHandler(const HomieRange& range, const String& property, const String& value) {
// bool homieNodeInputHandler(const String& property, const HomieRange& range, const String& value) {
if (!property.startsWith("partition-")) {
return false;
}
Expand Down Expand Up @@ -281,44 +256,60 @@ bool homieNodeInputHandler(const String& property, const HomieRange& range, cons
}

bool onRefreshStatus(const HomieRange& range, const String& command) {
resetDscStatus();
dsc.resetStatus();
homieNode.setProperty("refresh-status").setRetained(false).send("OK");
return true;
}

bool onDscActive(const HomieRange& range, const String& command) {
if (command == "1") {
dsc.begin();
} else {
dscEnd();
}
return true;
}
// bool onDscActive(const HomieRange& range, const String& command) {
// if (command == "1") {
// dsc.begin();
// } else {
// dsc.stop();
// }
// return true;
// }

bool onMaintenance(const HomieRange& range, const String& command) {
if (command == "reboot") {
homieNode.setProperty("maintenance").setRetained(false).send("Rebooting...");
homieNode.setProperty("message").setRetained(false).send("Rebooting...");
Homie.reboot();
} else if (command == "dsc-stop") {
dscEnd();
homieNode.setProperty("maintenance").setRetained(false).send("DSC Interface stopped");
dsc.stop();
homieNode.setProperty("message").setRetained(false).send("DSC Interface stopped");
dscStopTime = millis();
if (dscStopTime == 0) dscStopTime = 1; // just in case millis() returned 0
} else if (command == "dsc-start") {
dscStopTime = 0;
dsc.begin();
homieNode.setProperty("maintenance").setRetained(false).send("DSC Interface started");
homieNode.setProperty("message").setRetained(false).send("DSC Interface started");
} else {
homieNode.setProperty("maintenance").setRetained(false).send("Unknown maintenance command");
homieNode.setProperty("message").setRetained(false).send("Unknown maintenance command");
return false;
}
return true;
}

// parse the provided time and set the dsc panel time
bool onPanelTime(const HomieRange& range, const String& command) {
if (dsc.keybusConnected) {
struct tm tm; // note tm_year is year since 1900, and tm_mon is month since January. Add offsets accordingly
strptime(command.c_str(), "%Y-%m-%d %H:%M", &tm);
dsc.setTime(1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, dscAccessCode.get());
// homieNode.setProperty("message").setRetained(false).send("OK");
homieNode.setProperty("message").setRetained(false).send("OK: " +
String(tm.tm_year + 1900, DEC) + "-" + String(1+tm.tm_mon, DEC) + "-" + String(tm.tm_mday) + " " +
String(tm.tm_hour, DEC) + ":" + String(tm.tm_min, DEC));
} else {
homieNode.setProperty("message").setRetained(false).send("ERROR: DSC Keybus is not connected");
}
return true;
}

void onHomieEvent(const HomieEvent& event) {
switch (event.type) {
case HomieEventType::MQTT_READY: resetDscStatus(); break;
case HomieEventType::OTA_STARTED: dscEnd(); break;
case HomieEventType::MQTT_READY: dsc.resetStatus(); break;
case HomieEventType::OTA_STARTED: dsc.stop(); break;
case HomieEventType::OTA_SUCCESSFUL:
case HomieEventType::OTA_FAILED: dsc.begin(); break;
}
Expand All @@ -341,6 +332,7 @@ void setup() {
homieNode.advertise("fire-alarm-keypad");
homieNode.advertise("aux-alarm-keypad");
homieNode.advertise("panic-alarm-keypad");
homieNode.advertise("panel-time").settable(onPanelTime);

for (byte i = 1; i <= dscPartitions; i++) {
String pName = "partition-" + String(i) + "-";
Expand All @@ -349,6 +341,7 @@ void setup() {
homieNode.advertise(String(pName + "exit-delay").c_str());
homieNode.advertise(String(pName + "alarm").c_str());
homieNode.advertise(String(pName + "fire").c_str());
// homieNode.advertise(String(pName + "access-code").c_str()); //## BUG: It seems that the mqtt library cannot handle large messages
}

Homie.setup();
Expand Down

0 comments on commit 9509a40

Please sign in to comment.