Compare commits

..

1 Commits

Author SHA1 Message Date
Ricardo Melendez
9f5f1056ff MSA-2426: Nest Thermostat 2017-12-04 11:45:21 -08:00
24 changed files with 341 additions and 304 deletions

View File

@@ -28,12 +28,7 @@ metadata {
capability "Health Check"
fingerprint manufacturer: "015D", prod: "0221", model: "251C", deviceJoinName: "Show Home 2-Channel Smart Plug"
fingerprint manufacturer: "0312", prod: "0221", model: "251C", deviceJoinName: "Inovelli 2-Channel Smart Plug"
fingerprint manufacturer: "0312", prod: "B221", model: "251C", deviceJoinName: "Inovelli 2-Channel Smart Plug"
fingerprint manufacturer: "0312", prod: "0221", model: "611C", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
fingerprint manufacturer: "015D", prod: "0221", model: "611C", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
fingerprint manufacturer: "015D", prod: "6100", model: "6100", deviceJoinName: "Inovelli 2-Channel Outdoor Smart Plug"
fingerprint manufacturer: "015D", prod: "2500", model: "2500", deviceJoinName: "Inovelli 2-Channel Smart Plug w/Scene"
}
simulator {}
preferences {}

View File

@@ -24,10 +24,7 @@ metadata {
capability "Temperature Measurement"
capability "Health Check"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x20, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x30, 0x9C, 0x98, 0x7A", outClusters: ""
fingerprint mfr:"010F", prod:"0801", model:"2001"
fingerprint mfr:"010F", prod:"0801", model:"1001"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x20, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x30, 0x9C, 0x98, 0x7A", outClusters: ""
}
simulator {

View File

@@ -0,0 +1,173 @@
/**
* Nest
*
* Copyright 2017 dianoga7@3dgo.net
*
* 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.
*
*/
metadata {
definition (name: "Nest", namespace: "smartthings-users", author: "dianoga7@3dgo.net") {
capability "Polling"
capability "Presence Sensor"
capability "Relative Humidity Measurement"
capability "Sensor"
capability "Temperature Measurement"
capability "Thermostat"
attribute "temperatureUnit", "string"
command "away"
command "present"
command "setPresence"
command "heatingSetpointUp"
command "heatingSetPointDown"
command "setFarenheit"
command "setCelsius"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
// TODO: define your main and details tiles here
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'presence' attribute
// TODO: handle 'humidity' attribute
// TODO: handle 'temperature' attribute
// TODO: handle 'temperature' attribute
// TODO: handle 'heatingSetpoint' attribute
// TODO: handle 'coolingSetpoint' attribute
// TODO: handle 'thermostatSetpoint' attribute
// TODO: handle 'thermostatMode' attribute
// TODO: handle 'thermostatFanMode' attribute
// TODO: handle 'thermostatOperatingState' attribute
// TODO: handle 'schedule' attribute
// TODO: handle 'coolingSetpointRange' attribute
// TODO: handle 'heatingSetpointRange' attribute
// TODO: handle 'supportedThermostatFanModes' attribute
// TODO: handle 'supportedThermostatModes' attribute
// TODO: handle 'thermostatSetpointRange' attribute
// TODO: handle 'temperatureUnit' attribute
}
// handle commands
def poll() {
log.debug "Executing 'poll'"
// TODO: handle 'poll' command
}
def setHeatingSetpoint() {
log.debug "Executing 'setHeatingSetpoint'"
// TODO: handle 'setHeatingSetpoint' command
}
def setCoolingSetpoint() {
log.debug "Executing 'setCoolingSetpoint'"
// TODO: handle 'setCoolingSetpoint' command
}
def off() {
log.debug "Executing 'off'"
// TODO: handle 'off' command
}
def heat() {
log.debug "Executing 'heat'"
// TODO: handle 'heat' command
}
def emergencyHeat() {
log.debug "Executing 'emergencyHeat'"
// TODO: handle 'emergencyHeat' command
}
def cool() {
log.debug "Executing 'cool'"
// TODO: handle 'cool' command
}
def setThermostatMode() {
log.debug "Executing 'setThermostatMode'"
// TODO: handle 'setThermostatMode' command
}
def fanOn() {
log.debug "Executing 'fanOn'"
// TODO: handle 'fanOn' command
}
def fanAuto() {
log.debug "Executing 'fanAuto'"
// TODO: handle 'fanAuto' command
}
def fanCirculate() {
log.debug "Executing 'fanCirculate'"
// TODO: handle 'fanCirculate' command
}
def setThermostatFanMode() {
log.debug "Executing 'setThermostatFanMode'"
// TODO: handle 'setThermostatFanMode' command
}
def auto() {
log.debug "Executing 'auto'"
// TODO: handle 'auto' command
}
def setSchedule() {
log.debug "Executing 'setSchedule'"
// TODO: handle 'setSchedule' command
}
def away() {
log.debug "Executing 'away'"
// TODO: handle 'away' command
}
def present() {
log.debug "Executing 'present'"
// TODO: handle 'present' command
}
def setPresence() {
log.debug "Executing 'setPresence'"
// TODO: handle 'setPresence' command
}
def heatingSetpointUp() {
log.debug "Executing 'heatingSetpointUp'"
// TODO: handle 'heatingSetpointUp' command
}
def heatingSetPointDown() {
log.debug "Executing 'heatingSetPointDown'"
// TODO: handle 'heatingSetPointDown' command
}
def setFarenheit() {
log.debug "Executing 'setFarenheit'"
// TODO: handle 'setFarenheit' command
}
def setCelsius() {
log.debug "Executing 'setCelsius'"
// TODO: handle 'setCelsius' command
}

View File

@@ -18,6 +18,7 @@ metadata {
capability "Actuator"
capability "Switch"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
@@ -166,6 +167,10 @@ def setLevel(value, duration) {
zwave.switchMultilevelV2.switchMultilevelSet(value: value, dimmingDuration: dimmingDuration).format()
}
def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
def refresh() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}

View File

@@ -13,7 +13,7 @@
*/
metadata {
definition (name: "Aeon Multisensor 6", namespace: "smartthings", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.020.00008', executeCommandsLocally: true) {
definition (name: "Aeon Multisensor 6", namespace: "smartthings", author: "SmartThings") {
capability "Motion Sensor"
capability "Temperature Measurement"
capability "Relative Humidity Measurement"

View File

@@ -31,6 +31,7 @@ metadata {
status "no motion (basic)" : "command: 2001, payload: 00"
status "motion (binary)" : "command: 3003, payload: FF"
status "no motion (binary)" : "command: 3003, payload: 00"
status "wakeup" : "command: 8407, payload: "
for (int i = 0; i <= 100; i += 20) {
status "temperature ${i}F": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
@@ -85,12 +86,20 @@ metadata {
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
main(["motion", "temperature", "humidity", "illuminance"])
details(["motion", "temperature", "humidity", "illuminance", "battery", "configure"])
details(["motion", "temperature", "humidity", "illuminance", "battery"])
}
preferences {
input description: "Please consult the operating manual for advanced setting options. You can skip this configuration to use default settings",
title: "Advanced Configuration", displayDuringSetup: true, type: "paragraph", element: "paragraph"
// these are cribbed from the newer aeon-multisensor-6
input "motionDelayTime", "enum", title: "Motion Sensor Delay Time",
options: ["20 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"], defaultValue: "${motionDelayTime}", displayDuringSetup: true
input "reportInterval", "enum", title: "Sensors Report Interval",
options: ["5 minutes", "8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${reportInterval}", displayDuringSetup: true
}
}
@@ -102,25 +111,42 @@ def installed(){
def updated(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
log.debug "Updated with settings: ${settings}"
//preferences changed, so we should reconfigure on next wakeup
setConfigured("false")
}
// Parse incoming device messages to generate events
def parse(String description)
{
def result = []
def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x84: 1])
if (cmd) {
if( cmd.CMD == "8407" ) { result << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format()) }
result << createEvent(zwaveEvent(cmd))
def results = []
log.debug("parsing")
if (description.startsWith("Err")) {
results = createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description, [0x31: 2, 0x30: 1, 0x84: 1])
if(cmd) results = zwaveEvent(cmd)
if(!results) results = [ descriptionText: cmd, displayed: false ]
}
log.debug "Parse returned ${result}"
return result
log.debug("Parsed '$description' to $results")
return results
}
// Event Generation
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
{
[descriptionText: "${device.displayName} woke up", isStateChange: false]
def results = []
def cmds = []
results << createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
// If we haven't configured yet, then do so now
if (!isConfigured()) {
cmds = configure()
}
cmds += zwave.wakeUpV1.wakeUpNoMoreInformation()
results << response(cmds)
return results
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
@@ -147,7 +173,7 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelR
map.name = "humidity"
break;
}
map
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
@@ -156,20 +182,19 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
map.unit = "%"
map.displayed = false
map
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd) {
def map = [:]
def map = [name : "motion"]
map.value = cmd.sensorValue ? "active" : "inactive"
map.name = "motion"
if (map.value == "active") {
map.descriptionText = "$device.displayName detected motion"
}
else {
map.descriptionText = "$device.displayName motion has stopped"
}
map
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
@@ -182,12 +207,12 @@ def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
else {
map.descriptionText = "$device.displayName motion has stopped"
}
map
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.debug "Catchall reached for cmd: ${cmd.toString()}}"
[:]
createEvent([:])
}
/**
@@ -198,17 +223,57 @@ def ping() {
}
def configure() {
def defaultTimeout = motionDelayTime ? timeOptionValueMap[motionDelayTime] : 20
def defaultPeriod = reportInterval ? timeOptionValueMap[reportInterval]: 5*60
setConfigured("true")
delayBetween([
// send remove association to avoid double reports
zwave.associationV1.associationRemove(groupingIdentifier: 1, nodeId: zwaveHubNodeId).format(),
// send binary sensor report instead of basic set for motion
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2).format(),
// send no-motion report 15 seconds after motion stops
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: 15).format(),
// send no-motion report after a user-specified period (default 20 seconds) after motion stops
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: defaultTimeout).format(),
// send all data (temperature, humidity, illuminance & battery) periodically
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 225).format(),
// send all data (temperature, humidity, & illuminance) periodically
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 224).format(),
// set data reporting period to 5 minutes
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300).format()
])
// set data reporting period for above to a user-specified period (default 5 minutes)
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: defaultPeriod).format(),
// send a battery report less frequently
zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1).format(),
// like once every 12 hours
zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 12*60*60).format()
]) + ["delay 5000"]
}
private def getTimeOptionValueMap() { [
"20 seconds" : 20,
"40 seconds" : 40,
"1 minute" : 60,
"2 minutes" : 2*60,
"3 minutes" : 3*60,
"4 minutes" : 4*60,
"5 minutes" : 5*60,
"8 minutes" : 8*60,
"15 minutes" : 15*60,
"30 minutes" : 30*60,
"1 hours" : 1*60*60,
"6 hours" : 6*60*60,
"12 hours" : 12*60*60,
"18 hours" : 6*60*60,
"24 hours" : 24*60*60,
]}
private setConfigured(configure) {
updateDataValue("configured", configure)
}
private isConfigured() {
getDataValue("configured") == "true"
}

View File

@@ -17,6 +17,7 @@ metadata {
capability "Actuator"
capability "Switch"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
@@ -121,6 +122,13 @@ def off() {
])
}
def poll() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.meterV2.meterGet().format()
])
}
def refresh() {
zwave.switchBinaryV1.switchBinaryGet().format()
}

View File

@@ -17,6 +17,7 @@ metadata {
capability "Actuator"
capability "Indicator"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
@@ -25,6 +26,7 @@ metadata {
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer"
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer"
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer"
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
}
simulator {
@@ -240,6 +242,10 @@ def setLevel(value, duration) {
zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay)
}
def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */

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

@@ -46,16 +46,15 @@
capability "Illuminance Measurement"
capability "Sensor"
capability "Battery"
capability "Health Check"
capability "Health Check"
command "resetParams2StDefaults"
command "listCurrentParams"
command "updateZwaveParam"
command "test"
command "configure"
command "resetParams2StDefaults"
command "listCurrentParams"
command "updateZwaveParam"
command "test"
command "configure"
fingerprint mfr:"010F", prod:"0800", model:"2001"
fingerprint mfr:"010F", prod:"0800", model:"1001"
fingerprint deviceId: "0x2001", inClusters: "0x30,0x84,0x85,0x80,0x8F,0x56,0x72,0x86,0x70,0x8E,0x31,0x9C,0xEF,0x30,0x31,0x9C"
}
simulator {

View File

@@ -171,9 +171,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
result.value = pct
} else {
def useOldBatt = shouldUseOldBatteryReporting()
def minVolts = useOldBatt ? 2.1 : 2.4
def maxVolts = useOldBatt ? 3.0 : 2.7
def minVolts = 2.4
def maxVolts = 2.7
// Get the current battery percentage as a multiplier 0 - 1
def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0
// Find the corresponding voltage from our range
@@ -184,16 +183,15 @@ private Map getBatteryResult(rawValue) {
// OR we have received the same reading twice in a row
// OR we don't currently have a battery reading
// OR the value we just received is at least 2 steps off from the last reported value
// OR the device's firmware is older than 1.15.7
if(useOldBatt || state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
if(state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
} else {
// Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes
result.value = device.currentState("battery").value
// Don't update as we want to smooth the battery values
result = null
}
state.lastVolts = volts
}
@@ -239,22 +237,3 @@ def configure() {
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
}
private shouldUseOldBatteryReporting() {
def isFwVersionLess = true // By default use the old battery reporting
def deviceFwVer = "${device.getFirmwareVersion()}"
def deviceVersion = deviceFwVer.tokenize('.') // We expect the format ###.###.### where ### is some integer
if (deviceVersion.size() == 3) {
def targetVersion = [1, 15, 7] // Centralite Firmware 1.15.7 contains battery smoothing fixes, so versions before that should NOT be smoothed
def devMajor = deviceVersion[0] as int
def devMinor = deviceVersion[1] as int
def devBuild = deviceVersion[2] as int
isFwVersionLess = ((devMajor < targetVersion[0]) ||
(devMajor == targetVersion[0] && devMinor < targetVersion[1]) ||
(devMajor == targetVersion[0] && devMinor == targetVersion[1] && devBuild < targetVersion[2]))
}
return isFwVersionLess // If f/w version is less than 1.15.7 then do NOT smooth battery reports and use the old reporting
}

View File

@@ -273,10 +273,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
result.value = pct
} else {
def useOldBatt = shouldUseOldBatteryReporting()
def minVolts = 2.1
def maxVolts = useOldBatt ? 3.0 : 2.7
def maxVolts = 2.7
// Get the current battery percentage as a multiplier 0 - 1
def curValVolts = Integer.parseInt(device.currentState("battery")?.value ?: "100") / 100.0
// Find the corresponding voltage from our range
@@ -287,16 +285,15 @@ private Map getBatteryResult(rawValue) {
// OR we have received the same reading twice in a row
// OR we don't currently have a battery reading
// OR the value we just received is at least 2 steps off from the last reported value
// OR the device's firmware is older than 1.15.7
if(useOldBatt || state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
if(state?.lastVolts == null || state?.lastVolts == volts || device.currentState("battery")?.value == null || Math.abs(curValVolts - volts) > 0.1) {
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
} else {
// Don't update as we want to smooth the battery values, but do report the last battery state for record keeping purposes
result.value = device.currentState("battery").value
// Don't update as we want to smooth the battery values
result = null
}
state.lastVolts = volts
}
@@ -416,25 +413,6 @@ private getManufacturerCode() {
}
}
private shouldUseOldBatteryReporting() {
def isFwVersionLess = true // By default use the old battery reporting
def deviceFwVer = "${device.getFirmwareVersion()}"
def deviceVersion = deviceFwVer.tokenize('.') // We expect the format ###.###.### where ### is some integer
if (deviceVersion.size() == 3) {
def targetVersion = [1, 15, 7] // Centralite Firmware 1.15.7 contains battery smoothing fixes, so versions before that should NOT be smoothed
def devMajor = deviceVersion[0] as int
def devMinor = deviceVersion[1] as int
def devBuild = deviceVersion[2] as int
isFwVersionLess = ((devMajor < targetVersion[0]) ||
(devMajor == targetVersion[0] && devMinor < targetVersion[1]) ||
(devMajor == targetVersion[0] && devMinor == targetVersion[1] && devBuild < targetVersion[2]))
}
return isFwVersionLess // If f/w version is less than 1.15.7 then do NOT smooth battery reports and use the old reporting
}
private hexToInt(value) {
new BigInteger(value, 16)
}

View File

@@ -14,7 +14,7 @@
*
*/
metadata {
definition (name: "Simulated Dimmer Switch", namespace: "smartthings/testing", author: "SmartThings", runLocally: true, minHubCoreVersion: '000.020.00008', executeCommandsLocally: true) {
definition (name: "Simulated Dimmer Switch", namespace: "smartthings/testing", author: "SmartThings") {
capability "Actuator"
capability "Sensor"

View File

@@ -13,7 +13,7 @@
*/
metadata {
definition (name: "Simulated Switch", namespace: "smartthings/testing", author: "bob", runLocally: true, minHubCoreVersion: '000.020.00008', executeCommandsLocally: true) {
definition (name: "Simulated Switch", namespace: "smartthings/testing", author: "bob") {
capability "Switch"
capability "Relay Switch"
capability "Sensor"

View File

@@ -30,13 +30,8 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY PAR38 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart PAR38 Soft White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart BR30 Soft White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E11-G13", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E11-G14", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E11-G23", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E11-G33", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E12-N13", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E12-N14", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E12-N15", deviceJoinName: "Sengled Element Classic"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E11-G13", deviceJoinName: "Sengled Element Classic A19"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "E12-N14", deviceJoinName: "Sengled Element Classic BR30"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL6HD", deviceJoinName: "Leviton Dimmer Switch"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL3HL", deviceJoinName: "Leviton Lumina RF Plug-In Dimmer"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", outClusters: "0003, 0006, 0008, 0019, 0406", manufacturer: "Leviton", model: "DL1KD", deviceJoinName: "Leviton Lumina RF Dimmer Switch"

View File

@@ -32,7 +32,7 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock"
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 Assure 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,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_5", deviceJoinName: "Kwikset 5-Button Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_LEVER_5", deviceJoinName: "Kwikset 5-Button Lever"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_10", deviceJoinName: "Kwikset 10-Button Deadbolt"

View File

@@ -38,10 +38,7 @@ metadata {
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic A60 TW", deviceJoinName: "OSRAM LIGHTIFY LED Classic A60 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 Tunable White", deviceJoinName: "SYLVANIA Smart A19 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Classic B40 TW - LIGHTIFY", deviceJoinName: "OSRAM LIGHTIFY Classic B40 Tunable White"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A19NAE26", deviceJoinName: "Sengled Element Plus"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A191AE26W", deviceJoinName: "Sengled Element Plus"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A60EAB22", deviceJoinName: "Sengled Element Plus"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A60EAE27", deviceJoinName: "Sengled Element Plus"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0702, 0B05", outClusters: "0019", manufacturer: "sengled", model: "Z01-A19NAE26", deviceJoinName: "Sengled Element plus"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, 0B05, FC01, FC08", outClusters: "0003, 0019", manufacturer: "LEDVANCE", model: "A19 TW 10 year", deviceJoinName: "SYLVANIA Smart 10Y A19 TW"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Conv Under Cabinet TW", deviceJoinName: "SYLVANIA Smart Convertible Under Cabinet"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0300, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "ColorstripRGBW", deviceJoinName: "SYLVANIA Smart Convertible Under Cabinet"

View File

@@ -31,13 +31,6 @@ metadata {
fingerprint mfr:"001D", prod:"0401", model:"0334", deviceJoinName: "Leviton 600W Incandescent Dimmer"
fingerprint mfr:"0111", prod:"8200", model:"0200", deviceJoinName: "Remotec Technology Plug-In Dimmer"
fingerprint mfr:"1104", prod:"001D", model:"0501", deviceJoinName: "Leviton 1000W Incandescant Dimmer"
fingerprint mfr:"0039", prod:"5044", model:"3033", deviceJoinName: "Honeywell Z-Wave Plug-in Dimmer (Dual Outlet)"
fingerprint mfr:"0039", prod:"5044", model:"3038", deviceJoinName: "Honeywell Z-Wave Plug-in Dimmer"
fingerprint mfr:"0039", prod:"4944", model:"3038", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Dimmer"
fingerprint mfr:"0039", prod:"4944", model:"3130", deviceJoinName: "Honeywell Z-Wave In-Wall Smart Toggle Dimmer"
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
fingerprint mfr:"0063", prod:"4944", model:"3131", deviceJoinName: "GE In-Wall Smart Fan Control"
fingerprint mfr:"0039", prod:"4944", model:"3131", deviceJoinName: "Honeywell Z-Wave Plus In-Wall Fan Speed Control"
}
simulator {
@@ -88,7 +81,6 @@ 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, offlinePingable: "1"])
response(refresh())
}
def updated(){

View File

@@ -48,7 +48,7 @@ metadata {
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" //YRD416, YRD426, YRD446
fingerprint mfr:"0129", prod:"8002", model:"0600", deviceJoinName: "Yale Assure Lock with Bluetooth" //YRD416, YRD426, YRD446
fingerprint mfr:"0129", prod:"0007", model:"0001", deviceJoinName: "Yale Keyless Connected Smart Door Lock"
fingerprint mfr:"0129", prod:"8004", model:"0600", deviceJoinName: "Yale Assure Lock Push Button Deadbolt" //YRD216
// Samsung
@@ -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)
}
/**
@@ -398,7 +387,7 @@ private def handleAccessAlarmReport(cmd) {
break
case 5: // Locked with keypad
if (cmd.eventParameter || cmd.alarmLevel) {
codeID = readCodeSlotId(cmd)
codeID = cmd.eventParameter[2] ?: cmd.alarmLevel
codeName = getCodeName(lockCodes, codeID)
map.descriptionText = "Locked by \"$codeName\""
map.data = [ usedCode: codeID, codeName: codeName, method: "keypad" ]
@@ -409,7 +398,7 @@ private def handleAccessAlarmReport(cmd) {
break
case 6: // Unlocked with keypad
if (cmd.eventParameter || cmd.alarmLevel) {
codeID = readCodeSlotId(cmd)
codeID = cmd.eventParameter[2] ?: cmd.alarmLevel
codeName = getCodeName(lockCodes, codeID)
map.descriptionText = "Unlocked by \"$codeName\""
map.data = [ usedCode: codeID, codeName: codeName, method: "keypad" ]
@@ -442,7 +431,7 @@ private def handleAccessAlarmReport(cmd) {
break
case 0xD: // User code deleted
if (cmd.eventParameter || cmd.alarmLevel) {
codeID = readCodeSlotId(cmd)
codeID = cmd.eventParameter[2] ?: cmd.alarmLevel
if (lockCodes[codeID.toString()]) {
codeName = getCodeName(lockCodes, codeID)
map = [ name: "codeChanged", value: "$codeID deleted", isStateChange: true ]
@@ -454,7 +443,7 @@ private def handleAccessAlarmReport(cmd) {
break
case 0xE: // Master or user code changed/set
if (cmd.eventParameter || cmd.alarmLevel) {
codeID = readCodeSlotId(cmd)
codeID = cmd.eventParameter[2] ?: cmd.alarmLevel
if(codeID == 0 && isKwiksetLock()) {
//Ignoring this AlarmReport as Kwikset reports codeID 0 when all slots are full and user tries to set another lock code manually
//Kwikset locks don't send AlarmReport when Master code is set
@@ -475,7 +464,7 @@ private def handleAccessAlarmReport(cmd) {
break
case 0xF: // Duplicate Pin-code error
if (cmd.eventParameter || cmd.alarmLevel) {
codeID = readCodeSlotId(cmd)
codeID = cmd.eventParameter[2] ?: cmd.alarmLevel
clearStateForSlot(codeID)
map = [ name: "codeChanged", value: "$codeID failed", descriptionText: "User code is duplicate and not added",
isStateChange: true, data: [isCodeDuplicate: true] ]
@@ -603,14 +592,14 @@ private def handleAlarmReportUsingAlarmType(cmd) {
case 19: // Unlocked with keypad
map = [ name: "lock", value: "unlocked" ]
if (cmd.alarmLevel != null) {
codeID = readCodeSlotId(cmd)
codeID = cmd.alarmLevel
codeName = getCodeName(lockCodes, codeID)
map.descriptionText = "Unlocked by \"$codeName\""
map.data = [ usedCode: codeID, codeName: codeName, method: "keypad" ]
}
break
case 18: // Locked with keypad
codeID = readCodeSlotId(cmd)
codeID = cmd.alarmLevel
map = [ name: "lock", value: "locked" ]
// Kwikset lock reporting code id as 0 when locked using the lock keypad button
if (isKwiksetLock() && codeID == 0) {
@@ -657,7 +646,7 @@ private def handleAlarmReportUsingAlarmType(cmd) {
result << createEvent(name: "lockCodes", value: util.toJson([:]), displayed: false, descriptionText: "'lockCodes' attribute updated")
break
case 33: // User code deleted
codeID = readCodeSlotId(cmd)
codeID = cmd.alarmLevel
if (lockCodes[codeID.toString()]) {
codeName = getCodeName(lockCodes, codeID)
map = [ name: "codeChanged", value: "$codeID deleted", isStateChange: true ]
@@ -671,7 +660,7 @@ private def handleAlarmReportUsingAlarmType(cmd) {
break
case 13:
case 112: // Master or user code changed/set
codeID = readCodeSlotId(cmd)
codeID = cmd.alarmLevel
if(codeID == 0 && isKwiksetLock()) {
//Ignoring this AlarmReport as Kwikset reports codeID 0 when all slots are full and user tries to set another lock code manually
//Kwikset locks don't send AlarmReport when Master code is set
@@ -692,7 +681,7 @@ private def handleAlarmReportUsingAlarmType(cmd) {
break
case 34:
case 113: // Duplicate Pin-code error
codeID = readCodeSlotId(cmd)
codeID = cmd.alarmLevel
clearStateForSlot(codeID)
map = [ name: "codeChanged", value: "$codeID failed", descriptionText: "User code is duplicate and not added",
isStateChange: true, data: [isCodeDuplicate: true] ]
@@ -1668,30 +1657,3 @@ def isYaleLock() {
}
return false
}
/**
* 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
*/
def readCodeSlotId(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
if(cmd.numberOfEventParameters == 1) {
return cmd.eventParameter[0]
} else if(cmd.numberOfEventParameters >= 3) {
return cmd.eventParameter[2]
}
return cmd.alarmLevel
}

View File

@@ -17,6 +17,7 @@ metadata {
capability "Actuator"
capability "Switch"
capability "Power Meter"
capability "Polling"
capability "Refresh"
capability "Configuration"
capability "Sensor"
@@ -200,6 +201,14 @@ def off() {
]
}
def poll() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */

View File

@@ -16,7 +16,7 @@
*/
metadata {
definition (name: "Z-Wave Plus Door/Window Sensor", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.d.sensor.contact", runLocally: true, minHubCoreVersion: '000.020.00008', executeCommandsLocally: true) {
definition (name: "Z-Wave Plus Door/Window Sensor", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.d.sensor.contact") {
capability "Contact Sensor"
capability "Configuration"
capability "Battery"

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

@@ -16,6 +16,7 @@ metadata {
capability "Actuator"
capability "Indicator"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Health Check"
@@ -176,6 +177,13 @@ def off() {
])
}
def poll() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
])
}
/**
* PING is used by Device-Watch in attempt to reach the Device
**/

View File

@@ -1,121 +0,0 @@
/**
* Copyright 2017 SmartThings
*
* 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.
*
* Thermostats
*
* Author: Juan Pablo Risso
* Date: 2017-12-05
*
*/
definition(
name: "Thermostats",
namespace: "smartthings",
author: "SmartThings",
description: "Receive notifications when anything happens in your home.",
category: "SmartSolutions",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/Cat-SafetyAndSecurity.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/SafetyAndSecurity/Cat-SafetyAndSecurity@2x.png",
singleInstance: true
)
preferences {
section("Choose one or more, when..."){
input "smokeDevices", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
input "carbonMonoxideDevices", "capability.carbonMonoxideDetector", title: "Carbon Monoxide Detected", required: false, multiple: true
}
section("Turn off these thermostats"){
input "thermostatDevices", "capability.thermostat", title: "Thermostats", required: true, multiple: true
}
section("Send this message (optional, sends standard status message if not specified)"){
input "messageText", "text", title: "Message Text", required: false
}
section("Via a push notification and/or an SMS message"){
input("recipients", "contact", title: "Send notifications to") {
input "phone", "phone", title: "Enter a phone number to get SMS", required: false
paragraph "If outside the US please make sure to enter the proper country code"
input "pushAndPhone", "enum", title: "Notify me via Push Notification", required: false, options: ["Yes", "No"]
}
}
section("Minimum time between messages (optional, defaults to every message)") {
input "frequency", "decimal", title: "Minutes", required: false
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
subscribeToEvents()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
subscribeToEvents()
}
def subscribeToEvents() {
subscribe(smokeDevices, "smoke.detected", eventHandler)
subscribe(smokeDevices, "smoke.tested", eventHandler)
subscribe(smokeDevices, "carbonMonoxide.detected", eventHandler)
subscribe(carbonMonoxideDevices, "carbonMonoxide.detected", eventHandler)
}
def eventHandler(evt) {
log.debug "Notify got evt ${evt}"
// Turn off thermostat
thermostatDevices*.setThermostatMode("off")
if (frequency) {
def lastTime = state[evt.deviceId]
if (lastTime == null || now() - lastTime >= frequency * 60000) {
sendMessage(evt)
}
}
else {
sendMessage(evt)
}
}
private sendMessage(evt) {
String msg = messageText
Map options = [:]
if (!messageText) {
msg = '{{ triggerEvent.descriptionText }}'
options = [translatable: true, triggerEvent: evt]
}
log.debug "$evt.name:$evt.value, pushAndPhone:$pushAndPhone, '$msg'"
if (location.contactBookEnabled) {
sendNotificationToContacts(msg, recipients, options)
} else {
if (phone) {
options.phone = phone
if (pushAndPhone != 'No') {
log.debug 'Sending push and SMS'
options.method = 'both'
} else {
log.debug 'Sending SMS'
options.method = 'phone'
}
} else if (pushAndPhone != 'No') {
log.debug 'Sending push'
options.method = 'push'
} else {
log.debug 'Sending nothing'
options.method = 'none'
}
sendNotification(msg, options)
}
if (frequency) {
state[evt.deviceId] = now()
}
}