Hi, has anyone successfully ported over the ST Driver for this device?
Thanks
Tim
Hi, has anyone successfully ported over the ST Driver for this device?
Thanks
Tim
Hello,
I had a go at porting this driver over and it sort of works.
The learn IR commands work and the On / Off functions work but little else.
However I have had this working with Rule Machine to set a portable air conditioner to "Cool" (ON) when the room is above a certain temperature, and switch "Off" (OFF) when the temperature falls below a defined point.
Most of the buttons show on the Hubitat don't do anything yet.
The original code came from @gouldner here.
I ported Ron’s ST device handler but for my purposes, I actually only need the IR learning and on/off functionality to work. So I’m not sure if the version I ported would work with the rest of the device’s features. Happy to post it here in case you want to compare to your ported driver (though I’m not a developer at all, so I’m guessing it won’t really be any different than yours).
Hi Mark,
That would be really helpful. Thank you
Sorry for the delay, here ya go.
/**
* ZXT-120 IR Sender Unit from Remotec
* tested on V1.6H version of the device
*
* Author: Ronald Gouldner (based on b.dahlem@gmail.com version)
* Date: 2015-01-20
* Code: https://github.com/gouldner/ST-Devices/src/ZXT-120
*
* Copyright (C) 2013 Ronald Gouldner
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions: The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//***** Metadata */
//
// A description of the ZXT-120 IR Extender for HVAC and its options and commands for the SmartThings hub.
// Preferences pane
//
// options that the user can directly edit in the SmartThings app
preferences {
input description: "Press Configure after making any changes", displayDuringSetup: true,
type: "paragraph", element: "paragraph"
input("remoteCode", "number", title: "Remote Code", description: "The number of the remote to emulate")
input("tempOffset", "enum", title: "Temp correction offset?", options: ["-5","-4","-3","-2","-1","0","1","2","3","4","5"])
input("shortName", "string", title: "Short Name for Home Page Temp Icon", description: "Short Name:")
input("onCommand", "enum", title: "Command to send when 'On' Button is Pressed?", options: ["on(resume)","cool","heat","dry"])
}
metadata {
definition (name: "ZXT-120 IR Sender Improved", namespace: "gouldner", author: "Ronald Gouldner") {
// Device capabilities of the ZXT-120
capability "Actuator"
capability "Temperature Measurement"
capability "Thermostat"
capability "Configuration"
// Polling has never worked on Smart Things.
capability "Polling"
// Try Health Check to aquire temp from device
capability "Health Check"
capability "Sensor"
capability "Battery"
capability "Switch"
// Commands that this device-type exposes for controlling the ZXT-120 directly
command "switchModeOff"
command "switchModeHeat"
command "switchModeCool"
command "switchModeDry"
command "switchModeAuto"
command "switchFanLow"
command "switchFanMed"
command "switchFanHigh"
command "switchFanAuto"
command "switchFanMode"
command "switchFanOscillate"
command "setRemoteCode"
command "swingModeOn"
command "swingModeOff"
//commands for thermostat interface
command "cool"
command "heat"
command "dry"
command "off"
command "setLearningPosition"
command "issueLearningCommand"
// how do these work....do they take arguments ?
//command "setCoolingSetpoint"
//command "setHeatingSetpoint"
//command "setThermostatMode"
//command "adjustTemperature", ["NUMBER"]
attribute "swingMode", "STRING"
attribute "lastPoll", "STRING"
attribute "currentConfigCode", "STRING"
attribute "currentTempOffset", "STRING"
attribute "temperatureName", "STRING"
attribute "reportedCoolingSetpoint", "STRING"
attribute "reportedHeatingSetpoint", "STRING"
attribute "learningPosition", "NUMBER"
attribute "learningPositionTemp", "STRING"
// Z-Wave description of the ZXT-120 device
fingerprint deviceId: "0x0806"
fingerprint inClusters: "0x20,0x27,0x31,0x40,0x43,0x44,0x70,0x72,0x80,0x86"
}
}
def installed() {
log.debug "ZXT-120 installed()"
configure()
}
//***** Enumerations */
// modes - Possible heating/cooling modes for the device
def modes() {
["off", "auto", "heat", "emergencyHeat", "cool", "dry", "autoChangeover"]
}
// setpointModeMap - Link the possible modes the device can be in to the possible temperature setpoints.
def getSetpointModeMap() { [
"heat": "heatingSetpoint"
,"cool": "coolingSetpoint"
,"dry": "dryingSetpoint"
//,"autoChangeover": "autoChangeoverSetpoint"
]}
// setpointMap - Link the setpoint descriptions with ZWave id numbers
def getSetpointMap() { [
"heatingSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1,
"coolingSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1,
"dryingSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_DRY_AIR,
//"reportedAutoChangeoverSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_AUTO_CHANGEOVER
]}
// setpointReportingMap - Link the setpointReportingMap tiles with ZWave id numbers
def getSetpointReportingMap() { [
"reportedHeatingSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1,
"reportedCoolingSetpoint": hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1,
]}
// modeMap - Link the heating/cooling modes with their ZWave id numbers
def getModeMap() { [
"off": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_OFF,
"resume": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_RESUME,
"heat": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_HEAT,
"cool": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_COOL,
"auto": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_AUTO,
"emergencyHeat": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_AUXILIARY_HEAT,
"dry": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_DRY_AIR,
"autoChangeover": hubitat.zwave.commands.thermostatmodev1.ThermostatModeSet.MODE_AUTO_CHANGEOVER
]}
def fanModes() {
["fanAuto", "fanLow", "fanMedium", "fanHigh"]
}
// fanModeMap - Link the possible fan speeds with their ZWave id numbers
def getFanModeMap() { [
"fanAuto": hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_LOW,
"fanLow": hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_LOW,
"fanMedium": hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_MEDIUM,
"fanHigh": hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_HIGH
]}
// Command parameters
def getCommandParameters() { [
"remoteCode": 27,
"tempOffsetParam": 37,
"oscillateSetting": 33,
"learningMode": 25
]}
//***** Commands */
// parse - Handle events coming from the user and the device
def parse(String description)
{
// If the device sent an update, interpret it
log.info "Parsing Description=$description"
// 0X20=Basic - V1 supported
// 0x27=Switch All - V1 supported
// 0X31=Sensor Multilevel - V1 supported
// 0X40=Thermostat Mode - V2 supported
// -- 0x42=Thermostat Operating State (NOT SUPPORTED, was in original device handler)
// 0x43=Thermostat Setpoint - V2 supported
// 0x44=Thermostat Fan Mode - V2 supported
// 0x70=Configuration - V1 supported
// 0x72=Manufacturer Specific - V1 supported
// 0x80=Battery - V1 supported
// 0x86=Version - V1 supported
def cmd = zwave.parse(description, [0X20:1, 0X27:1, 0x31:1, 0x40:2, 0x43:2, 0x44:2, 0x70:1, 0x72:1, 0x80:1, 0x86:1])
def map = []
def result = null
if (cmd) {
log.debug "Parsed ${cmd} to ${result.inspect()}"
result = zwaveEvent(cmd)
map = createEvent(result)
} else {
log.debug "Non-parsed event. Perhaps wrong version is being handled?: ${description}"
return null
}
if (map) {
log.debug "Parsed ${description} to command ${cmd} to result ${result.inspect()} map=${map}"
// If the update was a change in the device's fan speed
if (map.name == "thermostatFanMode" && map.isStateChange) {
// store the new fan speed
updateState("lastTriedFanMode", map.value)
}
return [map]
} else {
return null
}
}
//***** Event Handlers */
//Handle events coming from the device
// Battery Level event
def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
log.debug "BatteryReport cmd:$cmd"
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
map.unit = "%"
map.displayed = false
log.debug "Battery Level Reported=$map.value"
map
}
// - Sensor Multilevel Report
// The device is reporting temperature readings
def zwaveEvent(hubitat.zwave.commands.sensormultilevelv1.SensorMultilevelReport cmd)
{
log.debug "SensorMultilevelReport reporting...cmd=$cmd"
// Determine the temperature the device is reporting
def map = [:]
switch (cmd.sensorType) {
case 1:
// temperature
def cmdScale = cmd.scale == 1 ? "F" : "C"
log.debug "cmd.scale=$cmd.scale"
log.debug "cmd.scaledSensorValue=$cmd.scaledSensorValue"
// converTemp returns string with two decimal places
// convert to double then to int to drop the decimal
Integer temp = (int) convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale).toDouble()
map.value = temp
map.unit = getTemperatureScale()
map.name = "temperature"
// Send event to set ShortName + Temp tile
def shortNameVal = shortName == null ? "ZXT-120" : shortName
def tempName = shortNameVal + " " + map.value + "°"
log.debug "Sensor Reporting temperatureName $tempName map.value=$map.value, cmdScale=$cmdScale"
sendEvent("name":"temperatureName", "value":tempName)
// Pass value converted to Fahrenheit and Unit of 1 which means Fahrenheit
sendEvent("name":"temperature", "value":map.value, "isStateChange":true, unit:1, displayed:true)
//sendEvent("name":"temperature", "value":map.value, "isStateChange":true, displayed:true)
break;
default:
log.warn "Unknown sensorType reading from device"
break;
}
}
// - Thermostat Mode Report
// The device is reporting its heating/cooling Mode
def zwaveEvent(hubitat.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
def map = [:]
// Determine the mode the device is reporting, based on its ZWave id
map.value = modeMap.find {it.value == cmd.mode}?.key
map.name = "thermostatMode"
log.debug "Thermostat Mode reported : $map.value"
// Return the interpreted report
map
}
// - Thermostat Fan Mode Report
// The device is reporting its current fan speed
def zwaveEvent(hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport cmd) {
def map = [:]
// Determine the fan speed the device is reporting, based on its ZWave id
map.value = fanModeMap.find {it.value == cmd.fanMode}?.key
map.name = "thermostatFanMode"
map.displayed = false
log.debug "Fan Mode Report=$value"
// Return the interpreted report
map
}
def zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
def map = [:]
switch (cmd.parameterNumber) {
// If the device is reporting its remote code
case commandParameters["remoteCode"]:
map.name = "remoteCode"
map.displayed = false
def short remoteCodeLow = cmd.configurationValue[1]
def short remoteCodeHigh = cmd.configurationValue[0]
map.value = (remoteCodeHigh << 8) + remoteCodeLow
// Display configured code in tile
log.debug "reported currentConfigCode=$map.value"
sendEvent("name":"currentConfigCode", "value":map.value)
break
// If the device is reporting its remote code
case commandParameters["tempOffsetParam"]:
map.name = "tempOffset"
map.displayed = false
def short offset = cmd.configurationValue[0]
if (offset >= 0xFB) {
// Hex FB-FF represent negative offsets FF=-1 - FB=-5
offset = offset - 256
}
map.value = offset
log.debug "reported offset=$map.value"
// Display temp offset in tile
sendEvent("name":"currentTempOffset", "value":map.value)
break
// If the device is reporting its oscillate mode
case commandParameters["oscillateSetting"]:
// determine if the device is oscillating
def oscillateMode = (cmd.configurationValue[0] == 0) ? "off" : "on"
//log.debug "Updated: Oscillate " + oscillateMode
map.name = "swingMode"
map.value = oscillateMode
map.displayed = false
map.isStateChange = oscillateMode != getDataByName("swingMode")
log.debug "reported swing mode = oscillateMode"
// Store and report the oscillate mode
updateState("swingMode", oscillateMode)
break
default:
log.warn "Unknown configuration report cmd.parameterNumber"
break;
}
map
}
// - Thermostat Supported Modes Report
// The device is reporting heating/cooling modes it supports
def zwaveEvent(hubitat.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
// Create a string with mode names for each available mode
def supportedModes = ""
if(cmd.off) { supportedModes += "off " }
if(cmd.heat) { supportedModes += "heat " }
//if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergencyHeat " }
if(cmd.cool) { supportedModes += "cool " }
//if(cmd.auto) { supportedModes += "auto " }
if(cmd.dryAir) { supportedModes += "dry " }
//if(cmd.autoChangeover) { supportedModes += "autoChangeover " }
// Report and save available modes
log.debug "Supported Modes: ${supportedModes}"
updateState("supportedModes", supportedModes)
}
// - Thermostat Fan Supported Modes Report
// The device is reporting fan speeds it supports
def zwaveEvent(hubitat.zwave.commands.thermostatfanmodev2.ThermostatFanModeSupportedReport cmd) {
// Create a string with mode names for each available mode
def supportedFanModes = ""
if(cmd.auto) { supportedFanModes += "fanAuto " }
if(cmd.low) { supportedFanModes += "fanLow " }
if(cmd.medium) { supportedFanModes += "fanMedium " }
if(cmd.high) { supportedFanModes += "fanHigh " }
// Report and save available speeds
log.debug "Supported Fan Modes: ${supportedFanModes}"
updateState("supportedFanModes", supportedFanModes)
}
// - Basic Report
// The device is sending standard ZWave updates
def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd) {
log.debug "Zwave event received: $cmd"
}
// - Command Report
// The device is reporting parameter settings
def zwaveEvent(hubitat.zwave.Command cmd) {
// simply report it
log.warn "Unexpected zwave command $cmd"
}
// Update State
// Store mode and settings
def updateState(String name, String value) {
state[name] = value
device.updateDataValue(name, value)
}
def ping() {
log.debug "ping called"
poll()
}
// Command Implementations
// Ask the device for its current state
def poll() {
def now=new Date()
def tz = location.timeZone
def nowString = now.format("MMM/dd HH:mm",tz)
sendEvent("name":"lastPoll", "value":nowString)
log.debug "Polling now $nowString"
// create a list of requests to send
def commands = []
commands << zwave.sensorMultilevelV1.sensorMultilevelGet().format() // current temperature
commands << zwave.batteryV1.batteryGet().format() // current battery level
commands << zwave.thermostatModeV2.thermostatModeGet().format() // thermostat mode
commands << zwave.thermostatFanModeV2.thermostatFanModeGet().format() // fan speed
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["remoteCode"]).format() // remote code
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["tempOffsetParam"]).format() // temp offset
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["oscillateSetting"]).format() // oscillate setting
// add requests for each thermostat setpoint available on the device
def supportedModes = getDataByName("supportedModes")
for (setpoint in setpointMap) {
// This code doesn't work correctly....Need to fix later for now only implemented supported modes for myself
//if (supportedModes.tokenize()?.contains(setpoint.key)) {
log.debug "Requesting setpoint $setpoint.value"
commands << [zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: setpoint.value).format()]
//} else {
// log.debug "Skipping unsupported mode $setpoint.key"
//}
}
// send the requests
delayBetween(commands, 2300)
}
def setHeatingSetpoint(degrees) {
def degreesInteger = degrees as Integer
def temperatureScale = getTemperatureScale()
if (temperatureScale == "C") {
// ZXT-120 lowest settings is 19 C
if (degreesInteger < 19) {
degreesInteger = 19;
}
// ZXT-120 highest setting is 28 C
if (degreesInteger > 28) {
degreesInteger = 28;
}
} else {
// ZXT-120 lowest settings is 67 F
if (degreesInteger < 67) {
degreesInteger = 67;
}
// ZXT-120 highest setting is 84
if (degreesInteger > 84) {
degreesInteger = 84;
}
}
log.debug "setHeatingSetpoint({$degreesInteger} ${temperatureScale})"
sendEvent("name":"heatingSetpoint", "value":degreesInteger)
//def celsius = (temperatureScale == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
//"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius*100) + "}"
//def setpointMode = hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1
//setThermostatSetpointForMode(degreesInteger.toDouble(), setpointMode)
}
def setCoolingSetpoint(degrees) {
def degreesInteger = degrees as Integer
def temperatureScale = getTemperatureScale()
if (temperatureScale == "C") {
// ZXT-120 lowest settings is 19 C
if (degreesInteger < 19) {
degreesInteger = 19;
}
// ZXT-120 highest setting is 28 C
if (degreesInteger > 28) {
degreesInteger = 28;
}
} else {
// ZXT-120 lowest settings is 67 F
if (degreesInteger < 67) {
degreesInteger = 67;
}
// ZXT-120 highest setting is 28
if (degreesInteger > 84) {
degreesInteger = 84;
}
}
log.debug "setCoolingSetpoint({$degreesInteger} ${temperatureScale})"
sendEvent("name":"coolingSetpoint", "value":degreesInteger)
// Sending temp to zxt-120
//def celsius = (temperatureScale == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
//"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius*100) + "}"
//def setpointMode = hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1
//setThermostatSetpointForMode(degreesInteger.toDouble(), setpointMode)
}
def setLearningPosition(position) {
log.debug "Setting learning postition: $position"
sendEvent("name":"learningPosition", "value":position)
def ctemp = 0
if (position < 12) {
ctemp=position+17
} else {
ctemp=position+7
}
def ftempLow=(Math.ceil(((ctemp*9)/5)+32)).toInteger()
def ftempHigh=ftempLow+1
def positionTemp = "not set"
switch (position) {
case 0:
positionTemp = 'Off'
break
case 1:
positionTemp = 'On(resume)'
break
case [3,4,5,6,8,9,10,11]:
positionTemp = "cool ${ctemp}C ${ftempLow}-${ftempHigh}F"
break
case [2,7]:
positionTemp = "cool ${ctemp}C ${ftempLow}F"
break
case [13,14,15,16,18,19,20,21]:
positionTemp = "heat ${ctemp}C ${ftempLow}-${ftempHigh}F"
break
case [12,17]:
positionTemp = "heat ${ctemp}C ${ftempLow}F"
break
case 22:
positionTemp = 'Dry mode'
break
default:
positionTemp = 'Invalid'
break
}
sendEvent("name":"learningPositionTemp", "value":positionTemp)
}
def issueLearningCommand() {
def position = device.currentValue("learningPosition").toInteger()
log.debug "Issue Learning Command pressed Position Currently: $position"
def positionConfigArray = [position]
log.debug "Position Config Array: ${positionConfigArray}"
delayBetween ([
// Send the new remote code
zwave.configurationV1.configurationSet(configurationValue: positionConfigArray,
parameterNumber: commandParameters["learningMode"], size: 1).format()
])
}
//***** Set the thermostat */
def setThermostatSetpoint(degrees) {
log.debug "setThermostatSetpoint called.....want to get rid of that"
// convert the temperature to a number and execute
setThermostatSetpoint(degrees.toDouble())
}
// Configure
// Syncronize the device capabilities with those that the UI provides
def configure() {
delayBetween([
// update the device's remote code to ensure it provides proper mode info
setRemoteCode(),
setTempOffset(),
// Request the device's current heating/cooling mode
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
// Request the device's current fan speed
zwave.thermostatFanModeV2.thermostatFanModeSupportedGet().format(),
// Assign the device to ZWave group 1
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()
], 2300)
}
// Switch Fan Mode
// Switch to the next available fan speed
def switchFanMode() {
// Determine the current fan speed setting
def currentMode = device.currentState("thermostatFanMode")?.value
def lastTriedMode = getDataByName("lastTriedFanMode") ?: currentMode.value ?: "off"
// Determine what fan speeds are available
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanLow"
def modeOrder = fanModes()
//log.info modeOrder
// Determine what the next fan speed should be
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
nextMode = next(nextMode)
}
// Make it so
switchToFanMode(nextMode)
}
// Switch to Fan Mode
// Given the name of a fan mode, make it happen
def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedFanModes")
if(supportedFanModes && !supportedFanModes.tokenize()?.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
// If the mode is even possible
if (nextMode in fanModes()) {
// Try to switch to the mode
updateState("lastTriedFanMode", nextMode)
return "$nextMode"() // Call the function perform the mode switch
} else {
// Otherwise, bail
log.debug("no fan mode method '$nextMode'")
}
}
// Get Data By Name
// Given the name of a setting/attribute, lookup the setting's value
def getDataByName(String name) {
state[name] ?: device.getDataValue(name)
}
// - Thermostat Setpoint Report
// The device is telling us what temperatures it is set to for a particular mode
def zwaveEvent(hubitat.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
{
log.info "RRG V1 ThermostatSetpointReport cmd=$cmd"
log.debug "cmd.scale=$cmd.scale"
log.debug "cmd.scaledValue=$cmd.scaledValue"
// Determine the temperature and mode the device is reporting
def cmdScale = cmd.scale == 1 ? "F" : "C"
def deviceScale = state.scale ?: 1
log.debug "deviceScale=${deviceScale}"
def deviceScaleString = deviceScale == 2 ? "C" : "F"
//NOTE: When temp is sent to device in Fahrenheit and returned in celsius
// 1 degree difference is normal. Device only has 1 degree celsius granularity
// issuing 80F for example returns 26C, which converts to 79F
// Maybe I should lie to user and report current set temp rather than reported temp
// to avoid confusion and false bug reports....needs to be considered.
def degrees = cmd.scaledValue
def reportedTemp
if (cmdScale == "C" && deviceScaleString == "F") {
log.debug "Converting celsius to fahrenheit"
reportedTemp = Math.ceil(celsiusToFahrenheit(degrees))
} else if (cmdScale == "F" && deviceScaleString == "C") {
log.debug "Converting fahrenheit to celsius"
reportedTemp = fahrenheitToCelsius(degrees)
} else {
log.debug "No Conversion needed"
reportedTemp = degrees
}
// Determine what mode the setpoint is for, if the mode is not valid, bail out
def name = setpointReportingMap.find {it.value == cmd.setpointType}?.key
if (name == null) {
log.warn "Setpoint Report for Unknown Type $cmd.setpointType"
return
}
// Return the interpretation of the report
log.debug "Thermostat Setpoint Report for $name = $reportedTemp forcing state change true"
sendEvent("name":name, "value":reportedTemp, "isStateChange":true)
}
// Set Thermostat Mode
// Set the device to the named mode
def setThermostatMode(String value) {
def commands = []
def degrees=0
def setpointMode=null
log.debug("setThermostatMode value:$value")
if (value == "cool") {
log.debug("Cool requested, sending setpoint cooling")
degrees = device.currentValue("coolingSetpoint")
setpointMode = hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1
} else if (value == "heat") {
log.debug("heat requested, sending setpoint heating")
degrees = device.currentValue("heatingSetpoint")
setpointMode = hubitat.zwave.commands.thermostatsetpointv1.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1
} else if (value == "dry" || value == "off" || value == "resume") {
log.debug("Dry Mode or Off no need to send temp")
} else {
log.warn("Unknown thermostat mode set:$value")
}
// Send temp if degrees set
if (degrees != 0 && setpointMode != null) {
log.debug "state.scale=${state.scale}"
def deviceScale = state.scale ?: 1
log.debug "deviceScale=${deviceScale}"
def deviceScaleString = deviceScale == 2 ? "C" : "F"
log.debug "deviceScaleString=${deviceScaleString}"
def locationScale = getTemperatureScale()
log.debug "state.precision=${state.precision}"
def p = (state.precision == null) ? 1 : state.precision
log.debug "p=${p}"
def convertedDegrees
if (locationScale == "C" && deviceScaleString == "F") {
log.debug "Converting celsius to fahrenheit"
convertedDegrees = Math.ceil(celsiusToFahrenheit(degrees))
} else if (locationScale == "F" && deviceScaleString == "C") {
log.debug "Converting fahrenheit to celsius"
convertedDegrees = fahrenheitToCelsius(degrees)
} else {
log.debug "No Conversion needed"
convertedDegrees = degrees
}
log.debug "convertedDegrees=${convertedDegrees}, degrees=${degrees}"
// Report the new temperature being set
log.debug "new temp ${degrees}"
log.debug("Sending Temp [$convertedDegrees] for $value mode before enabling mode")
// Send the new temperature from the thermostat and request confirmation
commands << zwave.thermostatSetpointV2.thermostatSetpointSet(setpointType: setpointMode, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format()
commands << zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: setpointMode).format()
}
// Set thermostat mode and request confirmation
commands << zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format()
commands << zwave.thermostatModeV2.thermostatModeGet().format()
// send the requests
delayBetween(commands, 2300)
}
// Set Thermostat Fan Mode
// Set the device to the named fan speed
def setThermostatFanMode(String value) {
log.debug "setThermostatFanMode to ${value} fanModeMap:${fanModeMap[value]}"
delayBetween([
// Command the device to change the fan speed
zwave.thermostatFanModeV2.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
// Request an update to make sure it worked
zwave.thermostatFanModeV2.thermostatFanModeGet().format()
])
}
// Mode Commands
// provide simple access to mode changes
// public interface commands for Thermostat
def cool() {
switchModeCool()
}
def heat() {
switchModeHeat()
}
def dry() {
switchModeDry()
}
def off() {
log.debug "${device.name} received off request"
switchModeOff()
}
def on() {
def onCommandVal = onCommand == null ? "on(resume)" : onCommand
log.debug "${device.name} received on request onCommandVal=${onCommandVal}"
switch (onCommandVal) {
case "on(resume)":
log.debug "issuing setThermostatMode:on"
setThermostatMode("resume")
break;
case ['cool','heat','dry']:
log.debug "issuing setThermostatMode:${onCommandVal}"
setThermostatMode(onCommandVal)
break;
default:
log.warn "Configuration Error: unknown onCommandVal: ${onCommandVal}"
break;
}
}
// switchModeCommands
def switchModeOff() {
setThermostatMode("off")
}
def switchModeHeat() {
setThermostatMode("heat")
}
def emergencyHeat() {
setThermostatMode("emergencyHeat")
}
def switchModeDry() {
setThermostatMode("dry")
}
def switchModeCool() {
setThermostatMode("cool")
}
def switchModeAuto() {
setThermostatMode("auto")
}
def autoChangeover() {
setThermostatMode("autoChangeover")
}
def switchFanLow() {
log.debug "setting fan mode low"
setThermostatFanMode("fanLow")
}
def switchFanMed() {
log.debug "setting fan mode med"
setThermostatFanMode("fanMedium")
}
def switchFanHigh() {
log.debug "setting fan mode high"
setThermostatFanMode("fanHigh")
}
def switchFanAuto() {
log.debug "setting fan mode auto"
setThermostatFanMode("fanAuto")
}
// Set Remote Code
// tell the ZXT-120 what remote code to use when communicating with the A/C
def setRemoteCode() {
// Load the user's remote code setting
def remoteCodeVal = remoteCode.toInteger()
// Divide the remote code into a 2 byte value
def short remoteCodeLow = remoteCodeVal & 0xFF
def short remoteCodeHigh = (remoteCodeVal >> 8) & 0xFF
def remoteBytes = [remoteCodeHigh, remoteCodeLow]
log.debug "New Remote Code: ${remoteBytes}"
delayBetween ([
// Send the new remote code
zwave.configurationV1.configurationSet(configurationValue: remoteBytes,
parameterNumber: commandParameters["remoteCode"], size: 2).format(),
// Request the device's remote code to make sure the new setting worked
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["remoteCode"]).format()
])
}
def setTempOffset() {
// Load the user's remote code setting
def tempOffsetVal = tempOffset == null ? 0 : tempOffset.toInteger()
// Convert negative values into hex value for this param -1 = 0xFF -5 = 0xFB
if (tempOffsetVal < 0) {
tempOffsetVal = 256 + tempOffsetVal
}
def configArray = [tempOffsetVal]
log.debug "TempOffset: ${tempOffsetVal}"
delayBetween ([
// Send the new remote code
zwave.configurationV1.configurationSet(configurationValue: configArray,
parameterNumber: commandParameters["tempOffsetParam"], size: 1).format(),
// Request the device's remote code to make sure the new setting worked
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["tempOffsetParam"]).format()
])
}
// Switch Fan Oscillate
// Toggle fan oscillation on and off
def switchFanOscillate() {
// Load the current swingmode and invert it (Off becomes true, On becomes false)
def swingMode = (getDataByName("swingMode") == "off")
// Make the new swingMode happen
setFanOscillate(swingMode)
}
def swingModeOn() {
log.debug "Setting Swing mode On"
setFanOscillate(true)
}
def swingModeOff() {
log.debug "Setting Swing mode Off"
setFanOscillate(false)
}
// Set Fan Oscillate
// Set the fan oscillation to On (swingMode == true) or Off (swingMode == false)
def setFanOscillate(swingMode) {
// Convert the swing mode requested to 1 for on, 0 for off
def swingValue = swingMode ? 1 : 0
log.debug "Sending Swing Mode swingValue=$swingValue"
delayBetween ([
// Command the new Swing Mode
zwave.configurationV1.configurationSet(configurationValue: [swingValue],
parameterNumber: commandParameters["oscillateSetting"], size: 1).format(),
// Request the device's swing mode to make sure the new setting was accepted
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["oscillateSetting"]).format()
])
}
This driver is the last piece of the puzzle for me, then I can finally disconnect SmartThings.
I'm not a developer either, so I wouldn't even know where to begin with trying to get full functionality back to this driver.
I tried going through this thread: https://community.hubitat.com/t/app-and-driver-porting-to-hubitat/812
Needless to say, I was lost. Any help out there?
Thank you!
I am in the same situation. This is the last device I have working on ST, and although I managed to see it in Hubitat using HubConnect, I would very much wish to have it controlled directly in Hubitat. Could anyone help us port this device handler, which is the one that in my experience has worked best?:
/**
def devVer() { return "1.0.1"}
def compileForC() {
def retVal = false // if using C mode, set this to true so that enums and colors are correct (due to ST issue of compile time evaluation)
return retVal
}
/*
preferences {
input("remoteCode", "number", title: "Remote Code", description: "The number of the remote to emulate")
input("tempOffset", "enum", title: "Temp correction offset (degrees C)?", options: ["-5","-4","-3","-2","-1","0","1","2","3","4","5"])
input("shortName", "string", title: "Short Name for Home Page Temp Icon", description: "Short Name:")
}
metadata {
definition (name: "ZXT-120 IR Sender imnotbob", namespace: "imnotbob", author: "imnotbob") {
capability "Actuator"
capability "Temperature Measurement"
//capability "Relative Humidity Measurement"
capability "Thermostat"
capability "Configuration"
capability "Polling"
capability "Sensor"
capability "Battery"
capability "Switch"
capability "Health Check"
// Commands that this device-type exposes for controlling the ZXT-120 directly
command "fanLow"
command "fanMed"
command "fanHigh"
command "switchFanMode"
command "switchFanOscillate"
command "setRemoteCode"
command "swingModeOn"
command "swingModeOff"
//commands for thermostat interface
command "eco"
command "dry"
command "setDrySetpoint", ["number"]
command "setAutoSetpoint", ["number"]
command "autoChangeover"
command "levelUpDown"
command "levelUp"
command "levelDown"
attribute "swingMode", "STRING"
attribute "lastPoll", "STRING"
attribute "currentConfigCode", "STRING"
attribute "currentTempOffset", "STRING"
attribute "currentemitterPower", "STRING"
attribute "currentsurroundIR", "STRING"
attribute "drySetpoint", "STRING"
attribute "autoSetpoint", "STRING"
attribute "lastTriedMode", "STRING"
attribute "supportedModes", "STRING"
attribute "lastTriedFanMode", "STRING"
attribute "supportedFanModes", "STRING"
// Z-Wave description of the ZXT-120 device
fingerprint deviceId: "0x0806"
fingerprint inClusters: "0x20,0x27,0x31,0x40,0x43,0x44,0x70,0x72,0x80,0x86"
}
// simulator metadata - for testing in the simulator
simulator {
// Not sure if these are correct
status "off" : "command: 4003, payload: 00"
status "heat" : "command: 4003, payload: 01"
status "cool" : "command: 4003, payload: 02"
status "auto" : "command: 4003, payload: 03"
status "emergencyHeat" : "command: 4003, payload: 04"
status "fanAuto" : "command: 4403, payload: 00"
status "fanOn" : "command: 4403, payload: 01"
status "fanCirculate" : "command: 4403, payload: 06"
status "heat 60" : "command: 4303, payload: 01 01 3C"
status "heat 68" : "command: 4303, payload: 01 01 44"
status "heat 72" : "command: 4303, payload: 01 01 48"
status "cool 72" : "command: 4303, payload: 02 01 48"
status "cool 76" : "command: 4303, payload: 02 01 4C"
status "cool 80" : "command: 4303, payload: 02 01 50"
status "temp 58" : "command: 3105, payload: 01 22 02 44"
status "temp 62" : "command: 3105, payload: 01 22 02 6C"
status "temp 70" : "command: 3105, payload: 01 22 02 BC"
status "temp 74" : "command: 3105, payload: 01 22 02 E4"
status "temp 78" : "command: 3105, payload: 01 22 03 0C"
status "temp 82" : "command: 3105, payload: 01 22 03 34"
// reply messages
reply "2502": "command: 2503, payload: FF"
}
tiles(scale: 2) {
multiAttributeTile(name:"temperature", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}°', foregroundColor: "#FFFFFF")
}
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("default", action: "levelUpDown")
attributeState("VALUE_UP", action: "levelUp")
attributeState("VALUE_DOWN", action: "levelDown")
}
/*
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
attributeState("default", label:'${currentValue}%', unit:"%")
}
*/
tileAttribute("device.thermostatFanMode", key: "SECONDARY_CONTROL") {
attributeState("default", label:'Fan ${currentValue}')
}
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
attributeState("idle", backgroundColor:"#44B621")
attributeState("heating", backgroundColor:"#FFA81E")
attributeState("cooling", backgroundColor:"#2ABBF0")
attributeState("fan only", backgroundColor:"#145D78")
attributeState("pending heat", backgroundColor:"#B27515")
attributeState("pending cool", backgroundColor:"#197090")
attributeState("vent economizer", backgroundColor:"#8000FF")
}
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
attributeState("off", label:'${name}')
attributeState("heat", label:'${name}')
attributeState("cool", label:'${name}')
attributeState("auto", label:'${name}')
attributeState("dry", label:'${name}')
}
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("default", label:'${currentValue}')
}
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("default", label:'${currentValue}')
}
}
valueTile("temp2", "device.temperature", width: 2, height: 2, canChangeIcon: true, foregroundColor: "#FFFFFF" ) {
state("default", label:'${currentValue}°', icon:"st.alarm.temperature.normal",
backgroundColors: getTempColors())
}
standardTile("thermostatMode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat", canChangeIcon: true, canChangeBackground: true) {
state "off", icon:"st.thermostat.heating-cooling-off", label: ' '
state "heat", icon:"st.thermostat.heat", label: ' '
state "cool", icon:"st.thermostat.cool", label: ' '
state "auto", icon:"st.thermostat.auto", label: ' '
state "dry", icon:"st.Weather.weather12", label: ' '
state "resume", icon:"st.Weather.weather12", label: ' '
state "autoChangeover", icon:"st.thermostat.auto", label: ' '
}
valueTile("battery", "device.battery", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "battery", label:'${currentValue}% battery'
}
standardTile("off", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, canChangeIcon: true, canChangeBackground: true) {
state "resume", action:"off", backgroundColor:"#92C081", icon: "st.thermostat.heating-cooling-off", label: 'Turn Off'
state "heat", action:"off", backgroundColor:"#92C081", icon: "st.thermostat.heating-cooling-off", label: 'Turn Off'
state "cool", action:"off", backgroundColor:"#92C081", icon: "st.thermostat.heating-cooling-off", label: 'Turn Off'
state "auto", action:"off", backgroundColor:"#92C081", icon: "st.thermostat.heating-cooling-off", label: 'Turn Off'
state "off", action:"on", backgroundColor:"#92C081", icon: "st.thermostat.heating-cooling-off", label: 'Turn On'
}
standardTile("cool", "device.thermostatMode", inactiveLabel: false) {
state "cool", action:"cool", label:'${name}', backgroundColor:"#0099FF", icon: "st.thermostat.cool"
}
standardTile("heat", "device.thermostatMode", inactiveLabel: false) {
state "heat", action:"heat", label:'${name}', backgroundColor:"#FF3300", icon: "st.thermostat.heat"
}
standardTile("auto", "device.thermostatMode", inactiveLabel: false) {
state "auto", action:"auto", label:'${name}', backgroundColor:"#b266b2", icon: "st.thermostat.auto"
}
standardTile("dry", "device.thermostatMode", inactiveLabel: false) {
state "dry", action:"dry", label:'${name}', backgroundColor:"#DBD099", icon: "st.Weather.weather12"
}
standardTile("autoChangeover", "device.thermostatMode", inactiveLabel: false) {
state "autoChangeover", action:"autoChangeover", label:'${name}', backgroundColor:"#b266b2", icon: "st.thermostat.auto"
}
standardTile("fanMode", "device.thermostatFanMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat", canChangeIcon: true, canChangeBackground: true) {
state "auto", icon:"st.Appliances.appliances11", label: 'Fan Auto'
state "on", icon:"st.Appliances.appliances11", label: 'Fan Low'
state "fanAuto", icon:"st.Appliances.appliances11", label: 'Fan Auto'
state "fanLow", icon:"st.Appliances.appliances11", label: 'Fan Low'
state "fanMedium", icon:"st.Appliances.appliances11", label: 'Fan Med'
state "fanHigh", icon:"st.Appliances.appliances11", label: 'Fan High'
}
standardTile("fanModeLow", "device.thermostatFanMode", inactiveLabel: false /* , decoration: "flat" */) {
state "fanLow", action:"fanLow", icon:"st.Appliances.appliances11", label: 'Fan Low'
}
standardTile("fanModeMed", "device.thermostatFanMode", inactiveLabel: false /*, decoration: "flat" */) {
state "fanMedium", action:"fanMed", icon:"st.Appliances.appliances11", label: 'Fan Med'
}
standardTile("fanModeHigh", "device.thermostatFanMode", inactiveLabel: false /*, decoration: "flat" */) {
state "fanHigh", action:"fanHigh", icon:"st.Appliances.appliances11", label: 'Fan High'
}
standardTile("fanModeAuto", "device.thermostatFanMode", inactiveLabel: false /*, decoration: "flat" */) {
state "fanAuto", action:"fanAuto", icon:"st.Appliances.appliances11", label: 'Fan Auto'
}
standardTile("swingMode", "device.swingMode", width: 2, height: 2, inactiveLabel: false, canChangeIcon: true, canChangeBackground: true) {
state "auto", action:"swingModeOff", icon:"st.secondary.refresh-icon", label: 'Swing Auto'
state "off", action:"swingModeOn", icon:"st.secondary.refresh-icon", label: 'Swing Off'
}
standardTile("swingModeOn", "device.swingMode", inactiveLabel: false /*, decoration: "flat" */) {
state "on", action:"swingModeOn", icon:"st.secondary.refresh-icon", label: 'Swing Auto'
}
standardTile("swingModeOff", "device.swingMode", inactiveLabel: false /*, decoration: "flat" */) {
state "off", action:"swingModeOff", icon:"st.secondary.refresh-icon", label: 'Swing Off'
}
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat", foregroundColor: "#FFFFFF") {
state "heatingSetpoint", label:'${currentValue}°', backgroundColor:"#FF3300"
}
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 2, width: 4, inactiveLabel: false, range: getTempRange() ) {
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor: "#d04e00"
}
valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat", foregroundColor: "#FFFFFF") {
state "coolingSetpoint", label:'${currentValue}°', backgroundColor:"#0099FF"
}
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 2, width: 4, inactiveLabel: false, range: getTempRange()) {
state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#1e9cbb"
}
valueTile("drySetpoint", "device.drySetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "drySetpoint", label:'${currentValue}° dry', backgroundColor:"#ffffff"
}
controlTile("drySliderControl", "device.drySetpoint", "slider", height: 2, width: 4, inactiveLabel: false) {
state "setDrySetpoint", action:"setDrySetpoint", backgroundColor: "#1e9cbb"
}
valueTile("autoSetpoint", "device.autoSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "autoSetpoint", label:'${currentValue}° auto', backgroundColor:"#ffffff"
}
controlTile("autoSliderControl", "device.autoSetpoint", "slider", height: 2, width: 4, inactiveLabel: false) {
state "setAutoSetpoint", action:"setAutoSetpoint", backgroundColor: "#1e9cbb"
}
valueTile("lastPoll", "device.lastPoll", height:2, width:2, inactiveLabel: false, decoration: "flat") {
state "lastPoll", label:'${currentValue}'
}
valueTile("currentConfigCode", "device.currentConfigCode", height:2, width: 2, inactiveLabel: false, decoration: "flat") {
state "currentConfigCode", label:'IR Config Code ${currentValue}'
}
valueTile("currentTempOffset", "device.currentTempOffset", height:2, width: 2, inactiveLabel: false, decoration: "flat") {
state "currentTempOffset", label:'Temp Offset ${currentValue}'
}
/*
standardTile("configure", "device.configure", height:2, width:2, inactiveLabel: false) {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
*/
standardTile("refresh", "device.thermostatMode", height:2, width:2, inactiveLabel: false) {
state "default", action:"polling.poll", icon:"st.secondary.refresh"
}
main "temp2"
details(["temperature",
"thermostatMode", "fanMode", "swingMode",
"heatingSetpoint", "heatSliderControl",
"coolingSetpoint", "coolSliderControl",
"drySetpoint", "drySliderControl",
"autoSetpoint", "autoSliderControl",
"off", "cool", "heat", "auto", "dry", /* "autoChangeover", */
"fanModeAuto", "fanModeLow", "fanModeMed", "fanModeHigh",
/* "swingModeOn", "swingModeOff", */
"battery", "lastPoll",
"currentConfigCode", "currentTempOffset",
/* "configure", */ "refresh"
])
}
}
def initialize() {
log.trace "initialize()"
}
def installed() {
log.trace "installed()"
def tempscale = getTemperatureScale()
if(!tz || !(tempscale == "F" || tempscale == "C")) {
log.warn "Timezone (${tz}) or Temperature Scale (${tempscale}) not set"
}
}
def ping() {
log.trace "ping()"
poll()
}
def updated() {
log.trace "updated()"
state.tempUnit = getTemperatureScale()
unschedule()
def random = new Random()
def random_int = random.nextInt(60)
def random_dint = random.nextInt(3)
schedule("${random_int} ${random_dint}/3 * * * ?", pollLite)
log.info "POLL scheduled (${random_int} ${random_dint}/3 * * * ?)"
if(device.getDataValue("supportedFanModes") || device.getDataValue("supportedModes")) {
device.updateDataValue("supportedFanModes", "")
device.updateDataValue("supportedModes", "")
device.updateDataValue("swingMode", "")
device.updateDataValue("lastTriedFanMode", "")
}
sendEvent(name: "checkInterval", value: 60 * 15, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID], displayed: false)
state.swVersion = devVer()
delayBetween([
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format(),
setRemoteCode(), // update the device's remote code to ensure it provides proper mode info
setTempOffset(),
zwave.thermostatModeV2.thermostatModeSupportedGet().format(), // Request the device's supported modes
zwave.thermostatFanModeV2.thermostatFanModeSupportedGet().format(), // Request the device's supported fan modes
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format() // Assign the device to ZWave group 1
], 1300)
}
def parse(String description)
{
//log.info "Parsing Description=$description"
// BatteryV1, ConfigurationV1, ThermostatModeV2, ThermostatOperatingStateV1, ThermostatSetpointV2, ThermostatFanModeV2, SensorMultilevelV1, SWITCHALLV1
//def map = createEvent(zwaveEvent(zwave.parse(description, [0x80:1, 0x70:1, 0x40:2, 0x42:1, 0x43:2, 0x44:2, 0x31:2, 0x27:1 ])))
def myzwave = zwave.parse(description, [0x80:1, 0x70:1, 0x40:2, 0x42:1, 0x43:2, 0x44:2, 0x31:2, 0x27:1 ])
//log.trace "myzwave is ${myzwave}"
def map = createEvent(zwaveEvent(myzwave))
if(!map) {
log.warn "parse called generating null map....why is this possible ? description=$description"
return null
}
//log.debug "Parse map=$map"
def result = [map]
if(map && map.name in ["heatingSetpoint","coolingSetpoint","drySetpoint", "autoSetpoint", "thermostatMode"]) {
def map2 = [
name: "thermostatSetpoint",
unit: getTemperatureScale(),
displayed: false
]
def map3 = [
name: "thermostatOperatingState",
displayed: false
]
if(map.name == "thermostatMode") {
updateState("lastTriedMode", map.value)
if(map.value == "cool") {
map2.value = device.latestValue("coolingSetpoint")
log.info "latest cooling setpoint = ${map2.value}"
map3.value = "cooling"
}
else if(map.value == "heat") {
map2.value = device.latestValue("heatingSetpoint")
log.info "latest heating setpoint = ${map2.value}"
map3.value = "heating"
}
else if(map.value == "dry") {
map2.value = device.latestValue("drySetpoint")
log.info "latest dry setpoint = ${map2.value}"
map3.value = "cooling"
}
else if(map.value == "auto") {
map2.value = device.latestValue("autoSetpoint")
log.info "latest auto setpoint = ${map2.value}"
map3.value = "heating"
}
else if(map.value == "off") {
map3.value = "idle"
}
}
else {
def mode = device.latestValue("thermostatMode")
//log.info "THERMOSTAT, latest mode = ${mode}"
if( (map.name == "heatingSetpoint" && mode == "heat") ||
(map.name == "coolingSetpoint" && mode == "cool") ||
(map.name == "drySetpoint" && mode == "dry") ||
(map.name == "autoSetpoint" && mode == "auto") ) {
map2.value = map.value
map2.unit = map.unit
}
}
if(map2?.value != null) {
//log.debug "THERMOSTAT, adding setpoint event: $map2"
result << createEvent(map2)
}
if(map3?.value != null) {
//log.debug "THERMOSTAT, adding operating state event: $map3"
result << createEvent(map3)
}
} else if(map.name == "thermostatFanMode" && map.isStateChange) {
updateState("lastTriedFanMode", map.value)
}
//log.debug "Parse returned $result"
result
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
{
//log.debug "ThermostatSetpointReport...cmd=$cmd"
def cmdScale = cmd.scale == 1 ? "F" : "C"
def map = [:]
map.value = convertTemperatureIfNeeded(cmd.scaledValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
//map.displayed = false
switch (cmd.setpointType) {
case physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1:
map.name = "heatingSetpoint"
break;
case physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1:
map.name = "coolingSetpoint"
break;
case physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_DRY_AIR:
map.name = "drySetpoint"
break;
case physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_AUTO_CHANGEOVER:
map.name = "autoSetpoint"
break;
default:
log.debug "Thermostat Setpoint Report for setpointType ${cmd.setpointType} = ${map.value} ${map.unit}"
return [:]
}
// So we can respond with same format
state.size = cmd.size
state.scale = cmd.scale
//state.precision = cmd.precision
state.precision = 1
//log.info "Thermostat Setpoint Report for ${map.name} = ${map.value} ${map.unit}"
map
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd)
{
//log.debug "SensorMultilevelReport...cmd=$cmd"
def map = [:]
switch (cmd.sensorType) {
case 1:
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmd.scale == 1 ? "F" : "C", cmd.precision)
map.unit = getTemperatureScale()
map.name = "temperature"
//log.info "SensorMultilevelReport temperature map.value=${map.value} ${map.unit}"
break;
default:
log.warn "Unknown sensorType ${cmd.sensorType} from device" // 5 is humidity in V2 and later
break;
}
map
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport cmd) {
def map = [:]
//log.debug "FanModeReport $cmd"
switch (cmd.fanMode) {
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_HIGH:
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_MEDIUM:
map.value = "auto"
break
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_LOW:
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_HIGH:
case physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_MEDIUM:
map.value = "on"
break
/*
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
map.value = "circulate"
break
*/
}
map.name = "thermostatFanMode"
map.displayed = false
//log.info "FanModeReport ${map.value}"
map
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
def map = [:]
//log.debug "ThermostatModeReport $cmd"
switch (cmd.mode) {
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
map.value = "off"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
map.value = "heat"
break
/*
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
map.value = "emergencyHeat"
break
*/
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
map.value = "cool"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
map.value = "auto"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_RESUME:
map.value = "resume"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_FAN_ONLY:
map.value = "fanonly"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_DRY_AIR:
map.value = "dry"
break
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO_CHANGEOVER:
map.value = "autoChangeover"
break
}
map.name = "thermostatMode"
log.info "Thermostat Mode reported : ${map.value.toString().capitalize()}"
map
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
def map = [:]
//log.debug "thermostatModeSupported $cmd"
def supportedModes = ""
if(cmd.off) { supportedModes += "off " }
if(cmd.heat) { supportedModes += "heat " }
//if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergencyHeat " }
if(cmd.cool) { supportedModes += "cool " }
if(cmd.auto) { supportedModes += "auto " }
if(cmd.dryAir) { supportedModes += "dry " }
//if(cmd.autoChangeover) { supportedModes += "autoChangeover " }
log.info "Supported Modes: ${supportedModes}"
updateState("supportedModes", supportedModes)
map
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeSupportedReport cmd) {
def map = [:]
//log.debug "fanModeSupported $cmd"
def supportedFanModes = ""
if(cmd.auto) { supportedFanModes += "fanAuto " }
if(cmd.low) { supportedFanModes += "fanLow " }
if(cmd.medium) { supportedFanModes += "fanMedium " }
if(cmd.high) { supportedFanModes += "fanHigh " }
log.info "Supported Fan Modes: ${supportedFanModes}"
updateState("supportedFanModes", supportedFanModes)
map
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel > 0 ? cmd.batteryLevel.toString() : 1
map.unit = "%"
map.displayed = false
//log.info "Battery Level Reported=$map.value"
map
}
def getCommandParameters() { [
"remoteCode": 27,
"tempOffsetParam": 37,
"oscillateSetting": 33,
"emitterPowerSetting": 28,
"surroundIRSetting": 32
]}
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
def map = [:]
//log.debug "ConfigurationReport $cmd"
map.displayed = false
switch (cmd.parameterNumber) {
// remote code
case commandParameters["remoteCode"]:
map.name = "currentConfigCode"
def short remoteCodeLow = cmd.configurationValue[1]
def short remoteCodeHigh = cmd.configurationValue[0]
map.value = (remoteCodeHigh << 8) + remoteCodeLow
//log.info "reported currentConfigCode=$map.value"
break
case commandParameters["tempOffsetParam"]:
map.name = "currentTempOffset"
def short offset = cmd.configurationValue[0]
if(offset >= 0xFB) { // Hex FB-FF represent negative offsets FF=-1 - FB=-5
offset = offset - 256
}
map.value = offset
//log.info "reported offset=$map.value C"
break
case commandParameters["emitterPowerSetting"]:
def power = (cmd.configurationValue[0] == 0) ? "normal" : "high"
map.name = "currentemitterPower"
map.value = power
//log.info "reported power ${cmd.configurationValue[0]} ${power}"
break
case commandParameters["surroundIRSetting"]:
def surround = (cmd.configurationValue[0] == 0) ? "disabled" : "enabled"
map.name = "currentsurroundIR"
map.value = surround
//log.info "reported surround ${cmd.configurationValue[0]} ${surround}"
break
case commandParameters["oscillateSetting"]:
def oscillateMode = (cmd.configurationValue[0] == 0) ? "off" : "auto" // THIS IS OFF, AUTO (default)
map.name = "swingMode"
map.value = oscillateMode
//log.info "reported swing mode = ${oscillateMode}"
state.swingMode = oscillateMode
break
default:
log.warn "Unknown configuration report ${cmd.parameterNumber}"
break;
}
map
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
//log.debug "Zwave event received: $cmd"
def map = [:]
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
updateDataValue("MSR", msr)
updateDataValue("manufacturer", cmd.manufacturerName)
//createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
map
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
def map = [:]
log.debug "Zwave event received: $cmd"
map
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
def map = [:]
log.warn "Unexpected zwave command $cmd"
map
}
def pollLite() {
//log.info "PollLite.."
sendHubCommand(new physicalgraph.device.HubAction(zwave.sensorMultilevelV2.sensorMultilevelGet().format())) // current temperature
def now=new Date()
def tz = location.timeZone
def nowString = now.format("MMM/dd HH:mm",tz)
sendEvent("name":"lastPoll", "value":nowString, displayed: false)
//refresh()
}
def refresh() {
def commands = []
//unschedule()
commands << zwave.sensorMultilevelV2.sensorMultilevelGet().format() // current temperature
commands << zwave.thermostatModeV2.thermostatModeGet().format() // thermostat mode
commands << zwave.thermostatFanModeV2.thermostatFanModeGet().format() // fan speed
delayBetween(commands, standardDelay)
}
def poll() {
def now=new Date()
def tz = location.timeZone
def tempscale = getTemperatureScale()
if(!tz || !(tempscale == "F" || tempscale == "C")) {
log.warn "Timezone (${tz}) or Temperature Scale (${tempscale}) not set"
return
}
def nowString = now.format("MMM/dd HH:mm",tz)
sendEvent("name":"lastPoll", "value":nowString)
log.info "Polling now $nowString"
def commands = []
commands << zwave.sensorMultilevelV2.sensorMultilevelGet().format() // current temperature
commands << zwave.batteryV1.batteryGet().format() // current battery level
commands << zwave.thermostatModeV2.thermostatModeGet().format() // thermostat mode
commands << zwave.thermostatFanModeV2.thermostatFanModeGet().format() // fan speed
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["remoteCode"]).format() // remote code
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["tempOffsetParam"]).format() // temp offset
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["oscillateSetting"]).format() // oscillate setting
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["emitterPowerSetting"]).format() // emitter setting
commands << zwave.configurationV1.configurationGet(parameterNumber: commandParameters["surroundIRSetting"]).format() // surround IR setting
// add requests for each thermostat setpoint available on the device
def supportedModes = getDataByName("supportedModes")
for (setpoint in setpointMap) {
commands << [zwave.thermostatSetpointV2.thermostatSetpointGet(setpointType: setpoint.value).format()]
}
delayBetween(commands, standardDelay)
}
def getSetpointMap() { [
"heatingSetpoint": physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_HEATING_1,
"coolingSetpoint": physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_COOLING_1,
"drySetpoint": physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_DRY_AIR,
"autoSetpoint": physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointSet.SETPOINT_TYPE_AUTO_CHANGEOVER
]}
def setThermostatMode(String value) {
def commands = []
def degrees = 0
log.debug "setting thermostat mode $value"
commands << zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format()
commands << zwave.thermostatModeV2.thermostatModeGet().format()
if(value == "cool") {
degrees = device.currentValue("coolingSetpoint")
commands << setCoolingSetpoint(degrees, true)
} else if(value == "heat") {
degrees = device.currentValue("heatingSetpoint")
commands << setHeatingSetpoint(degrees, true)
} else if(value == "dry" || value == "off" || value == "resume" || value == "auto") {
log.debug("Dry, Resume or Off no need to send temp")
} else {
log.warn("Unknown thermostat mode set:$value")
}
delayBetween(commands, standardDelay)
}
def getModeMap() { [
"off": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_OFF,
"heat": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_HEAT,
"cool": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_COOL,
"auto": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_AUTO,
"resume": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_RESUME,
"dry": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_DRY_AIR,
"autoChangeover": physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSet.MODE_AUTO_CHANGEOVER
]}
def setHeatingSetpoint(degrees) {
setHeatingSetpoint(degrees.toDouble())
}
def setHeatingSetpoint(Double degrees, nocheck = false, Integer delay = standardDelay) {
log.trace "setHeatingSetpoint($degrees, $delay)"
def commands = []
def hvacMode = device.latestValue("thermostatMode")
if(nocheck || hvacMode in ["heat"]) {
def convertedDegrees = checkValidTemp(degrees)
def deviceScale = state?.scale != null ? state.scale : 1
def deviceScaleString = deviceScale == 1 ? "F" : "C"
log.debug "setHeatingSetpoint({$convertedDegrees} ${deviceScaleString})"
def p = (state?.precision == null) ? 1 : state.precision
commands << zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format()
} else { log.warn "cannot change setpoint due to hvacMode: ${hvacMode}" }
commands << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
delayBetween(commands, delay)
}
def setCoolingSetpoint(degrees) {
setCoolingSetpoint(degrees.toDouble())
}
def setCoolingSetpoint(Double degrees, nocheck = false, Integer delay = standardDelay) {
log.trace "setCoolingSetpoint($degrees, $delay)"
def commands = []
def hvacMode = device.latestValue("thermostatMode")
if(nocheck || hvacMode in ["cool"]) {
def convertedDegrees = checkValidTemp(degrees)
def deviceScale = state?.scale != null ? state.scale : 1
def deviceScaleString = deviceScale == 1 ? "F" : "C"
log.debug "setCoolingSetpoint({$convertedDegrees} ${deviceScaleString})"
def p = (state?.precision == null) ? 1 : state.precision
commands << zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 2, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format()
} else { log.warn "cannot change setpoint due to hvacMode: ${hvacMode}" }
commands << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()
delayBetween(commands, delay)
}
def setDrySetpoint(degrees) {
setDrySetpoint(degrees.toDouble())
}
def setDrySetpoint(Double degrees, nocheck = false, Integer delay = standardDelay) {
log.trace "setDrySetpoint($degrees, $delay)"
def commands = []
def hvacMode = device.latestValue("thermostatMode")
if(nocheck || hvacMode in ["dry"]) {
def convertedDegrees = checkValidTemp(degrees)
def deviceScale = state?.scale != null ? state.scale : 1
def deviceScaleString = deviceScale == 1 ? "F" : "C"
log.debug "setDrySetpoint({$convertedDegrees} ${deviceScaleString})"
def p = (state?.precision == null) ? 1 : state.precision
commands << zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 8, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format()
} else { log.warn "cannot change setpoint due to hvacMode: ${hvacMode}" }
commands << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 8).format()
delayBetween(commands, delay)
}
def setAutoSetpoint(degrees) {
setAutoSetpoint(degrees.toDouble())
}
def setAutoSetpoint(Double degrees, nocheck = false, Integer delay = standardDelay) {
log.trace "setAutoSetpoint($degrees, $delay)"
def commands = []
def hvacMode = device.latestValue("thermostatMode")
if(nocheck || hvacMode in ["auto"]) {
def convertedDegrees = checkValidTemp(degrees)
def deviceScale = state?.scale != null ? state.scale : 1
def deviceScaleString = deviceScale == 1 ? "F" : "C"
log.debug "setAutoSetpoint({$convertedDegrees} ${deviceScaleString})"
def p = (state?.precision == null) ? 1 : state.precision
commands << zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 10, scale: deviceScale, precision: p, scaledValue: convertedDegrees).format()
} else { log.warn "cannot change setpoint due to hvacMode: ${hvacMode}" }
commands << zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 10).format()
delayBetween(commands, delay)
}
def checkValidTemp(degrees) {
def deviceScale = state?.scale != null ? state.scale : 1
def deviceScaleString = deviceScale == 1 ? "F" : "C"
def locationScale = getTemperatureScale()
def convertedDegrees = degrees
if(locationScale == "C" && deviceScaleString == "F") {
convertedDegrees = celsiusToFahrenheit(degrees).round(0).toInteger
} else if(locationScale == "F" && deviceScaleString == "C") {
def tval = fahrenheitToCelsius(degrees).toDouble()
//convertedDegrees = Math.round(tval.round(1) * 2) / 2.0f
convertedDegrees = tval.round(1)
log.warn "tval ${tval} ${convertedDegrees}"
}
def p = (state?.precision == null) ? 1 : state.precision
switch (p) {
case 0:
convertedDegrees = Math.ceil(convertedDegrees)
break
default:
break
}
def override = false
def overrideDegrees = convertedDegrees
if(deviceScaleString == "F") {
overrideDegrees = Math.ceil(convertedDegrees)
// ZXT-120 lowest settings is 67
if(overrideDegrees < 50) {
overrideDegrees = 50
override = true
}
// ZXT-120 highest setting is 90
if(overrideDegrees > 90) {
overrideDegrees = 90
override = true
}
} else if(deviceScaleString == "C") {
// ZXT-120 lowest settings is 19 C
if(overrideDegrees < 9) {
overrideDegrees = 9
override = true
}
// ZXT-120 highest setting is 28 C
if(overrideDegrees > 32) {
overrideDegrees = 32
override = true
}
} else { log.error "checkValidTemp: unknown device scale" }
if(override) {
log.warn "overriding temp ${convertedDegrees} to ${overrideDegrees}"
}
convertedDegrees = overrideDegrees
return convertedDegrees
}
def levelUp() {
levelUpDown(1)
}
def levelDown() {
levelUpDown(-1)
}
def levelUpDown(tempVal) {
//LogAction("levelUpDown()...($tempVal | $chgType)", "trace")
def hvacMode = device.latestValue("thermostatMode")
def cmds = []
if(hvacMode in ["heat", "cool", "auto", "dry"]) {
// From RBOY https://community.smartthings.com/t/multiattributetile-value-control/41651/23
// Determine OS intended behaviors based on value behaviors (urrgghhh.....ST!)
def upLevel
if(!state?.lastLevelUpDown) { state.lastLevelUpDown = 0 } // If it isn't defined lets baseline it
if((state.lastLevelUpDown == 1) && (tempVal == 1)) { upLevel = true } //Last time it was 1 and again it's 1 its increase
else if((state.lastLevelUpDown == 0) && (tempVal == 0)) { upLevel = false } //Last time it was 0 and again it's 0 then it's decrease
else if((state.lastLevelUpDown == -1) && (tempVal == -1)) { upLevel = false } //Last time it was -1 and again it's -1 then it's decrease
else if((tempVal - state.lastLevelUpDown) > 0) { upLevel = true } //If it's increasing then it's up
else if((tempVal - state.lastLevelUpDown) < 0) { upLevel = false } //If it's decreasing then it's down
else { log.error "UNDEFINED STATE, CONTACT DEVELOPER. Last level $state.lastLevelUpDown, Current level, $value" }
state.lastLevelUpDown = tempVal // Save it
def targetVal = 0.0
def curThermSetpoint = device.latestValue("thermostatSetpoint")
switch (hvacMode) {
case "heat":
def curHeatSetpoint = device.currentValue("heatingSetpoint")
targetVal = curHeatSetpoint ?: 0.0
break
case "cool":
def curCoolSetpoint = device.currentValue("coolingSetpoint")
targetVal = curCoolSetpoint ?: 0.0
break
case "dry":
def curDrySetpoint = device.currentValue("drySetpoint")
targetVal = curDrySetpoint ?: 0.0
break
case "auto":
def curAutoSetpoint = device.currentValue("autoSetpoint")
targetVal = curAutoSetpoint ?: 0.0
break
default:
log.warn "Change in Unsupported Mode Received: ($hvacMode}!!!"
return []
break
}
if(targetVal == 0.0) { log.warn "No targetVal"; return }
//def tempUnit = device.currentValue('temperatureUnit')
if(upLevel) {
//LogAction("Increasing by 1 increment")
targetVal = targetVal.toDouble() + 1.0
/*
if(tempUnit == "C" ) {
targetVal = targetVal.toDouble() + 0.5
if(targetVal < 9.0) { targetVal = 9.0 }
if(targetVal > 32.0 ) { targetVal = 32.0 }
} else {
targetVal = targetVal.toDouble() + 1.0
if(targetVal < 50.0) { targetVal = 50 }
if(targetVal > 90.0) { targetVal = 90 }
}
/
} else {
//LogAction("Reducing by 1 increment")
targetVal = targetVal.toDouble() - 1.0
/
if(tempUnit == "C" ) {
targetVal = targetVal.toDouble() - 0.5
if(targetVal < 9.0) { targetVal = 9.0 }
if(targetVal > 32.0 ) { targetVal = 32.0 }
} else {
targetVal = targetVal.toDouble() - 1.0
if(targetVal < 50.0) { targetVal = 50 }
if(targetVal > 90.0) { targetVal = 90 }
}
*/
}
if(targetVal != curThermSetpoint) {
log.info "Sending changeSetpoint(Temp: ${targetVal})"
switch (hvacMode) {
case "heat":
cmds << setHeatingSetpoint(targetVal)
break
case "cool":
cmds << setCoolingSetpoint(targetVal)
break
case "dry":
cmds << setDrySetpoint(targetVal)
break
case "auto":
cmds << setAutoSetpoint(targetVal)
break
default:
log.warn "Unsupported Mode Received: ($hvacMode}!!!"
break
}
}
} else { log.warn "levelUpDown: Cannot adjust temperature due to hvacMode ${hvacMode}" }
if(cmds) { delayBetween(cmds, standardDelay) }
}
def setRemoteCode() {
def remoteCodeVal = remoteCode.toInteger()
// Divide the remote code into a 2 byte value
def short remoteCodeLow = remoteCodeVal & 0xFF
def short remoteCodeHigh = (remoteCodeVal >> 8) & 0xFF
def remoteBytes = [remoteCodeHigh, remoteCodeLow]
log.debug "New Remote Code: ${remoteBytes}"
delayBetween ([
zwave.configurationV1.configurationSet(configurationValue: remoteBytes, parameterNumber: commandParameters["remoteCode"], size: 2).format(),
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["remoteCode"]).format()
], standardDelay)
}
def setTempOffset() {
def tempOffsetVal = tempOffset == null ? 0 : tempOffset.toInteger()
if(tempOffsetVal < 0) { // Convert negative values into hex value for this param -1 = 0xFF -5 = 0xFB
tempOffsetVal = 256 + tempOffsetVal
}
def configArray = [tempOffsetVal]
log.debug "TempOffset: ${tempOffsetVal}"
delayBetween ([
zwave.configurationV1.configurationSet(configurationValue: configArray, parameterNumber: commandParameters["tempOffsetParam"], size: 1).format(),
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["tempOffsetParam"]).format()
], standardDelay)
}
def switchFanOscillate() {
def swingMode = (getDataByName("swingMode") == "off") ? true : false // Load the current swingmode and invert it (Off becomes true, On becomes false)
setFanOscillate(swingMode)
}
def swingModeOn() {
log.debug "Setting Swing mode AUTO"
setFanOscillate(true)
}
def swingModeOff() {
log.debug "Setting Swing mode Off"
setFanOscillate(false)
}
def setFanOscillate(swingMode) {
def swingValue = swingMode ? 1 : 0 // Convert the swing mode requested to 1 for on, 0 for off
def hvacMode = device.latestValue("thermostatMode")
if( !(hvacMode in ["heat","cool","auto","dry"]) ) {
log.warn "wrong mode ${hvacMode}"
} else {
delayBetween ([
zwave.configurationV1.configurationSet(configurationValue: [swingValue], parameterNumber: commandParameters["oscillateSetting"], size: 1).format(),
zwave.configurationV1.configurationGet(parameterNumber: commandParameters["oscillateSetting"]).format()
], standardDelay)
}
}
def updateState(String name, String value) {
state[name] = value
sendEvent(name: "${name}", value: "${value}", displayed: false)
//device.updateDataValue(name, value)
}
def getDataByName(String name) {
//state[name] ?: device.getDataValue(name)
state[name] ?: device.currentState("${name}")?.value
}
def fanModes() {
["fanAuto", "fanOn", "fanLow", "fanMedium", "fanHigh"]
}
def switchFanMode() {
def currentMode = device.currentState("thermostatFanMode")?.value
if(currentMode == "auto") { currentMode = "fanAuto" }
else if(currentMode == "on") { currentMode = "fanLow" }
else { currentMode == null }
def lastTriedMode = getDataByName("lastTriedFanMode") ?: currentMode.value ?: "fanAuto"
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanLow"
def modeOrder = fanModes()
//log.info modeOrder
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
def nextMode = next(lastTriedMode)
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
nextMode = next(nextMode)
}
switchToFanMode(nextMode)
}
def switchToFanMode(nextMode) {
def supportedFanModes = getDataByName("supportedFanModes")
if(supportedFanModes && !supportedFanModes.tokenize()?.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
if(nextMode in fanModes()) {
updateState("lastTriedFanMode", nextMode)
return "$nextMode"()
} else {
log.debug("no fan mode method '$nextMode'")
}
}
def setThermostatFanMode(String value) {
log.info "fan mode " + value + " ${fanModeMap[value]}"
delayBetween([
zwave.thermostatFanModeV2.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
zwave.thermostatFanModeV2.thermostatFanModeGet().format()
], standardDelay)
}
def getFanModeMap() { [
"auto": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_LOW,
"circulate": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_LOW,
"on": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_LOW,
"fanAuto": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_AUTO_LOW,
"fanOn": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_LOW,
"fanLow": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_LOW,
"fanMedium": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_MEDIUM,
"fanHigh": physicalgraph.zwave.commands.thermostatfanmodev2.ThermostatFanModeReport.FAN_MODE_HIGH
]}
def auto() {
log.debug "${device.name} received AUTO request"
setThermostatMode("auto")
}
def cool() {
log.debug "${device.name} received COOL request"
setThermostatMode("cool")
}
def emergencyHeat() {
log.warn "emergencyheat() not supported"
return
setThermostatMode("emergencyHeat")
}
def heat() {
log.debug "${device.name} received HEAT request"
setThermostatMode("heat")
}
def off() {
log.debug "${device.name} received OFF request"
setThermostatMode("off")
}
def fanAuto() {
log.debug "${device.name} received FANAUTO request"
delayBetween([
zwave.thermostatFanModeV2.thermostatFanModeSet(fanMode: 0).format(),
zwave.thermostatFanModeV2.thermostatFanModeGet().format()
], standardDelay)
}
def fanCirculate() {
log.warn "fanCirculate() not supported"
return
delayBetween([
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
], standardDelay)
}
def fanOn() {
log.debug "${device.name} received FANON request"
delayBetween([
zwave.thermostatFanModeV2.thermostatFanModeSet(fanMode: 1).format(),
zwave.thermostatFanModeV2.thermostatFanModeGet().format()
], standardDelay)
}
def on() {
log.debug "${device.name} received on request"
setThermostatMode("resume")
}
private getStandardDelay() {
1000
}
def eco() {
log.debug "${device.name} received ECO request"
setThermostatMode("off")
}
def dry() {
log.debug "${device.name} received DRY request"
setThermostatMode("dry")
}
def autoChangeover() {
log.debug "${device.name} received AUTOCHANGEOVER request"
setThermostatMode("autoChangeover")
}
def fanLow() {
log.debug "setting fan mode low"
setThermostatFanMode("fanLow")
}
def fanMed() {
log.debug "setting fan mode med"
setThermostatFanMode("fanMedium")
}
def fanHigh() {
log.debug "setting fan mode high"
setThermostatFanMode("fanHigh")
}
def wantMetric() { return (state?.tempUnit == "C") }
def getTempColors() {
def colorMap
//if (wantMetric()) {
if (compileForC()) {
colorMap = [
// Celsius Color Range
[value: 0, color: "#153591"],
[value: 7, color: "#1e9cbb"],
[value: 15, color: "#90d2a7"],
[value: 23, color: "#44b621"],
[value: 29, color: "#f1d801"],
[value: 33, color: "#d04e00"],
[value: 36, color: "#bc2323"]
]
} else {
colorMap = [
// Fahrenheit Color Range
[value: 40, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 92, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
}
def getTempRange() {
def range
//if (wantMetric()) {
if (compileForC()) {
// Celsius Range
range = "(9..32)"
} else {
// Fahrenheit Color Range
range = "(50..90)"
}
}
See also:
Hi Guys. Old thread, but I am trying to use this IR blaster to send an on and off command. I have the driver loaded, how do you do the IR learning process? Thanks!
Another community-developed driver is now available:
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.