Compare commits

..

1 Commits

Author SHA1 Message Date
JC Wall
cab4456443 MSA-1987: Kwikset 2017-05-17 21:00:37 -07:00
24 changed files with 2018 additions and 199 deletions

View File

@@ -39,8 +39,8 @@ metadata {
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#00A0DC")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#CCCCCC")
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}

View File

@@ -37,8 +37,8 @@ metadata {
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#00A0DC")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#CCCCCC")
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}

View File

@@ -21,7 +21,6 @@ metadata {
capability "Battery"
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x80,0x84,0x85"
fingerprint mfr: "0086", prod: "0001", model: "0026", deviceJoinName: "Aeon Panic Button"
}
simulator {
@@ -131,12 +130,5 @@ def updated() {
}
def initialize() {
def zwMap = getZwaveInfo()
def buttons = 4 // Default for Key Fob
// Only one button for Aeon Panic Button
if (zwMap && zwMap.mfr == "0086" && zwMap.prod == "0001" && zwMap.model == "0026") {
buttons = 1
}
sendEvent(name: "numberOfButtons", value: buttons)
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,43 +0,0 @@
# Aeon Multisensor 6
Cloud Execution
Works with:
* [Aeon Labs MultiSensor 6](https://www.smartthings.com/products/aeon-labs-multisensor-6)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Motion Sensor** - can detect motion
* **Temperature Measurement** - defines device measures current temperature
* **Relative Humidity Measurement** - allow reading the relative humidity from devices that support it
* **Illuminance Measurement** - gives the illuminance reading from devices that support it
* **Ultraviolet Index** - gives the ability to get the ultraviolet index from devices that report it
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Sensor** - detects sensor events
* **Battery** - defines device uses a battery
* **Health Check** - indicates ability to get device health notifications
## Device Health
Aeon Labs MultiSensor 6 is polled by the hub.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __32min__ checkInterval
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
Pairing needs to be tried again by placing the device closer to the hub.
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
* [Aeon Labs MultiSensor 6 Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206157226)

View File

@@ -22,7 +22,6 @@ metadata {
capability "Configuration"
capability "Sensor"
capability "Battery"
capability "Health Check"
attribute "tamper", "enum", ["detected", "clear"]
attribute "batteryStatus", "string"
@@ -30,7 +29,6 @@ metadata {
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A"
fingerprint mfr:"0086", prod:"0102", model:"0064", deviceJoinName: "Aeon Labs MultiSensor 6"
}
simulator {
@@ -129,14 +127,7 @@ metadata {
}
}
def installed(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def updated() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
log.debug "Updated with settings: ${settings}"
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
@@ -335,13 +326,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
secure(zwave.batteryV1.batteryGet())
}
def configure() {
// This sensor joins as a secure device if you double-click the button to include it
log.debug "${device.displayName} is configuring its settings"

View File

@@ -9,7 +9,6 @@ metadata {
capability "Configuration"
capability "Refresh"
capability "Sensor"
capability "Health Check"
attribute "thermostatFanState", "string"
@@ -19,7 +18,6 @@ metadata {
command "quickSetHeat"
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
}
// simulator metadata
@@ -108,16 +106,6 @@ metadata {
}
}
def updated() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def installed() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}
def parse(String description)
{
def result = []
@@ -451,14 +439,6 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
], delay)
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
log.debug "ping() called"
refresh()
}
def configure() {
delayBetween([
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),

View File

@@ -107,8 +107,8 @@
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("acceleration", "device.acceleration") {
state("active", label:'vibration', icon:"st.motion.acceleration.active", backgroundColor:"#00a0dc")
state("inactive", label:'still', icon:"st.motion.acceleration.inactive", backgroundColor:"#cccccc")
state("active", label:'vibration', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'still', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
}

View File

@@ -12,13 +12,13 @@
*
*/
metadata {
definition (name: "Fortrezz Water Valve", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.watervalve") {
definition (name: "Fortrezz Water Valve", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Health Check"
capability "Valve"
capability "Refresh"
capability "Sensor"
fingerprint deviceId: "0x1000", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70"
fingerprint mfr:"0084", prod:"0213", model:"0215", deviceJoinName: "FortrezZ Water Valve"
}
@@ -34,22 +34,19 @@ metadata {
}
// tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
}
tiles {
standardTile("contact", "device.contact", width: 2, height: 2, canChangeIcon: true) {
state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
}
standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "valve"
details(["valve","refresh"])
main "contact"
details(["contact","refresh"])
}
}
@@ -65,23 +62,22 @@ def updated(){
def parse(String description) {
log.trace description
def result = null
def cmd = zwave.parse(description)
if (cmd) {
return zwaveEvent(cmd)
result = createEvent(zwaveEvent(cmd))
}
log.debug "Could not parse message"
return null
log.debug "Parse returned ${result?.descriptionText}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
def value = cmd.value ? "closed" : "open"
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
[:] // Handles all Z-Wave commands we aren't interested in
}
def open() {
@@ -102,9 +98,3 @@ def ping() {
def refresh() {
zwave.switchBinaryV1.switchBinaryGet().format()
}
def createEventWithDebug(eventMap) {
def event = createEvent(eventMap)
log.debug "Event created with ${event?.descriptionText}"
return event
}

View File

@@ -61,8 +61,8 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name: "motion", type: "generic", width: 6, height: 4) {
tileAttribute("device.motion", key: "PRIMARY_CONTROL") {
attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#00A0DC"
attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#cccccc"
attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#53a7c0"
attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#ffffff"
}
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {

View File

@@ -87,8 +87,8 @@ metadata {
state("closed", label: 'Closed', icon: "st.contact.contact.closed", backgroundColor: "#00a0dc")
}
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#00a0dc")
state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#cccccc")
state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#53a7c0")
state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#ffffff")
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label: '${currentValue}°',

View File

@@ -46,8 +46,8 @@
}
standardTile("motion", "device.motion", width: 2, height: 2) {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#CCCCCC")
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
state("offline", label:'${name}', icon:"st.motion.motion.inactive", backgroundColor:"#ff0000")
}

View File

@@ -35,7 +35,6 @@ import physicalgraph.zigbee.zcl.DataType
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,0020,0101,0B05", outClusters: "000A, 0019, 0B05", manufacturer: "Danalock", model: "V3-BTZB",deviceJoinName: "Danalock V3-BTZB"
}
tiles(scale: 2) {
@@ -171,4 +170,4 @@ private Map parseReportAttributeMessage(String description) {
log.debug "parseReportAttributeMessage() --- ignoring attribute"
}
return resultMap
}
}

View File

@@ -39,7 +39,7 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
@@ -83,9 +83,6 @@ def parse(String description) {
}
}
sendEvent(event)
//handle valve attribute
event.name = "valve"
sendEvent(event)
}
else {
def descMap = zigbee.parseDescriptionAsMap(description)

View File

@@ -30,15 +30,9 @@ metadata {
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
fingerprint mfr:"0086", prod:"0002", model:"001D", deviceJoinName: "Aeon Labs Door/Window Sensor (Gen 5)"
fingerprint mfr:"0086", prod:"0102", model:"0070", deviceJoinName: "Aeon Labs Door/Window Sensor 6"
fingerprint mfr:"0086", prod:"0102", model:"0059", deviceJoinName: "Aeon Labs Recessed Door Sensor"
fingerprint mfr:"014A", prod:"0001", model:"0002", deviceJoinName: "Ecolink Door/Window Sensor"
fingerprint mfr:"014A", prod:"0001", model:"0003", deviceJoinName: "Ecolink Tilt Sensor"
fingerprint mfr:"0086", prod:"0102", model:"0070", deviceJoinName: "Aeon Labs Door/Window Sensor 6"
fingerprint mfr:"011A", prod:"0601", model:"0903", deviceJoinName: "Enerwave Magnetic Door/Window Sensor"
fingerprint mfr:"014F", prod:"2001", model:"0102", deviceJoinName: "Nortek GoControl Door/Window Sensor"
fingerprint mfr:"0063", prod:"4953", model:"3031", deviceJoinName: "Jasco Hinge Pin Door Sensor"
fingerprint mfr:"019A", prod:"0003", model:"0003", deviceJoinName: "Sensative Strips"
}
// simulator metadata

View File

@@ -26,24 +26,7 @@ metadata {
fingerprint deviceId: "0x4003", inClusters: "0x98"
fingerprint deviceId: "0x4004", inClusters: "0x98"
fingerprint mfr:"0090", prod:"0001", model:"0236", deviceJoinName: "KwikSet SmartCode 910 Deadbolt Door Lock"
fingerprint mfr:"0090", prod:"0003", model:"0238", deviceJoinName: "KwikSet SmartCode 910 Deadbolt Door Lock"
fingerprint mfr:"0090", prod:"0001", model:"0001", deviceJoinName: "KwikSet SmartCode 910 Contemporary Deadbolt Door Lock"
fingerprint mfr:"0090", prod:"0003", model:"0339", deviceJoinName: "KwikSet SmartCode 912 Lever Door Lock"
fingerprint mfr:"0090", prod:"0003", model:"4006", deviceJoinName: "KwikSet SmartCode 914 Deadbolt Door Lock" //backlit version
fingerprint mfr:"0090", prod:"0003", model:"0440", deviceJoinName: "KwikSet SmartCode 914 Deadbolt Door Lock"
fingerprint mfr:"0090", prod:"0001", model:"0642", deviceJoinName: "KwikSet SmartCode 916 Touchscreen Deadbolt Door Lock"
fingerprint mfr:"0090", prod:"0003", model:"0642", deviceJoinName: "KwikSet SmartCode 916 Touchscreen Deadbolt Door Lock"
fingerprint mfr:"003B", prod:"6341", model:"0544", deviceJoinName: "Schlage Camelot Touchscreen Deadbolt Door Lock"
fingerprint mfr:"003B", prod:"6341", model:"5044", deviceJoinName: "Schlage Century Touchscreen Deadbolt Door Lock"
fingerprint mfr:"003B", prod:"634B", model:"504C", deviceJoinName: "Schlage Connected Keypad Lever Door Lock"
fingerprint mfr:"0129", prod:"0002", model:"0800", deviceJoinName: "Yale Touchscreen Deadbolt Door Lock" // YRD120
fingerprint mfr:"0129", prod:"0002", model:"0000", deviceJoinName: "Yale Touchscreen Deadbolt Door Lock" // YRD220, YRD240
fingerprint mfr:"0129", prod:"0002", model:"FFFF", deviceJoinName: "Yale Touchscreen Lever Door Lock" // YRD220
fingerprint mfr:"0129", prod:"0004", model:"0800", deviceJoinName: "Yale Push Button Deadbolt Door Lock" // YRD110
fingerprint mfr:"0129", prod:"0004", model:"0000", deviceJoinName: "Yale Push Button Deadbolt Door Lock" // YRD210
fingerprint mfr:"0129", prod:"0001", model:"0000", deviceJoinName: "Yale Push Button Lever Door Lock" // YRD210
fingerprint mfr:"0129", prod:"8002", model:"0600", deviceJoinName: "Yale Assure Lock with Bluetooth"
fingerprint mfr:"0129", prod:"0002", model:"0000", deviceJoinName: "Yale Key Free Touchscreen Deadbolt"
}
simulator {

View File

@@ -29,7 +29,7 @@ metadata {
fingerprint mfr: "0060", prod: "0001", model: "0002", deviceJoinName: "Everspring Motion Sensor" // Everspring SP814
fingerprint mfr: "0060", prod: "0001", model: "0003", deviceJoinName: "Everspring Motion Sensor" // Everspring HSP02
fingerprint mfr: "011A", prod: "0601", model: "0901", deviceJoinName: "Enerwave Motion Sensor" // Enerwave ZWN-BPC
fingerprint mfr: "0063", prod: "4953", model: "3133", deviceJoinName: "GE Portable Smart Motion Sensor"
fingerprint mfr: "0063", prod: "4953", model: "3133", deviceJoinName: "GE Smart Motion Sensor"
}
simulator {

View File

@@ -37,8 +37,8 @@ metadata {
tiles {
standardTile("motion", "device.motion", width: 3, height: 2) {
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#CCCCCC"
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
}
valueTile("temperature", "device.temperature", inactiveLabel: false) {

View File

@@ -22,7 +22,6 @@ metadata {
attribute "alarmState", "string"
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
}

View File

@@ -12,7 +12,7 @@
*
*/
metadata {
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.watervalve") {
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Health Check"
capability "Valve"
@@ -37,7 +37,7 @@ metadata {
// tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
@@ -45,7 +45,7 @@ metadata {
}
}
standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.contact", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
@@ -68,19 +68,18 @@ def updated() {
def parse(String description) {
log.trace "parse description : $description"
def result = null
def cmd = zwave.parse(description, [0x20: 1])
if (cmd) {
return zwaveEvent(cmd)
result = createEvent(zwaveEvent(cmd))
}
log.debug "Could not parse message"
return null
log.debug "Parse returned ${result?.descriptionText}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { //TODO should show MSR when device is discovered
@@ -90,22 +89,20 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
log.debug "productTypeId: ${cmd.productTypeId}"
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
updateDataValue("MSR", msr)
return createEventWithDebug([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
[descriptionText: "$device.displayName MSR: $msr", isStateChange: false]
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
return createEventWithDebug([descriptionText: cmd.toString(), isStateChange: true, displayed: true])
[descriptionText: cmd.toString(), isStateChange: true, displayed: true]
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
[:] // Handles all Z-Wave commands we aren't interested in
}
def open() {
@@ -141,9 +138,3 @@ def refresh() {
}
delayBetween(commands,100)
}
def createEventWithDebug(eventMap) {
def event = createEvent(eventMap)
log.debug "Event created with ${event?.descriptionText}"
return event
}

View File

@@ -0,0 +1,423 @@
definition(
name: 'Lock Manager',
namespace: 'ethayer',
author: 'Erik Thayer',
description: 'Manage locks and users',
category: 'Safety & Security',
iconUrl: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm.jpg',
iconX2Url: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm2x.jpg',
iconX3Url: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lm3x.jpg'
)
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
preferences {
page name: 'mainPage', title: 'Installed', install: true, uninstall: true, submitOnChange: true
page name: 'infoRefreshPage'
page name: 'notificationPage'
page name: 'helloHomePage'
page name: 'lockInfoPage'
page name: 'keypadPage'
page name: 'askAlexaPage'
}
def mainPage() {
dynamicPage(name: 'mainPage', install: true, uninstall: true, submitOnChange: true) {
section('Create') {
app(name: 'locks', appName: 'Lock', namespace: 'ethayer', title: 'New Lock', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/new-lock.png')
app(name: 'lockUsers', appName: 'Lock User', namespace: 'ethayer', title: 'New User', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/user-plus.png')
app(name: 'keypads', appName: 'Keypad', namespace: 'ethayer', title: 'New Keypad', multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/keypad-plus.png')
}
section('Locks') {
def lockApps = getLockApps()
lockApps = lockApps.sort{ it.lock.id }
if (lockApps) {
def i = 0
lockApps.each { lockApp ->
i++
href(name: "toLockInfoPage${i}", page: 'lockInfoPage', params: [id: lockApp.lock.id], required: false, title: lockApp.label, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png' )
}
}
}
section('Global Settings') {
href(name: 'toNotificationPage', page: 'notificationPage', title: 'Notification Settings', description: notificationPageDescription(), state: notificationPageDescription() ? 'complete' : '', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/bullhorn.png')
def actions = location.helloHome?.getPhrases()*.label
if (actions) {
href(name: 'toHelloHomePage', page: 'helloHomePage', title: 'Hello Home Settings', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/home.png')
}
def keypadApps = getKeypadApps()
if (keypadApps) {
href(name: 'toKeypadPage', page: 'keypadPage', title: 'Keypad Routines (optional)', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/keypad.png')
}
}
section('Advanced', hideable: true, hidden: true) {
input(name: 'overwriteMode', title: 'Overwrite?', type: 'bool', required: true, defaultValue: true, description: 'Overwrite mode automatically deletes codes not in the users list')
input(name: 'enableDebug', title: 'Enable IDE debug messages?', type: 'bool', required: true, defaultValue: false, description: 'Show activity from Lock Manger in logs for debugging.')
paragraph 'Lock Manager © 2017 v1.4'
}
}
}
def lockInfoPage(params) {
dynamicPage(name:"lockInfoPage", title:"Lock Info") {
def lockApp = getLockAppByIndex(params)
if (lockApp) {
section("${lockApp.label}") {
def complete = lockApp.isCodeComplete()
def refreshComplete = lockApp.isRefreshComplete()
if (!complete) {
paragraph 'App is learning codes. They will appear here when received.\n Lock may require special DTH to work properly'
lockApp.lock.poll()
}
if (!refreshComplete) {
paragraph 'App is in refresh mode.'
}
def codeData = lockApp.codeData()
if (codeData) {
def setCode = ''
def usage
def para
def image
def sortedCodes = codeData.sort{it.value.slot}
sortedCodes.each { data ->
data = data.value
if (data.codeState != 'unknown') {
def userApp = lockApp.findSlotUserApp(data.slot)
para = "Slot ${data.slot}"
if (data.code) {
para = para + "\nCode: ${data.code}"
}
if (userApp) {
para = para + userApp.getLockUserInfo(lockApp.lock)
image = userApp.lockInfoPageImage(lockApp.lock)
} else {
image = 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png'
}
if (data.codeState == 'refresh') {
para = para +'\nPending refresh...'
}
paragraph para, image: image
}
}
}
}
section('Lock Settings') {
def pinLength = lockApp.pinLength()
if (pinLength) {
paragraph "Required Length: ${pinLength}"
}
}
} else {
section() {
paragraph 'Error: Can\'t find lock!'
}
}
}
}
def notificationPage() {
dynamicPage(name: 'notificationPage', title: 'Global Notification Settings') {
section {
paragraph 'These settings will apply to all users. Settings on individual users will override these settings'
input('recipients', 'contact', title: 'Send notifications to', submitOnChange: true, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/book.png')
href(name: 'toAskAlexaPage', title: 'Ask Alexa', page: 'askAlexaPage', image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/Alexa.png')
if (!recipients) {
input(name: 'phone', type: 'text', title: 'Text This Number', description: 'Phone number', required: false, submitOnChange: true)
paragraph 'For multiple SMS recipients, separate phone numbers with a semicolon(;)'
input(name: 'notification', type: 'bool', title: 'Send A Push Notification', description: 'Notification', required: false, submitOnChange: true)
}
if (phone != null || notification || recipients) {
input(name: 'notifyAccess', title: 'on User Entry', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
input(name: 'notifyLock', title: 'on Lock', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
input(name: 'notifyAccessStart', title: 'when granting access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/check-circle-o.png')
input(name: 'notifyAccessEnd', title: 'when revoking access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png')
}
}
section('Only During These Times (optional)') {
input(name: 'notificationStartTime', type: 'time', title: 'Notify Starting At This Time', description: null, required: false)
input(name: 'notificationEndTime', type: 'time', title: 'Notify Ending At This Time', description: null, required: false)
}
}
}
def helloHomePage() {
dynamicPage(name: 'helloHomePage', title: 'Global Hello Home Settings (optional)') {
def actions = location.helloHome?.getPhrases()*.label
actions?.sort()
section('Hello Home Phrases') {
input(name: 'manualUnlockRoutine', title: 'On Manual Unlock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
input(name: 'manualLockRoutine', title: 'On Manual Lock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
input(name: 'codeUnlockRoutine', title: 'On Code Unlock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png' )
paragraph 'Supported on some locks:'
input(name: 'codeLockRoutine', title: 'On Code Lock', type: 'enum', options: actions, required: false, multiple: true, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
paragraph 'These restrictions apply to all the above:'
input "userNoRunPresence", "capability.presenceSensor", title: "DO NOT run Actions if any of these are present:", multiple: true, required: false
input "userDoRunPresence", "capability.presenceSensor", title: "ONLY run Actions if any of these are present:", multiple: true, required: false
}
}
}
def askAlexaPage() {
dynamicPage(name: 'askAlexaPage', title: 'Ask Alexa Message Settings') {
section('Que Messages with the Ask Alexa app') {
paragraph 'These settings apply to all users. These settings are overridable on the user level'
input(name: 'alexaAccess', title: 'on User Entry', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/unlock-alt.png')
input(name: 'alexaLock', title: 'on Lock', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/lock.png')
input(name: 'alexaAccessStart', title: 'when granting access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/check-circle-o.png')
input(name: 'alexaAccessEnd', title: 'when revoking access', type: 'bool', required: false, image: 'https://dl.dropboxusercontent.com/u/54190708/LockManager/times-circle-o.png')
}
section('Only During These Times (optional)') {
input(name: 'alexaStartTime', type: 'time', title: 'Notify Starting At This Time', description: null, required: false)
input(name: 'alexaEndTime', type: 'time', title: 'Notify Ending At This Time', description: null, required: false)
}
}
}
def keypadPage() {
dynamicPage(name: 'keypadPage',title: 'Keypad Settings (optional)', install: true, uninstall: true) {
def actions = location.helloHome?.getPhrases()*.label
actions?.sort()
section("Settings") {
paragraph 'settings here are for all users. When any user enters their passcode, run these routines'
input(name: 'armRoutine', title: 'Arm/Away routine', type: 'enum', options: actions, required: false, multiple: true)
input(name: 'disarmRoutine', title: 'Disarm routine', type: 'enum', options: actions, required: false, multiple: true)
input(name: 'stayRoutine', title: 'Arm/Stay routine', type: 'enum', options: actions, required: false, multiple: true)
input(name: 'nightRoutine', title: 'Arm/Night routine', type: 'enum', options: actions, required: false, multiple: true)
}
}
}
def fancyString(listOfStrings) {
listOfStrings.removeAll([null])
def fancify = { list ->
return list.collect {
def label = it
if (list.size() > 1 && it == list[-1]) {
label = "and ${label}"
}
label
}.join(", ")
}
return fancify(listOfStrings)
}
def notificationPageDescription() {
def parts = []
def msg = ""
if (settings.phone) {
parts << "SMS to ${phone}"
}
if (settings.recipients) {
parts << 'Sent to Address Book'
}
if (settings.notification) {
parts << 'Push Notification'
}
msg += fancyString(parts)
parts = []
if (settings.notifyAccess) {
parts << 'on entry'
}
if (settings.notifyLock) {
parts << 'on lock'
}
if (settings.notifyAccessStart) {
parts << 'when granting access'
}
if (settings.notifyAccessEnd) {
parts << 'when revoking access'
}
if (settings.notificationStartTime) {
parts << "starting at ${settings.notificationStartTime}"
}
if (settings.notificationEndTime) {
parts << "ending at ${settings.notificationEndTime}"
}
if (parts.size()) {
msg += ': '
msg += fancyString(parts)
}
return msg
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
def children = getChildApps()
log.debug "there are ${children.size()} lock users"
}
def getLockAppByIndex(params) {
def id = ''
// Assign params to id. Sometimes parameters are double nested.
if (params.id) {
id = params.id
} else if (params.params){
id = params.params.id
} else if (state.lastLock) {
id = state.lastLock
}
state.lastLock = id
def lockApp = false
def lockApps = getLockApps()
if (lockApps) {
def i = 0
lockApps.each { app ->
if (app.lock.id == state.lastLock) {
lockApp = app
}
}
}
return lockApp
}
def availableSlots(selectedSlot) {
def options = []
(1..30).each { slot->
def children = getChildApps()
def available = true
children.each { child ->
def userSlot = child.userSlot
if (!selectedSlot) {
selectedSlot = 0
}
if (!userSlot) {
userSlot = 0
}
if (userSlot.toInteger() == slot && selectedSlot.toInteger() != slot) {
available = false
}
}
if (available) {
options << ["${slot}": "Slot ${slot}"]
}
}
return options
}
def keypadMatchingUser(usedCode){
def correctUser = false
def userApps = getUserApps()
userApps.each { userApp ->
def code
log.debug userApp.userCode
if (userApp.isActiveKeypad()) {
code = userApp.userCode.take(4)
log.debug "code: ${code} used: ${usedCode}"
if (code.toInteger() == usedCode.toInteger()) {
correctUser = userApp
}
}
}
return correctUser
}
def findAssignedChildApp(lock, slot) {
def childApp
def userApps = getUserApps()
userApps.each { child ->
if (child.userSlot?.toInteger() == slot) {
childApp = child
}
}
return childApp
}
def getUserApps() {
def userApps = []
def children = getChildApps()
children.each { child ->
if (child.userSlot) {
userApps.push(child)
}
}
return userApps
}
def getKeypadApps() {
def keypadApps = []
def children = getChildApps()
children.each { child ->
if (child.keypad) {
keypadApps.push(child)
}
}
return keypadApps
}
def getLockApps() {
def lockApps = []
def children = getChildApps()
children.each { child ->
if (child.lock) {
lockApps.push(child)
}
}
return lockApps
}
def setAccess() {
def lockApps = getLockApps()
lockApps.each { lockApp ->
lockApp.makeRequest()
}
}
def debuggerOn() {
// needed for child apps
return enableDebug
}
def debugger(message) {
def doDebugger = debuggerOn()
if (enableDebug) {
return log.debug(message)
}
}
def anyoneHome(sensors) {
def result = false
if(sensors.findAll { it?.currentPresence == "present" }) {
result = true
}
result
}
def executeHelloPresenceCheck(routines) {
if (userNoRunPresence && userDoRunPresence == null) {
if (!anyoneHome(userNoRunPresence)) {
location.helloHome.execute(routines)
}
} else if (userDoRunPresence && userNoRunPresence == null) {
if (anyoneHome(userDoRunPresence)) {
location.helloHome.execute(routines)
}
} else if (userDoRunPresence && userNoRunPresence) {
if (anyoneHome(userDoRunPresence) && !anyoneHome(userNoRunPresence)) {
location.helloHome.execute(routines)
}
} else {
location.helloHome.execute(routines)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -98,7 +98,7 @@ def motionHandler(evt) {
else {
state.motionStopTime = now()
if(delayMinutes) {
runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: true])
runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: false])
} else {
turnOffMotionAfterDelay()
}

View File

@@ -125,19 +125,19 @@
if(allOk) {
if(everyoneIsAway() && (state.sunMode == "sunrise")) {
log.debug("Home is Empty Setting New Away Mode")
log.info("Home is Empty Setting New Away Mode")
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
runIn(delay, "setAway")
}
if(everyoneIsAway() && (state.sunMode == "sunset")) {
log.debug("Home is Empty Setting New Away Mode")
log.info("Home is Empty Setting New Away Mode")
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
runIn(delay, "setAway")
}
else {
log.debug("Home is Occupied Setting New Home Mode")
log.info("Home is Occupied Setting New Home Mode")
setHome()
@@ -152,7 +152,7 @@
log.debug("Checking if everyone is away")
if(everyoneIsAway()) {
log.debug("Nobody is home, running away sequence")
log.info("Nobody is home, running away sequence")
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
runIn(delay, "setAway")
}
@@ -161,7 +161,7 @@
else {
def lastTime = state[evt.deviceId]
if (lastTime == null || now() - lastTime >= 1 * 60000) {
log.debug("Someone is home, running home sequence")
log.info("Someone is home, running home sequence")
setHome()
}
state[evt.deviceId] = now()
@@ -175,14 +175,14 @@
if(everyoneIsAway()) {
if(state.sunMode == "sunset") {
def message = "Performing \"${awayNight}\" for you as requested."
log.debug(message)
log.info(message)
sendAway(message)
location.helloHome.execute(settings.awayNight)
}
else if(state.sunMode == "sunrise") {
def message = "Performing \"${awayDay}\" for you as requested."
log.debug(message)
log.info(message)
sendAway(message)
location.helloHome.execute(settings.awayDay)
}
@@ -192,19 +192,19 @@
}
else {
log.debug("Somebody returned home before we set to '${newAwayMode}'")
log.info("Somebody returned home before we set to '${newAwayMode}'")
}
}
//set home mode when house is occupied
def setHome() {
sendOutOfDateNotification()
log.debug("Setting Home Mode!!")
log.info("Setting Home Mode!!")
if(anyoneIsHome()) {
if(state.sunMode == "sunset"){
if (location.mode != "${homeModeNight}"){
def message = "Performing \"${homeNight}\" for you as requested."
log.debug(message)
log.info(message)
sendHome(message)
location.helloHome.execute(settings.homeNight)
}
@@ -213,7 +213,7 @@
if(state.sunMode == "sunrise"){
if (location.mode != "${homeModeDay}"){
def message = "Performing \"${homeDay}\" for you as requested."
log.debug(message)
log.info(message)
sendHome(message)
location.helloHome.execute(settings.homeDay)
}
@@ -329,4 +329,4 @@
sendNotification("Your version of Hello, Home Phrase Director is currently out of date. Please look for the new version of Hello, Home Phrase Director now called 'Routine Director' in the marketplace.")
state.lastTime = (new Date() + 31).getTime()
}
}
}