Compare commits

..

5 Commits

Author SHA1 Message Date
dsainteclaire
27d9bdd86c Merge pull request #2231 from marstorp/dvcsmp2973
DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException
2017-08-10 16:52:35 -07:00
Ingvar Marstorp
2b09b7c574 Update zwave-thermostat.groovy 2017-08-10 12:23:50 -07:00
Ingvar Marstorp
eb9b55e01c Update ct100-thermostat.groovy 2017-08-10 12:19:12 -07:00
marstorp
10fef276db DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException
CT100/Honeywell Z-wave NullPointerException when changing mode
Changed name of private method setThermostatMode(data) to setGetThermostatMode(data)
and method setThermostatFanMode(data) to setGetThermostatFanMode(data) so that platform
calls the exposed methods setThermostatMode(String value) setThermostatFanMode(String value)
as these validates the argument against the supported modes and then calls the private methods.
Also removed some of the leftshift operators that showed errors in Sumo logs.
2017-08-10 11:42:05 -07:00
Vinay Rao
2ea99a1b9e Merge pull request #2226 from SmartThingsCommunity/master
Rolling up master to staging
2017-08-08 13:48:18 -07:00
3 changed files with 49 additions and 513 deletions

View File

@@ -1,466 +0,0 @@
/**
* Copyright 2015 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.
*
*/
metadata {
definition (name: "Hue Motion Sensor", namespace: "digitalgecko", author: "digitalgecko") {
capability "Motion Sensor"
capability "Configuration"
capability "Battery"
capability "Refresh"
capability "Temperature Measurement"
capability "Sensor"
capability "Illuminance Measurement" //0x0400
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0406,0400,0402", outClusters: "0019", manufacturer: "Philips", model: "SML001", deviceJoinName: "Hue Motion Sensor"
}
preferences {
section {
input title: "Temperature Offset", description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}
section {
input title: "Luminance Offset", description: "This feature allows you to correct the luminance reading by selecting an offset. Enter a value such as 20 or -20 to adjust the luminance reading.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "luxOffset", "number", title: "Lux", description: "Adjust luminance by this amount", range: "*..*", displayDuringSetup: false
}
}
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:"#53a7c0"
attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
}
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
valueTile("illuminance", "device.illuminance", width: 2, height: 2) {
state("illuminance", label:'${currentValue}', unit:"lux",
backgroundColors:[
[value: 9, color: "#767676"],
[value: 315, color: "#ffa81e"],
[value: 1000, color: "#fbd41b"]
]
)
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"configure", action:"configure"
}
main "motion"
details(["motion","temperature","battery", "refresh","illuminance",'configure'])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
def msg = zigbee.parse(description)
//log.warn "--"
//log.trace description
//log.debug msg
//def x = zigbee.parseDescriptionAsMap( description )
//log.error x
Map map = [:]
if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description)
}
else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description)
}
else if (description?.startsWith('illuminance: ')) {
map = parseCustomMessage(description)
}
// else if (description?.startsWith('zone status')) {
// //map = parseIasMessage(description)
// log.trace "zone status"
// }
def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) {
List cmds = enrollResponse()
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
}
else if (description?.startsWith('read attr -')) {
result = parseReportAttributeMessage(description).each { createEvent(it) }
}
return result
}
/*
Refresh Function
*/
def refresh() {
log.debug "Refreshing Values"
def refreshCmds = []
refreshCmds +=zigbee.readAttribute(0x0001, 0x0020) // Read battery?
refreshCmds += zigbee.readAttribute(0x0402, 0x0000) // Read temp?
refreshCmds += zigbee.readAttribute(0x0400, 0x0000) // Read luminance?
refreshCmds += zigbee.readAttribute(0x0406, 0x0000) // Read motion?
return refreshCmds + enrollResponse()
}
/*
Configure Function
*/
def configure() {
// TODO : device watch?
String zigbeeId = swapEndianHex(device.hub.zigbeeId)
log.debug "Confuguring Reporting and Bindings."
def configCmds = []
configCmds += zigbee.batteryConfig()
configCmds += zigbee.temperatureConfig(60, 600) // Set temp reporting times // Confirmed
configCmds += zigbee.configureReporting(0x406,0x0000, 0x18, 30, 600, null) // motion // confirmed
// Data type is not 0x20 = 0x8D invalid data type Unsigned 8-bit integer
configCmds += zigbee.configureReporting(0x400,0x0000, 0x21, 60, 600, 0x20) // Set luminance reporting times?? maybe
return refresh() + configCmds
}
/*
getMotionResult
*/
private Map getMotionResult(value) {
//log.trace "Motion : " + value
def descriptionText = value == "01" ? '{{ device.displayName }} detected motion':
'{{ device.displayName }} stopped detecting motion'
return [
name: 'motion',
value: value == "01" ? "active" : "inactive",
descriptionText: descriptionText,
translatable: true,
]
}
/*
getTemperatureResult
*/
private Map getTemperatureResult(value) {
//log.trace "Temperature : " + value
if (tempOffset) {
def offset = tempOffset as int
def v = value as int
value = v + offset
}
def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C':
'{{ device.displayName }} was {{ value }}°F'
return [
name: 'temperature',
value: value,
descriptionText: descriptionText,
translatable: true,
unit: temperatureScale
]
}
def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == "C"){
return Math.round(celsius)
} else {
return Math.round(celsiusToFahrenheit(celsius))
}
}
private Map getLuminanceResult(rawValue) {
log.debug "Luminance rawValue = ${rawValue}"
if (luxOffset) {
def offset = luxOffset as int
def v = rawValue as int
rawValue = v + offset
}
def result = [
name: 'illuminance',
value: '--',
translatable: true,
unit: 'lux'
]
result.value = rawValue as Integer
return result
}
/*
getBatteryResult
*/
//TODO: needs calibration
private Map getBatteryResult(rawValue) {
//log.debug "Battery rawValue = ${rawValue}"
def result = [
name: 'battery',
value: '--',
translatable: true
]
def volts = rawValue / 10
if (rawValue == 0 || rawValue == 255) {}
else {
if (volts > 3.5) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
}
else {
if (device.getDataValue("manufacturer") == "SmartThings") {
volts = rawValue // For the batteryMap to work the key needs to be an int
def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70,
22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
def minVolts = 15
def maxVolts = 28
if (volts < minVolts)
volts = minVolts
else if (volts > maxVolts)
volts = maxVolts
def pct = batteryMap[volts]
if (pct != null) {
result.value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
else {
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
}
return result
}
/*
parseCustomMessage
*/
private Map parseCustomMessage(String description) {
Map resultMap = [:]
if (description?.startsWith('temperature: ')) {
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
resultMap = getTemperatureResult(value)
}
if (description?.startsWith('illuminance: ')) {
log.warn "value: " + description.split(": ")[1]
log.warn "proc: " + value
def value = zigbee.lux( description.split(": ")[1] as Integer ) //zigbee.parseHAIlluminanceValue(description, "illuminance: ", getTemperatureScale())
resultMap = getLuminanceResult(value)
}
return resultMap
}
/*
parseReportAttributeMessage
*/
private List parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
List result = []
// Temperature
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value)
result << getTemperatureResult(value)
}
// Motion
else if (descMap.cluster == "0406" && descMap.attrId == "0000") {
result << getMotionResult(descMap.value)
}
// Battery
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
result << getBatteryResult(Integer.parseInt(descMap.value, 16))
}
// Luminance
else if (descMap.cluster == "0402" ) { //&& descMap.attrId == "0020") {
log.error "Luminance Response " + description
//result << getBatteryResult(Integer.parseInt(descMap.value, 16))
}
return result
}
/*
parseCatchAllMessage
*/
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
// log.debug cluster
if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) {
case 0x0001:
// 0x07 - configure reporting
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break
case 0x0400:
if (cluster.command == 0x07) { // Ignore Configure Reporting Response
if(cluster.data[0] == 0x00) {
log.trace "Luminance Reporting Configured"
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "Luminance REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
log.debug "catchall : luminance" + cluster
resultMap = getLuminanceResult(cluster.data.last());
}
break
case 0x0402:
if (cluster.command == 0x07) {
if(cluster.data[0] == 0x00) {
log.trace "Temperature Reporting Configured"
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
// temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp)
resultMap = getTemperatureResult(value)
}
break
}
}
return resultMap
}
private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through
boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage
}
// This seems to be IAS Specific and not needed we are not really a motion sensor
def enrollResponse() {
// log.debug "Sending enroll response"
// String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
// [
// //Resending the CIE in case the enroll request is sent before CIE is written
// "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
// "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
// //Enroll Response
// "raw 0x500 {01 23 00 00 00}", "delay 200",
// "send 0x${device.deviceNetworkId} 1 1", "delay 200"
// ]
}
def configureHealthCheck() {
Integer hcIntervalMinutes = 12
refresh()
sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
def updated() {
log.debug "in updated()"
configureHealthCheck()
}
def ping() {
return zigbee.onOffRefresh()
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private byte[] reverseArray(byte[] array) {
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array
}

View File

@@ -18,6 +18,7 @@ metadata {
command "raiseHeatingSetpoint"
command "lowerCoolSetpoint"
command "raiseCoolSetpoint"
command "poll"
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
@@ -101,9 +102,8 @@ metadata {
def installed() {
// Configure device
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()),
new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())]
sendHubCommand(cmds)
runIn(3, "initialize", [overwrite: true]) // Allow configure command to be sent and acknowledged before proceeding
}
@@ -123,7 +123,7 @@ def initialize() {
// 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])
// Poll device for additional data that will be updated by refresh tile
poll()
pollDevice()
}
def parse(String description)
@@ -150,7 +150,6 @@ def parse(String description)
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
}
@@ -341,7 +340,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSuppo
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
state.supportedModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
@@ -351,7 +350,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
if(cmd.circulation) { supportedFanModes << "circulate" }
state.supportedFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
@@ -377,7 +376,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
if (cmd.manufacturerName) {
updateDataValue("manufacturer", cmd.manufacturerName)
}
@@ -389,6 +387,11 @@ log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
}
}
def poll() {
// Call refresh which will cap the polling to once every 2 minutes
refresh()
}
def refresh() {
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
def timeNow = now()
@@ -397,11 +400,11 @@ def refresh() {
// refresh will request battery, prevent multiple request by setting lastbatt now
state.lastbatt = timeNow
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
runIn(2, "poll", [overwrite: true])
runIn(2, "pollDevice", [overwrite: true])
}
}
def poll() {
def pollDevice() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
@@ -614,7 +617,7 @@ def updateThermostatSetpoint(setpoint, value) {
* */
def ping() {
log.debug "ping() called"
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
// Just get Operating State as it is not reported when it changes and there's no need to flood more commands
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()))
}
@@ -624,7 +627,7 @@ def switchMode() {
if (supportedModes) {
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedModes not defined"
getSupportedModes()
@@ -635,7 +638,7 @@ def switchToMode(nextMode) {
def supportedModes = state.supportedModes
if (supportedModes) {
if (supportedModes.contains(nextMode)) {
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
}
@@ -657,7 +660,7 @@ def switchFanMode() {
if (supportedFanModes) {
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedFanModes not defined"
getSupportedFanModes()
@@ -668,7 +671,7 @@ def switchToFanMode(nextMode) {
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
if (supportedFanModes.contains(nextMode)) {
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
}
@@ -679,8 +682,7 @@ def switchToFanMode(nextMode) {
}
def getSupportedFanModes() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())]
sendHubCommand(cmds)
}
@@ -696,10 +698,9 @@ def setThermostatMode(String value) {
switchToMode(value)
}
def setThermostatMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
def setGetThermostatMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())]
sendHubCommand(cmds)
}
@@ -713,10 +714,9 @@ def setThermostatFanMode(String value) {
switchToFanMode(value)
}
def setThermostatFanMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
def setGetThermostatFanMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())]
sendHubCommand(cmds)
}

View File

@@ -28,6 +28,7 @@ metadata {
command "raiseHeatingSetpoint"
command "lowerCoolSetpoint"
command "raiseCoolSetpoint"
command "poll"
fingerprint deviceId: "0x08"
fingerprint inClusters: "0x43,0x40,0x44,0x31"
@@ -91,7 +92,7 @@ metadata {
standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right"
}
valueTile("thermostatOperatingState", "device.thermostatOperatingState", width: 2, height:1, decoration: "flat") {
standardTile("thermostatOperatingState", "device.thermostatOperatingState", width: 2, height:1, decoration: "flat") {
state "thermostatOperatingState", label:'${currentValue}', backgroundColor:"#ffffff"
}
standardTile("refresh", "device.thermostatMode", width:2, height:1, inactiveLabel: false, decoration: "flat") {
@@ -105,9 +106,8 @@ metadata {
def installed() {
// Configure device
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()),
new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())]
sendHubCommand(cmds)
runIn(3, "initialize", [overwrite: true]) // Allow configure command to be sent and acknowledged before proceeding
}
@@ -129,7 +129,7 @@ def initialize() {
if (getDataValue("manufacturer") != "Honeywell") {
runEvery5Minutes("poll") // This is not necessary for Honeywell Z-wave, but could be for other Z-wave thermostats
}
poll()
pollDevice()
}
def parse(String description)
@@ -319,17 +319,22 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
// Command Implementations
def poll() {
// Call refresh which will cap the polling to once every 2 minutes
refresh()
}
def refresh() {
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
def timeNow = now()
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
state.refreshTriggeredAt = timeNow
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
runIn(2, "poll", [overwrite: true])
runIn(2, "pollDevice", [overwrite: true])
}
}
def poll() {
def pollDevice() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
@@ -505,7 +510,7 @@ def switchMode() {
if (supportedModes) {
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedModes not defined"
getSupportedModes()
@@ -516,7 +521,7 @@ def switchToMode(nextMode) {
def supportedModes = state.supportedModes
if (supportedModes) {
if (supportedModes.contains(nextMode)) {
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
}
@@ -538,7 +543,7 @@ def switchFanMode() {
if (supportedFanModes) {
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedFanModes not defined"
getSupportedFanModes()
@@ -549,7 +554,7 @@ def switchToFanMode(nextMode) {
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
if (supportedFanModes.contains(nextMode)) {
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
}
@@ -560,8 +565,7 @@ def switchToFanMode(nextMode) {
}
def getSupportedFanModes() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())]
sendHubCommand(cmds)
}
@@ -577,10 +581,9 @@ def setThermostatMode(String value) {
switchToMode(value)
}
def setThermostatMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
def setGetThermostatMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())]
sendHubCommand(cmds)
}
@@ -594,10 +597,9 @@ def setThermostatFanMode(String value) {
switchToFanMode(value)
}
def setThermostatFanMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
def setGetThermostatFanMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())]
sendHubCommand(cmds)
}
@@ -663,7 +665,7 @@ def getTempInDeviceScale(temp, scale) {
if (temp && scale) {
def deviceScale = (state.scale == 1) ? "F" : "C"
return (deviceScale == scale) ? temp :
(deviceScale == "F" ? celsiusToFahrenheit(temp) : roundC(fahrenheitToCelsius(temp)))
(deviceScale == "F" ? celsiusToFahrenheit(temp).toDouble().round(0).toInteger() : roundC(fahrenheitToCelsius(temp)))
}
return 0
}