diff --git a/devicetypes/alarmdecoder/alarmdecoder-virtual-carbon-monoxide-detector.src/alarmdecoder-virtual-carbon-monoxide-detector.groovy b/devicetypes/alarmdecoder/alarmdecoder-virtual-carbon-monoxide-detector.src/alarmdecoder-virtual-carbon-monoxide-detector.groovy index d7104d8..d68a447 100644 --- a/devicetypes/alarmdecoder/alarmdecoder-virtual-carbon-monoxide-detector.src/alarmdecoder-virtual-carbon-monoxide-detector.groovy +++ b/devicetypes/alarmdecoder/alarmdecoder-virtual-carbon-monoxide-detector.src/alarmdecoder-virtual-carbon-monoxide-detector.groovy @@ -28,6 +28,9 @@ metadata { namespace: APPNAMESPACE, author: "Nu Tech Software Solutions, Inc.") { capability "CarbonMonoxideDetector" + capability "Tamper Alert" + attribute "low_battery", "bool" + attribute "last_checkin", "number" } // tile definitions @@ -67,6 +70,26 @@ metadata { title: "Zone Number", description: "Zone # required for zone events.", required: false) + input( + name: "serial", type: + "string", title: "Serial Number", + description: "The serial number of an RF device.", + required: false) + if (serial != null) { + input( + name: "zoneLoop", type: + "number", title: "Zone Loop", + range: "1..4", + description: "The loop to use for zone open/close.", + required: true) + input( + name: "tamperLoop", type: + "number", title: "Tamper Loop", + range: "1..4", + description: "The loop to use to detect tamper.", + defaultValue: 4, + required: false) + } } } @@ -82,9 +105,15 @@ metadata { def installed() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } def updated() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } diff --git a/devicetypes/alarmdecoder/alarmdecoder-virtual-contact-sensor.src/alarmdecoder-virtual-contact-sensor.groovy b/devicetypes/alarmdecoder/alarmdecoder-virtual-contact-sensor.src/alarmdecoder-virtual-contact-sensor.groovy index 0167270..6fd0172 100644 --- a/devicetypes/alarmdecoder/alarmdecoder-virtual-contact-sensor.src/alarmdecoder-virtual-contact-sensor.groovy +++ b/devicetypes/alarmdecoder/alarmdecoder-virtual-contact-sensor.src/alarmdecoder-virtual-contact-sensor.groovy @@ -28,6 +28,9 @@ metadata { namespace: APPNAMESPACE, author: "Nu Tech Software Solutions, Inc.") { capability "Contact Sensor" + capability "Tamper Alert" + attribute "low_battery", "bool" + attribute "last_checkin", "number" } // tile definitions @@ -66,6 +69,27 @@ metadata { "number", title: "Zone Number", description: "Zone # required for zone events.", required: false) + input( + name: "serial", type: + "string", title: "Serial Number", + submitOnChange: true, + description: "The serial number of an RF device.", + required: false) + if (serial != null) { + input( + name: "zoneLoop", type: + "number", title: "Zone Loop", + range: "1..4", + description: "The loop to use for zone open/close.", + required: true) + input( + name: "tamperLoop", type: + "number", title: "Tamper Loop", + range: "1..4", + description: "The loop to use to detect tamper.", + defaultValue: 4, + required: false) + } } } @@ -81,9 +105,15 @@ metadata { def installed() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } def updated() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } diff --git a/devicetypes/alarmdecoder/alarmdecoder-virtual-motion-detector.src/alarmdecoder-virtual-motion-detector.groovy b/devicetypes/alarmdecoder/alarmdecoder-virtual-motion-detector.src/alarmdecoder-virtual-motion-detector.groovy index c7e948d..5fa1e01 100644 --- a/devicetypes/alarmdecoder/alarmdecoder-virtual-motion-detector.src/alarmdecoder-virtual-motion-detector.groovy +++ b/devicetypes/alarmdecoder/alarmdecoder-virtual-motion-detector.src/alarmdecoder-virtual-motion-detector.groovy @@ -28,6 +28,9 @@ metadata { namespace: APPNAMESPACE, author: "Nu Tech Software Solutions, Inc.") { capability "Motion Sensor" + capability "Tamper Alert" + attribute "low_battery", "bool" + attribute "last_checkin", "number" } // tile definitions @@ -69,6 +72,26 @@ metadata { title: "Zone Number", description: "Zone # required for zone events.", required: false) + input( + name: "serial", type: + "string", title: "Serial Number", + description: "The serial number of an RF device.", + required: false) + if (serial != null) { + input( + name: "zoneLoop", type: + "number", title: "Zone Loop", + range: "1..4", + description: "The loop to use for zone open/close.", + required: true) + input( + name: "tamperLoop", type: + "number", title: "Tamper Loop", + range: "1..4", + description: "The loop to use to detect tamper.", + defaultValue: 4, + required: false) + } } } @@ -84,11 +107,17 @@ metadata { def installed() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } def updated() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } // FIXME: what? diff --git a/devicetypes/alarmdecoder/alarmdecoder-virtual-shock-sensor.src/alarmdecoder-virtual-shock-sensor.groovy b/devicetypes/alarmdecoder/alarmdecoder-virtual-shock-sensor.src/alarmdecoder-virtual-shock-sensor.groovy index f8c838e..97e0527 100644 --- a/devicetypes/alarmdecoder/alarmdecoder-virtual-shock-sensor.src/alarmdecoder-virtual-shock-sensor.groovy +++ b/devicetypes/alarmdecoder/alarmdecoder-virtual-shock-sensor.src/alarmdecoder-virtual-shock-sensor.groovy @@ -28,6 +28,9 @@ metadata { namespace: APPNAMESPACE, author: "Nu Tech Software Solutions, Inc.") { capability "ShockSensor" + capability "Tamper Alert" + attribute "low_battery", "bool" + attribute "last_checkin", "number" } // tile definitions @@ -68,6 +71,26 @@ metadata { title: "Zone Number", description: "Zone # required for zone events.", required: false) + input( + name: "serial", type: + "string", title: "Serial Number", + description: "The serial number of an RF device.", + required: false) + if (serial != null) { + input( + name: "zoneLoop", type: + "number", title: "Zone Loop", + range: "1..4", + description: "The loop to use for zone open/close.", + required: true) + input( + name: "tamperLoop", type: + "number", title: "Tamper Loop", + range: "1..4", + description: "The loop to use to detect tamper.", + defaultValue: 4, + required: false) + } } } @@ -83,9 +106,15 @@ metadata { def installed() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } def updated() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } diff --git a/devicetypes/alarmdecoder/alarmdecoder-virtual-smoke-alarm.src/alarmdecoder-virtual-smoke-alarm.groovy b/devicetypes/alarmdecoder/alarmdecoder-virtual-smoke-alarm.src/alarmdecoder-virtual-smoke-alarm.groovy index a0a6ffe..a05c60c 100644 --- a/devicetypes/alarmdecoder/alarmdecoder-virtual-smoke-alarm.src/alarmdecoder-virtual-smoke-alarm.groovy +++ b/devicetypes/alarmdecoder/alarmdecoder-virtual-smoke-alarm.src/alarmdecoder-virtual-smoke-alarm.groovy @@ -28,6 +28,9 @@ metadata { namespace: APPNAMESPACE, author: "Nu Tech Software Solutions, Inc.") { capability "Smoke Detector" + capability "Tamper Alert" + attribute "low_battery", "bool" + attribute "last_checkin", "number" } // tile definitions @@ -67,6 +70,26 @@ metadata { title: "Zone Number", description: "Zone # required for zone events.", required: false) + input( + name: "serial", type: + "string", title: "Serial Number", + description: "The serial number of an RF device.", + required: false) + if (serial != null) { + input( + name: "zoneLoop", type: + "number", title: "Zone Loop", + range: "1..4", + description: "The loop to use for zone open/close.", + required: true) + input( + name: "tamperLoop", type: + "number", title: "Tamper Loop", + range: "1..4", + description: "The loop to use to detect tamper.", + defaultValue: 4, + required: false) + } } } @@ -82,9 +105,15 @@ metadata { def installed() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } def updated() { updateDataValue("invert", invert.toString()) updateDataValue("zone", zone.toString()) + updateDataValue("serial", serial) + updateDataValue("zoneLoop", zoneLoop.toString()) + updateDataValue("tamperLoop", tamperLoop.toString()) } diff --git a/smartapps/alarmdecoder/alarmdecoder-service.src/alarmdecoder-service.groovy b/smartapps/alarmdecoder/alarmdecoder-service.src/alarmdecoder-service.groovy index 1e4a9cd..6e0c1fc 100644 --- a/smartapps/alarmdecoder/alarmdecoder-service.src/alarmdecoder-service.groovy +++ b/smartapps/alarmdecoder/alarmdecoder-service.src/alarmdecoder-service.groovy @@ -1986,6 +1986,57 @@ def rfxSet(evt) { def device_name = "RFX-${sn}-${bat}-${supv}-" + "${loop0}-${loop1}-${loop2}-${loop3}" + def d = getChildDevices().findAll { + it.deviceNetworkId.contains(":switch") && + it.getDataValue("serial") == sn + } + + if (d) { + d.each { + def zoneLoop = it.getDataValue("zoneLoop") + def tamperLoop = it.getDataValue("tamperLoop") + + if (zoneLoop != null && zoneLoop != "null") { + if ((zoneLoop == "1" && loop0 == "1") || (zoneLoop == "2" && loop1 == "1") || (zoneLoop == "3" && loop2 == "1") || (zoneLoop == "4" && loop3 == "1")) { + _sendEventTranslate(it, ("on"), false) + } else { + _sendEventTranslate(it, ("off"), false) + } + } + + if (tamperLoop != null && tamperLoop != "null") { + if ((tamperLoop == "1" && loop0 == "1") || (tamperLoop == "2" && loop1 == "1") || (tamperLoop == "3" && loop2 == "1") || (tamperLoop == "4" && loop3 == "1")) { + it.sendEvent( + name: "tamper", + value: "detected" + ) + } else { + it.sendEvent( + name: "tamper", + value: "clear" + ) + } + } + } + + d.each { + it.sendEvent( + name: "low_battery", + value: bat == "1" + ) + } + + if (supv == "1") { + def last_checkin = now() + d.each { + it.sendEvent( + name: "last_checkin", + value: last_checkin + ) + } + } + } + def children = getChildDevices() children.each { if (it.deviceNetworkId.contains(":RFX-")) { @@ -2022,11 +2073,9 @@ def rfxSet(evt) { sent = true - } else { - if (debug) log.info("rfxSet device: ${device_name} no match ${match}") - } + } } - } + } if (!sent) { log.warn("rfxSet: Could not find '${device_name}|XXX' device.") @@ -2092,7 +2141,7 @@ def zoneOn(evt) { // Find all :switch devices with a matching zone the event. def d = getChildDevices().findAll { it.deviceNetworkId.contains(":switch") && - it.getDataValue("zone") == evt.value + it.getDataValue("zone") == evt.value && it.getDataValue("serial") == null } if (d) { @@ -2114,7 +2163,7 @@ def zoneOff(evt) { def d = getChildDevices().findAll { it.deviceNetworkId.contains(":switch") && - it.getDataValue("zone") == evt.value + it.getDataValue("zone") == evt.value && it.getDataValue("serial") == null } if (d) { @@ -3039,7 +3088,7 @@ private getDeviceNamePart(d) { * Default clear = off, detected(Alerting) = on * */ -def _sendEventTranslate(ad2d, state) { +def _sendEventTranslate(ad2d, state, stateChange = true) { // Grab the devices preferences for inverting def invert = (ad2d.device.getDataValue("invert") == "true" ? true : false) @@ -3057,7 +3106,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "switch", value: (sval ? "on" : "off"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) } @@ -3075,7 +3124,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "contact", value: (sval ? "open" : "closed"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) } @@ -3093,7 +3142,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "motion", value: (sval ? "active" : "inactive"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) } @@ -3111,7 +3160,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "shock", value: (sval ? "detected" : "clear"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) } @@ -3129,7 +3178,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "carbonMonoxide", value: (sval ? "detected" : "clear"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) } @@ -3147,7 +3196,7 @@ def _sendEventTranslate(ad2d, state) { ad2d.sendEvent( name: "smoke", value: (sval ? "detected" : "clear"), - isStateChange: true, + isStateChange: stateChange, filtered: true ) }