Compare commits

..

1 Commits

5 changed files with 112 additions and 39 deletions

View File

@@ -124,10 +124,6 @@ metadata {
input "holdType", "enum", title: "Hold Type",
description: "When changing temperature, use Temporary (Until next transition) or Permanent hold (default)",
required: false, options:["Temporary", "Permanent"]
input "deadbandSetting", "number", title: "Minimum temperature difference between the desired Heat and Cool " +
"temperatures in Auto mode:\nNote! This must be the same as configured on the thermostat",
description: "temperature difference °F", defaultValue: 5,
required: false
}
}
@@ -176,7 +172,7 @@ def generateEvent(Map results) {
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
sendValue = getTempInLocalScale(value, "F") // API return temperature values in F
event << [value: sendValue, unit: locationScale]
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
// Old attributes, keeping for backward compatibility
sendValue = getTempInLocalScale(value, "F") // API return temperature values in F
event << [value: sendValue, unit: locationScale, displayed: false]
@@ -469,7 +465,7 @@ def enforceSetpointLimits(setpoint, data, raise = null) {
def maxSetpoint = (setpoint == "heatingSetpoint") ? device.getDataValue("maxHeatingSetpointFahrenheit") : device.getDataValue("maxCoolingSetpointFahrenheit")
minSetpoint = minSetpoint ? Double.parseDouble(minSetpoint) : ((setpoint == "heatingSetpoint") ? 45 : 65) // default 45 heat, 65 cool
maxSetpoint = maxSetpoint ? Double.parseDouble(maxSetpoint) : ((setpoint == "heatingSetpoint") ? 79 : 92) // default 79 heat, 92 cool
def deadband = deadbandSetting ? deadbandSetting : 5 // °F
def deadband = 5 // °F
def delta = (locationScale == "F") ? 1 : 0.5
def targetValue = getTempInDeviceScale(data.targetValue, locationScale)
def heatingSetpoint = getTempInDeviceScale(data.heatingSetpoint, locationScale)
@@ -479,8 +475,8 @@ def enforceSetpointLimits(setpoint, data, raise = null) {
targetValue = maxSetpoint
} else if (targetValue < minSetpoint) {
targetValue = minSetpoint
} else if ((raise != null) && ((setpoint == "heatingSetpoint" && targetValue == heatingSetpoint) ||
(setpoint == "coolingSetpoint" && targetValue == coolingSetpoint))) {
} else if ((setpoint == "heatingSetpoint" && targetValue == heatingSetpoint) ||
(setpoint == "coolingSetpoint" && targetValue == coolingSetpoint)){
// Ensure targetValue differes from old. When location scale differs from device,
// converting between C -> F -> C may otherwise result in no change.
targetValue += raise ? delta : - delta

View File

@@ -320,18 +320,7 @@ def zwaveEvent(DoorLockOperationReport cmd) {
result << response(secure(zwave.associationV1.associationGet(groupingIdentifier:1)))
}
}
if (generatesDoorLockOperationReportBeforeAlarmReport()) {
// we're expecting lock events to come after notification events, but for specific yale locks they come out of order
runIn(3, "delayLockEvent", [data: [map: map]])
return [:]
} else {
return result ? [createEvent(map), *result] : createEvent(map)
}
}
def delayLockEvent(data) {
log.debug "Sending cached lock operation: $data.map"
sendEvent(data.map)
result ? [createEvent(map), *result] : createEvent(map)
}
/**
@@ -1670,19 +1659,6 @@ def isYaleLock() {
}
/**
* Returns true if this lock generates door lock operation report before alarm report, false otherwise
* @return true if this lock generates door lock operation report before alarm report, false otherwise
*/
def generatesDoorLockOperationReportBeforeAlarmReport() {
//Fix for ICP-2367, ICP-2366
if(isYaleLock() && "0007" == zwaveInfo.prod && "0001" == zwaveInfo.model) {
//Yale Keyless Connected Smart Door Lock
return true
}
return false
}
/**
* Generic function for reading code Slot ID from AlarmReport command
* @param cmd: The AlarmReport command
* @return user code slot id

View File

@@ -31,12 +31,6 @@ metadata {
fingerprint mfr:"001D", prod:"1603", model:"0334", deviceJoinName: "Leviton 15A Split Duplex Receptacle"
fingerprint mfr:"011A", prod:"0101", model:"0102", deviceJoinName: "Enerwave On/Off Switch"
fingerprint mfr:"011A", prod:"0101", model:"0603", deviceJoinName: "Enerwave Duplex Receptacle"
fingerprint mfr:"0039", prod:"5052", model:"3038", deviceJoinName: "Honeywell Z-Wave Plug-in Switch"
fingerprint mfr:"0039", prod:"5052", model:"3033", deviceJoinName: "Honeywell Z-Wave Plug-in Switch (Dual Outlet)"
fingerprint mfr:"0039", prod:"4F50", model:"3032", deviceJoinName: "Honeywell Z-Wave Plug-in Outdoor Smart Switch"
fingerprint mfr:"0039", prod:"4952", model:"3036", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Switch"
fingerprint mfr:"0039", prod:"4952", model:"3037", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Toggle Switch"
fingerprint mfr:"0039", prod:"4952", model:"3133", deviceJoinName: "Honeywell Z-Wave In-Wall Tamper Resistant Duplex Receptacle"
}
// simulator metadata

View File

@@ -25,6 +25,12 @@ metadata {
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "GE Wall Switch"
fingerprint mfr:"0063", prod:"5052", deviceJoinName: "GE Plug-In Switch"
fingerprint mfr:"0113", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
fingerprint mfr:"0039", prod:"5052", model:"3038", deviceJoinName: "Honeywell Z-Wave Plug-in Switch"
fingerprint mfr:"0039", prod:"5052", model:"3033", deviceJoinName: "Honeywell Z-Wave Plug-in Switch (Dual Outlet)"
fingerprint mfr:"0039", prod:"4F50", model:"3032", deviceJoinName: "Honeywell Z-Wave Plug-in Outdoor Smart Switch"
fingerprint mfr:"0039", prod:"4952", model:"3036", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Switch"
fingerprint mfr:"0039", prod:"4952", model:"3037", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Toggle Switch"
fingerprint mfr:"0039", prod:"4952", model:"3133", deviceJoinName: "Honeywell Z-Wave In-Wall Tamper Resistant Duplex Receptacle"
}
// simulator metadata

View File

@@ -0,0 +1,101 @@
/**
* Door Ajar
*
* Copyright 2018 Robin Adams
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "Door Ajar",
namespace: "robinaa2",
author: "Robin Adams",
description: "Notifies when a door has been open over a specified amount of time.",
category: "",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
preferences {
section("Door sensor:") {
input "sensor", "capability.contactSensor", required: true, title: "Where?"
}
section("Timeout:") {
input "minutes", "number", required: true, title: "Minutes?"
}
section("Send Notifications?") {
input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "Warn with text message (optional)",
description: "Phone Number", required: false
}
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
subscribe(sensor, "contact.open", doorOpenHandler)
subscribe(sensor, "contact.closed", doorClosedHandler)
}
def doorOpenHandler(evt) {
log.debug "doorOpenHandler called: $evt"
runIn(60 * minutes, check)
}
def doorClosedHandler(evt) {
log.debug "doorClosedHandler called: $evt"
}
def check() {
log.debug "In check scheduled method"
def state = sensor.currentState("contact")
if (state.value == "open") {
// get the time elapsed between now and when the motion reported inactive
def elapsed = now() - state.date.time
// elapsed time is in milliseconds, so the threshold must be converted to milliseconds too
def threshold = 1000 * 60 * minutes
if (elapsed >= threshold) {
log.debug "Door has been ajar long enough ($elapsed ms)"
def message = "The ${sensor.displayName} is open!"
if (location.contactBookEnabled && recipients) {
log.debug "Contact Book enabled!"
sendNotificationToContacts(message, recipients)
} else {
log.debug "Contact Book not enabled"
if (phone) {
sendSms(phone, message)
}
}
} else {
log.debug "Door has been re-opened since timer started ($elapsed ms): doing nothing"
}
} else {
// Motion active; just log it and do nothing
log.debug "Door was closed, do nothing"
}
}