mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-16 21:03:28 +00:00
MSA-1994: keypad
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user