MSA-1993: keypad

This commit is contained in:
Raffaele Giannattasio
2017-05-20 12:36:16 -07:00
parent 01c2968f91
commit 8583c42282
5 changed files with 4169 additions and 26 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -296,7 +296,10 @@ def pageHistory() {
if (history.size() == 0) {
paragraph "No history available."
} else {
paragraph "Not implemented"
history.each() {
def text = "" + new Date(it.time + location.timeZone.rawOffset ).format("yyyy-MM-dd HH:mm") + ": " + it.event + " - " + it.description
paragraph text
}
}
}
}
@@ -405,6 +408,7 @@ def pageConfigureZones() {
section("${it.displayName} (contact)") {
input "type_${devId}", "enum", title:"Zone Type", metadata:[values:zoneTypes], defaultValue:"exterior"
input "delay_${devId}", "bool", title:"Entry/Exit Delays", defaultValue:true
input "chime_${devId}", "bool", title:"Chime on open", defaultValue:true
}
}
}
@@ -416,6 +420,7 @@ def pageConfigureZones() {
section("${it.displayName} (motion)") {
input "type_${devId}", "enum", title:"Zone Type", metadata:[values:zoneTypes], defaultValue:"interior"
input "delay_${devId}", "bool", title:"Entry/Exit Delays", defaultValue:false
input "chime_${devId}", "bool", title:"Chime on motion", defaultValue:false
}
}
}
@@ -427,6 +432,7 @@ def pageConfigureZones() {
section("${it.displayName} (movement)") {
input "type_${devId}", "enum", title:"Zone Type", metadata:[values:zoneTypes], defaultValue:"interior"
input "delay_${devId}", "bool", title:"Entry/Exit Delays", defaultValue:false
input "chime_${devId}", "bool", title:"Chime on movement", defaultValue:false
}
}
}
@@ -438,6 +444,7 @@ def pageConfigureZones() {
section("${it.displayName} (smoke)") {
input "type_${devId}", "enum", title:"Zone Type", metadata:[values:zoneTypes], defaultValue:"alert"
input "delay_${devId}", "bool", title:"Entry/Exit Delays", defaultValue:false
input "chime_${devId}", "bool", title:"Chime on smoke", defaultValue:false
}
}
}
@@ -449,6 +456,7 @@ def pageConfigureZones() {
section("${it.displayName} (moisture)") {
input "type_${devId}", "enum", title:"Zone Type", metadata:[values:zoneTypes], defaultValue:"alert"
input "delay_${devId}", "bool", title:"Entry/Exit Delays", defaultValue:false
input "chime_${devId}", "bool", title:"Chime on water", defaultValue:false
}
}
}
@@ -471,6 +479,14 @@ def pageArmingOptions() {
"is armed without setting off an alarm. You can optionally disable " +
"entry and exit delay when the alarm is armed in Stay mode."
def inputKeypads = [
name: "keypads",
type: "capability.lockCodes",
title: "Keypads for Exit / Entry delay",
multiple: true,
required: false
]
def inputAwayModes = [
name: "awayModes",
type: "mode",
@@ -507,10 +523,19 @@ def pageArmingOptions() {
def inputDelayStay = [
name: "stayDelayOff",
type: "bool",
title: "Disable delays in Stay mode",
title: "Disable alarm (entry) delay in Stay mode",
defaultValue: false,
required: true
]
def inputExitDelayStay = [
name: "stayExitDelayOff",
type: "bool",
title: "Disable arming (exit) delay in Stay mode",
defaultValue: true,
required: true
]
def pageProperties = [
name: "pageArmingOptions",
@@ -524,16 +549,21 @@ def pageArmingOptions() {
paragraph helpArming
}
section("Keypads") {
input inputKeypads
}
section("Modes") {
input inputAwayModes
input inputStayModes
input inputDisarmModes
}
section("Exit and Entry Delay") {
paragraph helpDelay
input inputDelay
input inputDelayStay
input inputExitDelayStay
}
}
}
@@ -563,6 +593,14 @@ def pageAlarmOptions() {
defaultValue: "Both"
]
def inputSirenEntryStrobe = [
name: "sirenEntryStrobe",
type: "bool",
title: "Strobe siren during entry delay",
defaultValue: true,
required: true
]
def inputSwitches = [
name: "switches",
type: "capability.switch",
@@ -602,6 +640,7 @@ def pageAlarmOptions() {
section("Sirens") {
input inputAlarms
input inputSirenMode
input inputSirenEntryStrobe
}
section("Switches") {
input inputSwitches
@@ -624,7 +663,49 @@ def pageNotifications() {
"disarmed or when an alarm is set off. Notifications can be send " +
"using either Push messages, SMS (text) messages and Pushbullet " +
"messaging service. Smart Alarm can also notify you with sounds or " +
"voice alerts using compatible audio devices, such as Sonos."
"voice alerts using compatible audio devices, such as Sonos." +
"Or using a SmartAlarm dashboard virtual device."
def inputNotificationDevice = [
name: "notificationDevice",
type: "capability.notification",
title: "Which smart alarm notification device?",
multiple: false,
required: false
]
def inputChimeDevices = [
name: "chimeDevices",
type: "capability.tone",
title: "Which Chime Devices?",
multiple: true,
required: false
]
def inputSirenOnWaterAlert = [
name: "sirenOnWaterAlert",
type: "bool",
title: "Use Siren for Water Leak?",
defaultValue: true,
required: true
]
def inputSirenOnSmokeAlert = [
name: "sirenOnSmokeAlert",
type: "bool",
title: "Use Siren for Smoke Alert?",
defaultValue: true,
required: true
]
def inputSirenOnIntrusionAlert = [
name: "sirenOnIntrusionAlert",
type: "bool",
title: "Use Siren for Intrusion Alert?",
defaultValue: true,
required: true
]
def inputPushAlarm = [
name: "pushMessage",
@@ -807,6 +888,19 @@ def pageNotifications() {
section("Notification Options") {
paragraph helpAbout
}
section("Notification Device")
{
input inputNotificationDevice
}
section("Chime Devices") {
input inputChimeDevices
}
section("Siren Notifcations")
{
input inputSirenOnWaterAlert
input inputSirenOnSmokeAlert
input inputSirenOnIntrusionAlert
}
section("Push Notifications") {
input inputPushAlarm
input inputPushStatus
@@ -1047,6 +1141,7 @@ private def setupInit() {
state.zones = []
state.alarms = []
state.history = []
state.alertType = "None"
} else {
def version = state.version as String
if (version == null || version.startsWith('1')) {
@@ -1065,7 +1160,7 @@ private def initialize() {
clearAlarm()
state.delay = settings.delay?.toInteger() ?: 30
state.offSwitches = []
state.history = []
//state.history = []
if (settings.awayModes?.contains(location.mode)) {
state.armed = true
@@ -1082,8 +1177,20 @@ private def initialize() {
initButtons()
initRestApi()
subscribe(location, onLocation)
if (settings.notificationDevice)
{
subscribe(settings.notificationDevice, "switch.off", gotDismissMessage)
}
STATE()
reportStatus()
}
def gotDismissMessage(evt)
{
log.debug "Got the dismiss message from the notification device.. clearing alarm!"
clearAlarm()
}
private def clearAlarm() {
@@ -1103,6 +1210,7 @@ private def clearAlarm() {
}
state.offSwitches = []
}
reportStatus()
}
private def initZones() {
@@ -1123,7 +1231,8 @@ private def initZones() {
deviceId: it.id,
sensorType: "contact",
zoneType: settings["type_${it.id}"] ?: "exterior",
delay: settings["delay_${it.id}"]
delay: settings["delay_${it.id}"],
chime: settings["chime_${it.id}"]
]
}
subscribe(settings.z_contact, "contact.open", onContact)
@@ -1135,7 +1244,8 @@ private def initZones() {
deviceId: it.id,
sensorType: "motion",
zoneType: settings["type_${it.id}"] ?: "interior",
delay: settings["delay_${it.id}"]
delay: settings["delay_${it.id}"],
chime: settings["chime_${it.id}"]
]
}
subscribe(settings.z_motion, "motion.active", onMotion)
@@ -1147,7 +1257,8 @@ private def initZones() {
deviceId: it.id,
sensorType: "acceleration",
zoneType: settings["type_${it.id}"] ?: "interior",
delay: settings["delay_${it.id}"]
delay: settings["delay_${it.id}"],
chime: settings["chime_${it.id}"]
]
}
subscribe(settings.z_movement, "acceleration.active", onMovement)
@@ -1159,7 +1270,8 @@ private def initZones() {
deviceId: it.id,
sensorType: "smoke",
zoneType: settings["type_${it.id}"] ?: "alert",
delay: settings["delay_${it.id}"]
delay: settings["delay_${it.id}"],
chime: settings["chime_${it.id}"]
]
}
subscribe(settings.z_smoke, "smoke.detected", onSmoke)
@@ -1174,7 +1286,8 @@ private def initZones() {
deviceId: it.id,
sensorType: "water",
zoneType: settings["type_${it.id}"] ?: "alert",
delay: settings["delay_${it.id}"]
delay: settings["delay_${it.id}"],
chime: settings["chime_${it.id}"]
]
}
subscribe(settings.z_water, "water.wet", onWater)
@@ -1257,20 +1370,37 @@ def onWater(evt) { onZoneEvent(evt, "water") }
private def onZoneEvent(evt, sensorType) {
LOG("onZoneEvent(${evt.displayName}, ${sensorType})")
state.alertType = sensorType
def zone = getZoneForDevice(evt.deviceId, sensorType)
if (!zone) {
log.warn "Cannot find zone for device ${evt.deviceId}"
state.alertType = "None"
return
}
if (zone.armed) {
state.alarms << evt.displayName
if (zone.zoneType == "alert" || !zone.delay || (state.stay && settings.stayDelayOff)) {
history("Alarm", "Alarm triggered by ${sensorType} sensor ${evt.displayName}")
activateAlarm()
} else {
history("Entry Delay", "Entry delay triggered by ${sensorType} sensor ${evt.displayName}")
if(settings.sirenEntryStrobe)
{
settings.alarms*.strobe()
}
keypads?.each() { it.setEntryDelay(state.delay) }
myRunIn(state.delay, activateAlarm)
}
}
else if (zone.chime)
{
chimeDevices?.each() {
it.beep()
}
}
}
def onLocation(evt) {
@@ -1311,23 +1441,27 @@ def onButtonEvent(evt) {
def armAway() {
LOG("armAway()")
history("Armed Away", "Alarm armed away")
if (!atomicState.armed || atomicState.stay) {
armPanel(false)
}
reportStatus()
}
def armStay() {
LOG("armStay()")
history("Armed Stay", "Alarm armed stay")
if (!atomicState.armed || !atomicState.stay) {
armPanel(true)
}
reportStatus()
}
def disarm() {
LOG("disarm()")
history("Disarmed", "Alarm disarmed")
if (atomicState.armed) {
state.armed = false
state.zones.each() {
@@ -1335,9 +1469,12 @@ def disarm() {
it.armed = false
}
}
keypads?.each() { it.setDisarmed() }
reset()
}
reportStatus()
}
def panic() {
@@ -1364,6 +1501,7 @@ def reset() {
notify(msg)
notifyVoice()
reportStatus()
}
def exitDelayExpired() {
@@ -1383,6 +1521,16 @@ def exitDelayExpired() {
it.armed = true
}
}
if(stay)
{
keypads?.each() { it.setArmedStay() }
}
else
{
keypads?.each() { it.setArmedAway() }
}
def msg = "${location.name}: all "
if (stay) {
@@ -1406,7 +1554,7 @@ private def armPanel(stay) {
state.zones.each() {
def zoneType = it.zoneType
if (zoneType == "exterior") {
if (it.delay) {
if (it.delay && !(stay && settings.stayExitDelayOff)) {
it.armed = false
armDelay = true
} else {
@@ -1424,10 +1572,22 @@ private def armPanel(stay) {
}
}
def delay = armDelay && !(stay && settings.stayDelayOff) ? atomicState.delay : 0
def delay = armDelay && !(stay && settings.stayExitDelayOff) ? atomicState.delay : 0
if (delay) {
keypads?.each() { it.setExitDelay(delay) }
myRunIn(delay, exitDelayExpired)
}
else
{
if(stay)
{
keypads?.each() { it.setArmedStay() }
}
else
{
keypads?.each() { it.setArmedAway() }
}
}
def mode = stay ? "STAY" : "AWAY"
def msg = "${location.name} "
@@ -1532,21 +1692,50 @@ def activateAlarm() {
log.warn "activateAlarm: false alarm"
return
}
history("Alarm", "Alarm Triggered")
switch (settings.sirenMode) {
case "Siren":
settings.alarms*.siren()
break
case "Strobe":
settings.alarms*.strobe()
break
case "Both":
settings.alarms*.both()
break
if(settings.sirenEntryStrobe)
{
settings.alarms*.off()
}
def atype = state.alertType
if ((atype == "Water" && settings.sirenOnWaterAlert) ||
(atype == "Smoke" && settings.sirenOnSmokeAlert) ||
((atype == "contact" || atype == "acceleration" || atype == "motion") && settings.sirenOnIntrusionAlert))
{
switch (settings.sirenMode) {
case "Siren":
settings.alarms*.siren()
break
case "Strobe":
settings.alarms*.strobe()
break
case "Both":
settings.alarms*.both()
break
}
}
else
{
log.debug "No siren for $atype Alert"
}
}
def activateAlarmPostDelay(String lastAlertType)
{
// no alarm check here as if door opens only for second with delay and system is not disarmed
// we still want alarm even if door is closed after delay.. Basically like real alarm the delay is only
// to disarm the system. Otherwise someone can open door come it, quickly close and there is not alarm LGK.
// issue here is that after delay we could have lost the alert type so pass it in
log.debug "activate alarm post delay check - alert type = $lastAlertType"
activateSirenAfterCheck(lastAlertType)
// Only turn on those switches that are currently off
def switchesOn = settings.switches?.findAll { it?.currentSwitch == "off" }
LOG("switchesOn: ${switchesOn}")
@@ -1570,6 +1759,8 @@ def activateAlarm() {
notify(msg)
notifyVoice()
reportStatus()
myRunIn(180, reset)
}
@@ -1668,12 +1859,61 @@ private def notifyVoice() {
}
}
def reportStatus()
{
log.debug "in report status"
log.debug "notification device = ${settings.notificationDevice}"
if (settings.notificationDevice)
{
def phrase = ""
if (state.alarms.size())
{
phrase = "Alert: Alarm at ${location.name}!"
notificationDevice.deviceNotification(phrase)
log.debug "sending notification alert: = $phrase"
def zones = "Zones: "
state.alarms.each()
{
//log.debug "in loop it"
//log.debug "it = $it"
zones = "Zones: "
zones += " $it" +"\n"
}
notificationDevice.deviceNotification(zones)
log.debug "sending nofication zones = $zones"
// send zone type
phrase = "AlertType: "
def atype = state.alertType
if (atype == null)
atype = "None"
phrase += " $atype"
notificationDevice.deviceNotification(phrase)
log.debug "sending nofication alert type = $phrase"
}
else
{
phrase = "Status: "
if (state.armed)
{
def mode = state.stay ? "Armed - Stay" : "Armed - Away"
phrase += "${mode}"
} else {
phrase += "Disarmed"
}
log.debug "sending notification status = $phrase"
notificationDevice.deviceNotification(phrase)
}
}
}
private def history(String event, String description = "") {
LOG("history(${event}, ${description})")
def history = atomicState.history
history << [time: now(), event: event, description: description]
if (history.size() > 10) {
if (history.size() > 20) {
history = history.sort{it.time}
history = history[1..-1]
}
@@ -1850,3 +2090,27 @@ private def LOG(message) {
private def STATE() {
//log.trace "state: ${state}"
}
def onAlarmSystemStatus(evt) {
LOG("Alarm System Status has been changed to '${evt.value}'")
String mode = evt.value.toLowerCase()
if (mode == "away") {
armAway()
} else if (mode == "stay") {
armStay()
} else if (mode == "off") {
disarm()
}
}
def setAlarmMode(name) {
LOG("Alarm System Status will be set to '${name}'")
def event = [
name: "alarmSystemStatus",
value: name,
isStateChange: true,
displayed: true,
description: "alarm system status is ${name}",
]
sendLocationEvent(event)
}