FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

/* DSC Keybus Interface Functions used by all sketches to track the security system status. These are the most commonly used status data by the example sketches. Sketches can be extended to support all Keybus data (as seen in dscKeybusPrintData.cpp) by checking panelData[] - see the Unlocker sketch for an example of this. If there is a status that is not currently being tracked and would be useful in most applications, post an issue/PR: https://github.com/taligentx/dscKeybusInterface This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dscKeybusInterface.h" // Resets the state of all status components as changed for sketches to get the current status void dscKeybusInterface::resetStatus() { statusChanged = true; keybusChanged = true; troubleChanged = true; powerChanged = true; batteryChanged = true; for (byte partition = 0; partition < dscPartitions; partition++) { readyChanged[partition] = true; armedChanged[partition] = true; alarmChanged[partition] = true; fireChanged[partition] = true; disabled[partition] = true; } openZonesStatusChanged = true; alarmZonesStatusChanged = true; for (byte zoneGroup = 0; zoneGroup < dscZones; zoneGroup++) { openZonesChanged[zoneGroup] = 0xFF; alarmZonesChanged[zoneGroup] = 0xFF; } pgmOutputsChanged[0] = 0xFF; pgmOutputsChanged[1] = 0x3F; } // Sets the panel time bool dscKeybusInterface::setTime(unsigned int year, byte month, byte day, byte hour, byte minute, const char* accessCode, byte timePartition) { // Loops if a previous write is in progress while(writeKeyPending || writeKeysPending) { loop(); #if defined(ESP8266) yield(); #endif } if (!ready[0]) return false; // Skips if partition 1 is not ready if (hour > 23 || minute > 59 || month > 12 || day > 31 || year > 2099 || (year > 99 && year < 1900)) return false; // Skips if input date/time is invalid static char timeEntry[21]; strcpy(timeEntry, "*6"); strcat(timeEntry, accessCode); strcat(timeEntry, "1"); char timeChar[3]; if (hour < 10) strcat(timeEntry, "0"); itoa(hour, timeChar, 10); strcat(timeEntry, timeChar); if (minute < 10) strcat(timeEntry, "0"); itoa(minute, timeChar, 10); strcat(timeEntry, timeChar); if (month < 10) strcat(timeEntry, "0"); itoa(month, timeChar, 10); strcat(timeEntry, timeChar); if (day < 10) strcat(timeEntry, "0"); itoa(day, timeChar, 10); strcat(timeEntry, timeChar); if (year >= 2000) year -= 2000; else if (year >= 1900) year -= 1900; if (year < 10) strcat(timeEntry, "0"); itoa(year, timeChar, 10); strcat(timeEntry, timeChar); strcat(timeEntry, "#"); if (writePartition != timePartition) { byte previousPartition = writePartition; writePartition = timePartition; write(timeEntry); while(writeKeyPending || writeKeysPending) { loop(); #if defined(ESP8266) yield(); #endif } writePartition = previousPartition; } else write(timeEntry); return true; } // Processes status commands: 0x05 (Partitions 1-4) and 0x1B (Partitions 5-8) void dscKeybusInterface::processPanelStatus() { // Trouble status if (panelData[3] 4 && panelData[0] == 0x1B) { partitionStart = 4; partitionCount = 8; if (dscPartitions < partitionCount) partitionCount = dscPartitions; } // Sets status per partition for (byte partitionIndex = partitionStart; partitionIndex < partitionCount; partitionIndex++) { byte statusByte, messageByte; if (partitionIndex < 4) { statusByte = (partitionIndex * 2) + 2; messageByte = (partitionIndex * 2) + 3; } else { statusByte = ((partitionIndex - 4) * 2) + 2; messageByte = ((partitionIndex - 4) * 2) + 3; } // Partition disabled status if (panelData[messageByte] == 0xC7) { disabled[partitionIndex] = true; processReadyStatus(partitionIndex, false); } else disabled[partitionIndex] = false; if (disabled[partitionIndex] != previousDisabled[partitionIndex]) { previousDisabled[partitionIndex] = disabled[partitionIndex]; disabledChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } // Status lights lights[partitionIndex] = panelData[statusByte]; if (lights[partitionIndex] != previousLights[partitionIndex]) { previousLights[partitionIndex] = lights[partitionIndex]; if (!pauseStatus) statusChanged = true; } // Status messages status[partitionIndex] = panelData[messageByte]; if (status[partitionIndex] != previousStatus[partitionIndex]) { previousStatus[partitionIndex] = status[partitionIndex]; if (!pauseStatus) statusChanged = true; } // Fire status if (panelData[messageByte] < 0x12) { // Ignores fire light status in intermittent states if (bitRead(panelData[statusByte],6)) fire[partitionIndex] = true; else fire[partitionIndex] = false; if (fire[partitionIndex] != previousFire[partitionIndex]) { previousFire[partitionIndex] = fire[partitionIndex]; fireChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } } // Messages switch (panelData[messageByte]) { // Ready case 0x01: // Partition ready case 0x02: { // Stay/away zones open processReadyStatus(partitionIndex, true); processEntryDelayStatus(partitionIndex, false); armedStay[partitionIndex] = false; armedAway[partitionIndex] = false; armed[partitionIndex] = false; if (armed[partitionIndex] != previousArmed[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processAlarmStatus(partitionIndex, false); break; } // Zones open case 0x03: { processReadyStatus(partitionIndex, false); processEntryDelayStatus(partitionIndex, false); break; } // Armed case 0x04: // Armed stay case 0x05: { // Armed away if (panelData[messageByte] == 0x04) { armedStay[partitionIndex] = true; armedAway[partitionIndex] = false; } else { armedStay[partitionIndex] = false; armedAway[partitionIndex] = true; } writeArm[partitionIndex] = false; armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex] || armedStay[partitionIndex] != previousArmedStay[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processReadyStatus(partitionIndex, false); processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; processEntryDelayStatus(partitionIndex, false); break; } // Exit delay in progress case 0x08: { writeArm[partitionIndex] = false; accessCodePrompt = false; processExitDelayStatus(partitionIndex, true); if (exitState[partitionIndex] != DSC_EXIT_NO_ENTRY_DELAY) { if (bitRead(lights[partitionIndex],3)) exitState[partitionIndex] = DSC_EXIT_STAY; else exitState[partitionIndex] = DSC_EXIT_AWAY; if (exitState[partitionIndex] != previousExitState[partitionIndex]) { previousExitState[partitionIndex] = exitState[partitionIndex]; exitDelayChanged[partitionIndex] = true; exitStateChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } } processReadyStatus(partitionIndex, true); break; } // Arming with no entry delay case 0x09: { processReadyStatus(partitionIndex, true); exitState[partitionIndex] = DSC_EXIT_NO_ENTRY_DELAY; break; } // Entry delay in progress case 0x0C: { processReadyStatus(partitionIndex, false); processEntryDelayStatus(partitionIndex, true); break; } // Partition in alarm case 0x11: { processReadyStatus(partitionIndex, false); processEntryDelayStatus(partitionIndex, false); processAlarmStatus(partitionIndex, true); break; } // Arming with bypassed zones case 0x15: { processReadyStatus(partitionIndex, true); break; } // Partition armed with no entry delay case 0x06: case 0x16: { noEntryDelay[partitionIndex] = true; // Sets an armed mode if not already set, used if interface is initialized while the panel is armed if (!armedStay[partitionIndex] && !armedAway[partitionIndex]) armedStay[partitionIndex] = true; armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processReadyStatus(partitionIndex, false); break; } // Partition disarmed case 0x3D: case 0x3E: { if (panelData[messageByte] == 0x3E) { // Sets ready only during Partition disarmed processReadyStatus(partitionIndex, true); } processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; processEntryDelayStatus(partitionIndex, false); processArmed(partitionIndex, false); processAlarmStatus(partitionIndex, false); break; } // Invalid access code case 0x8F: { if (!armed[partitionIndex]) processReadyStatus(partitionIndex, true); break; } // Enter * function code case 0x9E: case 0xB8: { if (starKeyWait[partitionIndex]) { // Resets the flag that waits for panel status 0x9E, 0xB8 after '*' is pressed starKeyWait[partitionIndex] = false; starKeyCheck = false; writeKeyPending = false; } processReadyStatus(partitionIndex, false); break; } // Enter access code case 0x9F: { if (writeArm[partitionIndex]) { // Ensures access codes are only sent when an arm command is sent through this interface accessCodePrompt = true; if (!pauseStatus) statusChanged = true; } processReadyStatus(partitionIndex, false); break; } default: { processReadyStatus(partitionIndex, false); break; } } } } void dscKeybusInterface::processPanel_0x16() { if (!validCRC()) return; // Panel version panelVersion = ((panelData[3] >> 4) * 10) + (panelData[3] & 0x0F); } // Panel status and zones 1-8 status void dscKeybusInterface::processPanel_0x27() { if (!validCRC()) return; // Messages for (byte partitionIndex = 0; partitionIndex < 2; partitionIndex++) { byte messageByte = (partitionIndex * 2) + 3; // Armed if (panelData[messageByte] == 0x04 || panelData[messageByte] == 0x05) { processReadyStatus(partitionIndex, false); if (panelData[messageByte] == 0x04) { armedStay[partitionIndex] = true; armedAway[partitionIndex] = false; } else if (panelData[messageByte] == 0x05) { armedStay[partitionIndex] = false; armedAway[partitionIndex] = true; } armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex] || armedStay[partitionIndex] != previousArmedStay[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; } // Armed with no entry delay else if (panelData[messageByte] == 0x06 || panelData[messageByte] == 0x16) { noEntryDelay[partitionIndex] = true; // Sets an armed mode if not already set, used if interface is initialized while the panel is armed if (!armedStay[partitionIndex] && !armedAway[partitionIndex]) armedStay[partitionIndex] = true; armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; processReadyStatus(partitionIndex, false); } } // Zones 1-8 status is stored in openZones[0] and openZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 processZoneStatus(0, 6); } // Zones 9-16 status is stored in openZones[1] and openZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 void dscKeybusInterface::processPanel_0x2D() { if (!validCRC()) return; if (dscZones < 2) return; processZoneStatus(1, 6); } // Zones 17-24 status is stored in openZones[2] and openZonesChanged[2]: Bit 0 = Zone 17 ... Bit 7 = Zone 24 void dscKeybusInterface::processPanel_0x34() { if (!validCRC()) return; if (dscZones < 3) return; processZoneStatus(2, 6); } // Zones 25-32 status is stored in openZones[3] and openZonesChanged[3]: Bit 0 = Zone 25 ... Bit 7 = Zone 32 void dscKeybusInterface::processPanel_0x3E() { if (!validCRC()) return; if (dscZones < 4) return; processZoneStatus(3, 6); } /* * PGM outputs 1-14 status is stored in pgmOutputs[] * * pgmOutputs[0], Bit 0 = PGM 1 ... Bit 7 = PGM 8 * pgmOutputs[1], Bit 0 = PGM 9 ... Bit 5 = PGM 14 */ void dscKeybusInterface::processPanel_0x87() { if (!validCRC()) return; pgmOutputs[0] = panelData[3] & 0x03; pgmOutputs[0] |= panelData[2] > 6; pgmOutputs[1] |= (panelData[3] & 0xF0) >> 2; for (byte pgmByte = 0; pgmByte < 2; pgmByte++) { byte pgmChanged = pgmOutputs[pgmByte] ^ previousPgmOutputs[pgmByte]; if (pgmChanged != 0) { previousPgmOutputs[pgmByte] = pgmOutputs[pgmByte]; pgmOutputsStatusChanged = true; if (!pauseStatus) statusChanged = true; for (byte pgmBit = 0; pgmBit < 8; pgmBit++) { if (bitRead(pgmChanged, pgmBit)) bitWrite(pgmOutputsChanged[pgmByte], pgmBit, 1); } } } } void dscKeybusInterface::processPanel_0xA5() { if (!validCRC()) return; processTime(2); // Timestamp if (panelData[6] == 0 && panelData[7] == 0) { statusChanged = true; timestampChanged = true; return; } byte partition = panelData[3] >> 6; switch (panelData[5] & 0x03) { case 0x00: processPanelStatus0(partition, 6); break; case 0x02: processPanelStatus2(partition, 6); break; } } /* * 0xE6: Extended status, partitions 1-8 * Panels: PC5020, PC1616, PC1832, PC1864 * * 0xE6 commands are split into multiple subcommands to handle up to 8 partitions/64 zones. */ void dscKeybusInterface::processPanel_0xE6() { if (!validCRC()) return; switch (panelData[2]) { case 0x09: processPanel_0xE6_0x09(); return; // Zones 33-40 status case 0x0B: processPanel_0xE6_0x0B(); return; // Zones 41-48 status case 0x0D: processPanel_0xE6_0x0D(); return; // Zones 49-56 status case 0x0F: processPanel_0xE6_0x0F(); return; // Zones 57-64 status case 0x1A: processPanel_0xE6_0x1A(); return; // Panel status } } // Zones 33-40 status is stored in openZones[4] and openZonesChanged[4]: Bit 0 = Zone 33 ... Bit 7 = Zone 40 void dscKeybusInterface::processPanel_0xE6_0x09() { if (dscZones > 4) processZoneStatus(4, 3); } // Zones 41-48 status is stored in openZones[5] and openZonesChanged[5]: Bit 0 = Zone 41 ... Bit 7 = Zone 48 void dscKeybusInterface::processPanel_0xE6_0x0B() { if (dscZones > 5) processZoneStatus(5, 3); } // Zones 49-56 status is stored in openZones[6] and openZonesChanged[6]: Bit 0 = Zone 49 ... Bit 7 = Zone 56 void dscKeybusInterface::processPanel_0xE6_0x0D() { if (dscZones > 6) processZoneStatus(6, 3); } // Zones 57-64 status is stored in openZones[7] and openZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 void dscKeybusInterface::processPanel_0xE6_0x0F() { if (dscZones > 7) processZoneStatus(7, 3); } // Panel status void dscKeybusInterface::processPanel_0xE6_0x1A() { // Panel AC power trouble if (panelData[6] & 0x10) powerTrouble = true; else powerTrouble = false; if (powerTrouble != previousPower) { previousPower = powerTrouble; powerChanged = true; if (!pauseStatus) statusChanged = true; } } void dscKeybusInterface::processPanel_0xEB() { if (!validCRC()) return; if (dscPartitions < 3) return; processTime(3); byte partition; switch (panelData[2]) { case 0x01: partition = 1; break; case 0x02: partition = 2; break; case 0x04: partition = 3; break; case 0x08: partition = 4; break; case 0x10: partition = 5; break; case 0x20: partition = 6; break; case 0x40: partition = 7; break; case 0x80: partition = 8; break; default: partition = 0; break; } switch (panelData[7] & 0x07) { case 0x00: processPanelStatus0(partition, 8); break; case 0x02: processPanelStatus2(partition, 8); break; case 0x04: processPanelStatus4(partition, 8); break; case 0x05: processPanelStatus5(partition, 8); break; } } void dscKeybusInterface::processPanelStatus0(byte partition, byte panelByte) { // Processes status messages that are not partition-specific if (panelData[0] == 0xA5) { switch (panelData[panelByte]) { // Keypad Fire alarm case 0x4E: { keypadFireAlarm = true; if (!pauseStatus) statusChanged = true; return; } // Keypad Aux alarm case 0x4F: { keypadAuxAlarm = true; if (!pauseStatus) statusChanged = true; return; } // Keypad Panic alarm case 0x50: { keypadPanicAlarm = true; if (!pauseStatus) statusChanged =true; return; } // Panel battery trouble case 0xE7: { batteryTrouble = true; batteryChanged = true; if (!pauseStatus) statusChanged = true; return; } // Panel AC power failure case 0xE8: { powerTrouble = true; if (powerTrouble != previousPower) { previousPower = powerTrouble; powerChanged = true; if (!pauseStatus) statusChanged = true; } return; } // Panel battery restored case 0xEF: { batteryTrouble = false; batteryChanged = true; if (!pauseStatus) statusChanged = true; return; } // Panel AC power restored case 0xF0: { powerTrouble = false; if (powerTrouble != previousPower) { previousPower = powerTrouble; powerChanged = true; if (!pauseStatus) statusChanged = true; } return; } default: if (partition == 0) return; } } // Processes partition-specific status if (partition > dscPartitions) return; // Ensures that only the configured number of partitions are processed byte partitionIndex = partition - 1; // Disarmed if (panelData[panelByte] == 0x4A || // Disarmed after alarm in memory panelData[panelByte] == 0xE6 || // Disarmed special: keyswitch/wireless key/DLS (panelData[panelByte] >= 0xC0 && panelData[panelByte] = 0x09 && panelData[panelByte] = 0x29 && panelData[panelByte] = 0x99 && panelData[panelByte] = 0xC0 && panelData[panelByte] dscPartitions) return; byte partitionIndex = partition - 1; // Armed: stay and Armed: away if (panelData[panelByte] == 0x9A || panelData[panelByte] == 0x9B) { if (panelData[panelByte] == 0x9A) { armedStay[partitionIndex] = true; armedAway[partitionIndex] = false; } else if (panelData[panelByte] == 0x9B) { armedStay[partitionIndex] = false; armedAway[partitionIndex] = true; } armed[partitionIndex] = true; if (armed[partitionIndex] != previousArmed[partitionIndex] || armedStay[partitionIndex] != previousArmedStay[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; previousArmedStay[partitionIndex] = armedStay[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } processExitDelayStatus(partitionIndex, false); exitState[partitionIndex] = 0; processReadyStatus(partitionIndex, false); return; } if (panelData[panelByte] == 0xA5) { switch (panelData[panelByte]) { // Activate stay/away zones case 0x99: { armed[partitionIndex] = true; armedAway[partitionIndex] = true; armedStay[partitionIndex] = false; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; return; } // Armed with no entry delay case 0x9C: { noEntryDelay[partitionIndex] = true; processReadyStatus(partitionIndex, false); return; } } } } void dscKeybusInterface::processPanelStatus4(byte partition, byte panelByte) { if (partition == 0 || partition > dscPartitions) return; byte partitionIndex = partition - 1; // Zone alarm, zones 33-64 // Zone alarm status is stored using 1 bit per zone in alarmZones[] and alarmZonesChanged[]: // alarmZones[0] and alarmZonesChanged[0]: Bit 0 = Zone 1 ... Bit 7 = Zone 8 // alarmZones[1] and alarmZonesChanged[1]: Bit 0 = Zone 9 ... Bit 7 = Zone 16 // ... // alarmZones[7] and alarmZonesChanged[7]: Bit 0 = Zone 57 ... Bit 7 = Zone 64 if (panelData[panelByte] = 0x20 && panelData[panelByte] dscPartitions) return; byte partitionIndex = partition - 1; /* * Armed by access codes 35-95 * 0x00 - 0x04: Access codes 35-39 * 0x05 - 0x39: Access codes 43-95 */ if (panelData[panelByte] = 0x3A && panelData[panelByte] > 4; byte dscYear4 = panelData[panelByte] & 0x0F; year = (dscYear3 * 10) + dscYear4; if (dscYear3 >= 7) year += 1900; else year += 2000; month = panelData[panelByte + 1] >=4; byte dscDay1 = panelData[panelByte + 1] >= 3; byte dscDay2 = panelData[panelByte + 2] >> 5; day = dscDay1 | dscDay2; hour = panelData[panelByte + 2] & 0x1F; minute = panelData[panelByte + 3] >> 2; } void dscKeybusInterface::processAlarmZones(byte panelByte, byte startByte, byte zoneCountOffset, byte writeValue) { byte maxZones = dscZones * 8; if (maxZones > 32) { if (startByte < 4) maxZones = 32; else maxZones -= 32; } else if (startByte >= 4) return; for (byte zoneCount = 0; zoneCount < maxZones; zoneCount++) { if (panelData[panelByte] == zoneCount + zoneCountOffset) { for (byte zoneRange = 0; zoneRange = (zoneRange * 8) && zoneCount < ((zoneRange * 8) + 8)) { processAlarmZonesStatus(startByte + zoneRange, zoneCount, writeValue); } } } } } void dscKeybusInterface::processAlarmZonesStatus(byte zonesByte, byte zoneCount, byte writeValue) { byte zonesRange = zonesByte; if (zonesRange >= 4) zonesRange -= 4; bitWrite(alarmZones[zonesByte], (zoneCount - (zonesRange * 8)), writeValue); if (bitRead(previousAlarmZones[zonesByte], (zoneCount - (zonesRange * 8))) != writeValue) { bitWrite(previousAlarmZones[zonesByte], (zoneCount - (zonesRange * 8)), writeValue); bitWrite(alarmZonesChanged[zonesByte], (zoneCount - (zonesRange * 8)), 1); alarmZonesStatusChanged = true; if (!pauseStatus) statusChanged = true; } } void dscKeybusInterface::processArmed(byte partitionIndex, bool armedStatus) { armedStay[partitionIndex] = armedStatus; armedAway[partitionIndex] = armedStatus; armed[partitionIndex] = armedStatus; if (armed[partitionIndex] != previousArmed[partitionIndex]) { previousArmed[partitionIndex] = armed[partitionIndex]; armedChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } } void dscKeybusInterface::processPanelAccessCode(byte partitionIndex, byte dscCode, bool accessCodeIncrease) { if (accessCodeIncrease) { if (dscCode >= 35) dscCode += 5; } else { if (dscCode >= 40) dscCode += 3; } accessCode[partitionIndex] = dscCode; if (accessCode[partitionIndex] != previousAccessCode[partitionIndex]) { previousAccessCode[partitionIndex] = accessCode[partitionIndex]; accessCodeChanged[partitionIndex] = true; if (!pauseStatus) statusChanged = true; } }

Back | FazBrowse Home | New Git URL