Centralite HA Thermostat

Part Number 3156105.

Anyone know if this is compatible with SmartThings?

Anyone have a working link to a list of compatible devices?

Thanks,
John

Hi John,

That particular device isnā€™t yet officially compatible.

Hereā€™s the list for you:

Thank you for the information Tyler!

We are working on the new CentraLite thermostat. I wonder if you try and pair this one what happens. Iā€™d bet its not very different from their new model.

I tried to pair it the other day - never saw it show up in my device list - even as a Thingā€¦

Would you confirm how to put these in pairing mode? My understanding is that you press HOLD and RESET and then release both at the same time. When I do this, I see a symbol that looks like radio waves that blinks for a few seconds.

Thanks again for any help you can provide.

I managed to pair one of these. It paired fine and I associated it with a custom Device Type. I started with the example Centralite Thermostat code. It didnā€™t quite work. Iā€™ve managed to rewrite the parse code so Iā€™m reliably able to read the current temp, setpoints, hold and fan switch. But I canā€™t send it commands. Mainly because I donā€™t know where to begin with that. It keeps sending what I believe is a request for the current time:
0104 000A 01 01 0140 00 3244 00 00 0000 00 00 0700
Iā€™ve tried a few wattr messages and they donā€™t seem to do anything. Perhaps it needs the time first?

Carl,

Do you remember the sequence to put the thermostat into pairing mode?

Thanks,
john

John,

Yes, to get this thermostat into pairing mode you start by removing the plastic cover to expose the screw terminals. Second, press and hold the ā€˜Holdā€™ button. While youā€™re holding the ā€˜Holdā€™ button, press and release the momentary switch to the left of the screw terminals. Then you can release the ā€˜Holdā€™ button. The thermostat should start blinking the wireless icon until it finds the controller waiting to pair. This part can happen fairly quickly. So, it might go solid right away. Pairing doesnā€™t seem to complete, however for several minutes.

Carl

Thanks Carl - it paired -

The example Centralite device type is basically functional now. I suppose thereā€™s been a firmware update to make the thermostat fields parse better. I still donā€™t know how to set the tim on the thermostat. Iā€™ll try a couple wattr commands and see how far I get.

1 Like

Carl,

I must be missing something hereā€¦

I pair the thermostat, it shows as a Thing on my developer page. I edit the device and change it to the CentraLite Thermostat.

I open the app on my phone, I am prompted to name the new device.

But none of the panes are updating - just dashes for the temp - the rest are just white/grey.

Thanks again for all your help,
John

John,

Thatā€™s not quite how I set mine up. I started in My Device Types where I set up a New SmartDevice.
Once in the code for the smartdevice I chose Centralite Thermostat from the Device Type Examples list and replaced my code with their example.
I found the log line in parse to be very helpful. It logs all the incoming messages from the device. In a previous version all I was getting were catchall: messages. Smartthings must have updated their zigbee parsing somewhere because today itā€™s recognizing read attr responses. I still get the occasional catchall and Iā€™m trying to work out what those messages are.
Iā€™ve been unsuccessful at sending the time and date info to the thermostat. Iā€™m not sure what attribute codes to use. Iā€™ve done some zigbee packet sniffing with a new killerbee setup at work and all the other clusters report their attributes and datatypes except for the Time cluster so Iā€™m a little stuck.

Carl

How is this working these days?

I just heard back from Centralite tech support, and they said it is indeed compatible. The person I was in contact with said thisā€¦

ā€œ***As far as our thermostat working most of the people that work here have the Smart Things Hub and our devices, they work flawlessly. Pairing our thermostat, no our name is not on the list but it will connect fine if you select the ā€œConnect New Deviceā€ option. Once it connects it will come up Centralite Thermostat. If you have the hub looking for a device the first time you power up our thermostat it will join automatically.***ā€

So, unless I hear any major BAD things from anybody here, I am going to buy one and see how it goes.

The next thing is to figure out which SmartApp to use to accomplish an ā€˜Autoā€™ feature so that it goes back and forth between heating and cooling automatically for meā€¦itā€™s crazy during these transition seasons. Any ideas on that?

I added the supplied device type in the IDE, but itā€™s still not pairing. I know Iā€™m probably missing something (still noobish).

Any pointers?

EDIT: Actually, I got it paired. For me, it took holding the HOLD button while pressing reset switch, but I had to hold it for a couple seconds after pressing reset.

Now the problem is that itā€™s only showing up as ā€˜Thingā€™ lol

I think I recall reading somebody talk about going in and replacing the code in the discovered app with that from the ST device type for this Tstat. So, thatā€™s what Iā€™m going to try nextā€¦

WORKING! :slight_smile:
Well, itā€™s workingā€¦ All I had to do was go into the IDE and change the device type in the settings for this device on the ā€˜My Devicesā€™ page.

I am now able to see temp, heat/cool status, fan status, heat set point, cool set point, refresh and Configure (which does nothingā€¦have seen ā€˜Configureā€™ in other things, but it never does anything; not sure why itā€™s in there).

I am also able to manipulate the Tstat from the ST app, and it reacts properly when triggered by temp changes.

Now Iā€™m off to figure out how to actually install it lol

Now Iā€™m having a problemā€¦
Interaction is still working in that I can change settings (mode, fan mode, setpoints, etc) from the ST app, but now itā€™s no longer updating automatically. The only way I can get it to reflect correctly in the ST app is if I press the refresh button in there.

Have any of you (@jhart, @Tyler, @urman, @carlism) seen it do thisā€¦and/or is this a common or know problem with ST?

Any suggestions?

How are you all setting this up? I can only turn the fan from auto to on, I canā€™t set or read temperatures or anything.

Try this device which is already supported by ST.

Thatā€™s the one I used. It can only turn fan from auto to on, it canā€™t set or read temperature for me.

Try below. I modified codes for my PEQ Thermostat which made by Centralite

/**
 *  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.
 *
 *	Author: SmartThings
 *	Date: 2013-12-02
 */
metadata {
	definition (name: "ZigBee CentraLite Thermostat", namespace: "smartthings", author: "SmartThings") {
		capability "Actuator"
		capability "Temperature Measurement"
		capability "Thermostat"
		capability "Configuration"
		capability "Refresh"
		capability "Sensor"
		capability "Polling"
		capability "Battery"
		

		// Custom commands 
        command "raiseHeatLevel"
		command "lowHeatLevel"
		command "raiseCoolLevel"
		command "lowCoolLevel"
		command "setTemperature"
		command "setThermostatHoldMode"
		command "getPowerSource"

		attribute "temperatureScale", "string"
		attribute "thermostatHoldMode", "string"
		attribute "powerSource", "string"
		
		fingerprint profileId: "0104", inClusters: "0000,0001,0003,0020,0201,0202,0204,0B05", outClusters: "000A, 0019"
	}

	// simulator metadata
	simulator { }

	tiles(scale: 2) {
	
        multiAttributeTile(name:"summary", type: "thermostat", width: 6, height: 4) {
        	tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
				attributeState("temperature", label:'${currentValue}Ā°', unit:"dF",
				backgroundColors:[
                		// 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"],
						// Fahrenheit Color Range
						[value: 40, color: "#153591"],
						[value: 44, color: "#1e9cbb"],
						[value: 59, color: "#90d2a7"],
                        [value: 65, color: "#99ff33"],
						[value: 74, color: "#44b621"],
						[value: 84, color: "#f1d801"],
						[value: 92, color: "#d04e00"],
						[value: 96, color: "#bc2323"]
					])
			}
			tileAttribute("device.temperature", key: "VALUE_CONTROL") {
                attributeState("default", action: "setTemperature")
			}
            
			tileAttribute("device.powerSource", key: "SECONDARY_CONTROL") {
				attributeState("default", label:'PowerSrc: ${currentValue}')
			}            
			
            tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
            	attributeState("default", label:'${currentValue}', unit:"dF")
            }
			tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
				attributeState("default", label:'${currentValue}', unit:"dF")
			}
 		}
		
		valueTile("temperature", "device.temperature", width: 2, height: 2) {
			state("temperature", label:'${currentValue}Ā°', unit:"",
				backgroundColors:[
                		// 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"],
						// Fahrenheit Color Range
						[value: 40, color: "#153591"],
						[value: 44, color: "#1e9cbb"],
						[value: 59, color: "#90d2a7"],
                        [value: 65, color: "#99ff33"],
						[value: 74, color: "#44b621"],
						[value: 84, color: "#f1d801"],
						[value: 92, color: "#d04e00"],
						[value: 96, color: "#bc2323"]
				]
			)
		}

		standardTile("mode", "device.thermostatMode", height: 2, width: 2, inactiveLabel: false, canChangeIcon: false) {
			state "off", label:'', action:"thermostat.setThermostatMode", icon: "st.thermostat.heating-cooling-off"
			state "cool", label:'', action:"thermostat.setThermostatMode", icon: "st.thermostat.cool", backgroundColor: '#99ddff'
			state "heat", label:'', action:"thermostat.setThermostatMode", icon: "st.thermostat.heat", backgroundColor: '#fd631c'
			state "emergencyHeat", label:'', action:"thermostat.setThermostatMode", icon: "st.thermostat.emergency-heat", backgroundColor: '#E11102'
			//state "auto", label:'', action:"thermostat.setThermostatMode", icon: "st.thermostat.auto", backgroundColor: '#ffff00'
		}
		
		standardTile("fanMode", "device.thermostatFanMode", height: 2, width: 2, inactiveLabel: false, canChangeIcon: false) {
			state "fanAuto", label:'', action:"thermostat.setThermostatFanMode", icon: "st.thermostat.fan-auto", backgroundColor: '#ffff00'
			state "fanOn", label:'', action:"thermostat.setThermostatFanMode", icon: "st.thermostat.fan-on", backgroundColor: '#ffff00'
		}

		standardTile("holdMode", "device.thermostatHoldMode", height: 2, width: 2, inactiveLabel: false, canChangeIcon: false) {
			state "holdOff", label:'Hold Off', action:"setThermostatHoldMode", icon: "st.Lighting.light13", backgroundColor:"#ffffff", nextState:"holdOff"
			state "holdOn", label:'Hold On', action:"setThermostatHoldMode", icon: "st.Lighting.light11", backgroundColor:"#ffb3ff", nextState:"holdOn"
		}
		
        standardTile("raiseHeatLevel", "device.heatingSetpoint", canChangeIcon: false, , height: 0.5, width: 0.5, inactiveLabel: false, decoration: "flat") {
            state "raiseHeatLevel", label:'  ', action:"raiseHeatLevel", icon:"st.thermostat.thermostat-up"
        }
		valueTile("heatingSetpoint", "device.heatingSetpoint", height: 1, width: 2, inactiveLabel: false, decoration: "flat") {
			state "heat", label:'${currentValue}Ā° Heat', unit:"", backgroundColor:"#fd631c"
		}
        standardTile("lowHeatLevel", "device.heatingSetpoint", canChangeIcon: false, , height: 0.5, width: 0.5, inactiveLabel: false, decoration: "flat") {
            state "lowHeatLevel", label:'  ', action:"lowHeatLevel", icon:"st.thermostat.thermostat-down"
        }
		controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 4, inactiveLabel: false) {
			state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00"
		}

        standardTile("raiseCoolLevel", "device.heatingSetpoint", canChangeIcon: false, , height: 0.5, width: 0.5, inactiveLabel: false, decoration: "flat") {
            state "raiseCoolLevel", label:'  ', action:"raiseCoolLevel", icon:"st.thermostat.thermostat-up"
        }
 		valueTile("coolingSetpoint", "device.coolingSetpoint", , height: 1, width: 2, inactiveLabel: false, decoration: "flat") {
			state "cool", label:'${currentValue}Ā° Cool', unit:"", backgroundColor:"#66ccff"
		}
        standardTile("lowCoolLevel", "device.heatingSetpoint", canChangeIcon: false, , height: 0.5, width: 0.5, inactiveLabel: false, decoration: "flat") {
            state "lowCoolLevel", label:'  ', action:"lowCoolLevel", icon:"st.thermostat.thermostat-down"
        }
		controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 4, inactiveLabel: false) {
			state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#003CEC"
		}

		standardTile("refresh", "device.temperature", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
			state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		standardTile("configure", "device.configure", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
			state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		}
		valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
			state "battery", label:'${currentValue}% Battery', unit:""
		}
		main(["temperature", "summary"])
		details(["summary", "holdMode", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl",  "coolingSetpoint", "battery", "refresh"])
	}
}


def setTemperature(setpoint) {
	log.debug "setTemperature() called with setpoint ${setpoint}. "
 	log.debug "Current temperature: ${device.currentValue("temperature")}. Heat Setpoint: ${device.currentValue("heatingSetpoint")}. Cool Setpoint: ${device.currentValue("coolingSetpoint")}"
   
    def mode = device.currentValue("thermostatMode")
    def midpoint
	def targetvalue

	if (mode == "off") {
		log.warn "setTemperature(): this mode: $mode does not allow raiseSetpoint"
        return
    } 
    
    def currentTemp = device.currentValue("temperature")
    def deltaTemp = setpoint - currentTemp
    
    log.debug "deltaTemp = ${deltaTemp}"
    
    if (mode == "heat") {
    	// Change the heat
        log.debug "setTemperature(): change the heat temp"
		 if (deltaTemp < 0) {
			lowHeatLevel()
        } else if (deltaTemp > 0) {
			raiseHeatLevel()
		}
    } else if (mode == "cool") {
    	// Change the cool
        log.debug "setTemperature(): change the cool temp"
		 if (deltaTemp < 0) {
			lowCoolLevel()
        } else if (deltaTemp > 0) {
			raiseCoolLevel()
		}
    }
}


def raiseHeatLevel(){

    def mode = device.currentValue("thermostatMode")

 	if (mode == "off") {
		log.warn "raiseHeatLevel(): this mode: $mode does not allow raiseHeatLevel"
	} else {
		def locationScale = getTemperatureScale()
		def maxTemp
		def minTemp
		if (locationScale == "C") {
			maxTemp = 44 // Max Temp in C
			minTemp = 7 // Min Temp in C
			log.trace "Location is in Celsius, MaxTemp: $maxTemp, MinTemp: $minTemp"
		} else {
			maxTemp = 86 // Max temp in F
			minTemp = 30 // Max temp in F
			log.trace "Location is in Farenheit, MaxTemp: $maxTemp, MinTemp: $minTemp"
		}
		
		def currentLevel = device.currentValue("heatingSetpoint")
		int nextLevel = currentLevel.toInteger() + 1
    
		if( nextLevel > maxTemp){
			nextLevel = maxTemp
		}
		log.debug "Setting heat set point up to: ${nextLevel}"
		setHeatingSetpoint(nextLevel)
	}
}

def lowHeatLevel(){
 	if (mode == "off") {
		log.warn "lowHeatLevel(): this mode: $mode does not allow lowHeatLevel"
	} else {
		def locationScale = getTemperatureScale()
		def maxTemp
		def minTemp
		if (locationScale == "C") {
			maxTemp = 44 // Max Temp in C
			minTemp = 7 // Min Temp in C
			log.trace "Location is in Celsius, MaxTemp: $maxTemp, MinTemp: $minTemp"
		} else {
			maxTemp = 86 // Max temp in F
			minTemp = 30 // Max temp in F
			log.trace "Location is in Farenheit, MaxTemp: $maxTemp, MinTemp: $minTemp"
		}

		def currentLevel = device.currentValue("heatingSetpoint")
		int nextLevel = currentLevel.toInteger() - 1
    
		if( nextLevel < minTemp){
			nextLevel = minTemp
		}
		log.debug "Setting heat set point down to: ${nextLevel}"
		setHeatingSetpoint(nextLevel)
	}
}

def raiseCoolLevel(){
 	if (mode == "off") {
		log.warn "raiseCoolLevel(): this mode: $mode does not allow raiseCoolLevel"
	} else {
		def locationScale = getTemperatureScale()
		def maxTemp
		def minTemp
		if (locationScale == "C") {
			maxTemp = 44 // Max Temp in C
			minTemp = 7 // Min Temp in C
			log.trace "Location is in Celsius, MaxTemp: $maxTemp, MinTemp: $minTemp"
		} else {
			maxTemp = 86 // Max temp in F
			minTemp = 30 // Max temp in F
			log.trace "Location is in Farenheit, MaxTemp: $maxTemp, MinTemp: $minTemp"
		}

		def currentLevel = device.currentValue("coolingSetpoint")
		int nextLevel = currentLevel.toInteger() + 1
   
		if( nextLevel > maxTemp){
			nextLevel = maxTemp
		}
		log.debug "Setting cool set point up to: ${nextLevel}"
		setCoolingSetpoint(nextLevel)
	}
}

def lowCoolLevel(){
 	if (mode == "off") {
		log.warn "lowCoolLevel(): this mode: $mode does not allow lowCoolLevel"
	} else {
		def locationScale = getTemperatureScale()
		def maxTemp
		def minTemp
		if (locationScale == "C") {
			maxTemp = 44 // Max Temp in C
			minTemp = 7 // Min Temp in C
			log.trace "Location is in Celsius, MaxTemp: $maxTemp, MinTemp: $minTemp"
		} else {
			maxTemp = 86 // Max temp in F
			minTemp = 30 // Max temp in F
			log.trace "Location is in Farenheit, MaxTemp: $maxTemp, MinTemp: $minTemp"
		}

		def currentLevel = device.currentValue("coolingSetpoint")
		int nextLevel = currentLevel.toInteger() - 1
    
		if( nextLevel < minTemp){
			nextLevel = minTemp
		}
		log.debug "Setting cool set point down to: ${nextLevel}"
		setCoolingSetpoint(nextLevel)
	}
}




// parse events into attributes
def parse(String description) {
	log.debug "Parse description $description"
	def map = [:]
	if (description?.startsWith("read attr -")) {
		def descMap = parseDescriptionAsMap(description)
		log.debug "Desc Map: $descMap"
		if (descMap.cluster == "0201" && descMap.attrId == "0000") {
			log.debug "TEMP"
			map.name = "temperature"
			map.value = getTemperature(descMap.value)
		} else if (descMap.cluster == "0201" && descMap.attrId == "0011") {
			log.debug "COOLING SETPOINT"
			map.name = "coolingSetpoint"
			map.value = getTemperature(descMap.value)
		} else if (descMap.cluster == "0201" && descMap.attrId == "0012") {
			log.debug "HEATING SETPOINT"
			map.name = "heatingSetpoint"
			map.value = getTemperature(descMap.value)
		} else if (descMap.cluster == "0201" && descMap.attrId == "001c") {
			log.debug "MODE"
			map.name = "thermostatMode"
			map.value = getModeMap()[descMap.value]
		} else if (descMap.cluster == "0202" && descMap.attrId == "0000") {
			log.debug "FAN MODE"
			map.name = "thermostatFanMode"
			map.value = getFanModeMap()[descMap.value]
		} else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
		    log.debug "BATTERY"
			map.name = "battery"
			map.value = getBatteryLevel(descMap.value)
		} else if (descMap.cluster == "0201" && descMap.attrId == "001e") {
		    log.debug "RUN MODE"
			map.name = "thermostatRunMode"
			//map.value = getRunModeMap()[descMap.value]
			map.value = getThermostatRunMode(descMap.value)
		} else if (descMap.cluster == "0201" && descMap.attrId == "0023") {
		    log.debug "HOLD MODE"
			map.name = "thermostatHoldMode"
			map.value = getHoldModeMap()[descMap.value]			
		} else if (descMap.cluster == "0000" && descMap.attrId == "0007") {
		    log.debug "Power Source"
			map.name = "powerSource"
			map.value = getPowerSource()[descMap.value]			
		}
	}

	def result = null
	if (map) {
		result = createEvent(map)
	}
	log.debug "Parse returned $map"
	return result
}

def parseDescriptionAsMap(description) {
	(description - "read attr - ").split(",").inject([:]) { map, param ->
		def nameAndValue = param.split(":")
		map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
}

def getModeMap() { [
	"00":"off",
    //"01":"auto",
	"03":"cool",
	"04":"heat",
	"05":"emergencyHeat"
]}

def getHoldModeMap() { [
	"00":"holdOff",
	"01":"holdOn",
]}

def getPowerSource() { [
	"01":"24VAC",
	"03":"Battery",
]}


def getFanModeMap() { [
	"04":"fanOn",
	"05":"fanAuto"
]}


def getThermostatRunMode(value) {
	if (value != null) {
	    def RunModeValue = Integer.parseInt(value, 16)
		log.debug "Thermostat RunMode: ${RunModeValue} "
	}
}


def getTemperature(value) {
	if (value != null) {
		def celsius = Integer.parseInt(value, 16) / 100
		if (getTemperatureScale() == "C") {
			return celsius
		} else {
			return Math.round(celsiusToFahrenheit(celsius))			
		}
	}
}

def setHeatingSetpoint(degrees) {
	if (degrees != null) {
		def temperatureScale = getTemperatureScale()

		def degreesInteger = Math.round(degrees)
		log.debug "setHeatingSetpoint({$degreesInteger} ${temperatureScale})"
		sendEvent("name": "heatingSetpoint", "value": degreesInteger)

		def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
		"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius * 100) + "}"
	}
}

def setCoolingSetpoint(degrees) {
	if (degrees != null) {
		def degreesInteger = Math.round(degrees)
		log.debug "setCoolingSetpoint({$degreesInteger} ${temperatureScale})"
		sendEvent("name": "coolingSetpoint", "value": degreesInteger)
		def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
		"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius * 100) + "}"
	}
}

def modes() {
	["off", "cool", "heat", "emergencyHeat"]
}

def setThermostatMode() {
	log.debug "switching thermostatMode"
	def currentMode = device.currentState("thermostatMode")?.value
	def modeOrder = modes()
    log.debug "modeOrder: ${modeOrder}"
	def index = modeOrder.indexOf(currentMode)
    log.debug "index: ${index}"
    def next = index >= 0 && index < modeOrder.size() - 1 ? modeOrder[index + 1] : modeOrder[0]
	log.debug "switching mode from $currentMode to $next"
	"$next"()
}

def setThermostatFanMode() {
	log.debug "Switching fan mode"
	def currentFanMode = device.currentState("thermostatFanMode")?.value
	log.debug "switching fan from current mode: $currentFanMode"
	def returnCommand

	switch (currentFanMode) {
		case "fanAuto":
			returnCommand = fanOn()
			break
		case "fanOn":
			returnCommand = fanAuto()
			break
	}
	if(!currentFanMode) { returnCommand = fanAuto() }
	returnCommand
}


def setThermostatHoldMode() {
	log.debug "Switching Hold mode"
	def currentHoldMode = device.currentState("thermostatHoldMode")?.value
	log.debug "switching thermostat from current mode: $currentHoldMode"
	def returnCommand

	switch (currentHoldMode) {
		case "holdOff":
			returnCommand = holdOn()
			break
		case "holdOn":
			returnCommand = holdOff()
			break
	}
	if(!currentHoldMode) { returnCommand = holdOff() }
	returnCommand
}

def setThermostatMode(String value) {
	log.debug "setThermostatMode({$value})"
	"$value"()
}

def setThermostatFanMode(String value) {
	log.debug "setThermostatFanMode({$value})"
	"$value"()
}

def setThermostatHoldMode(String value) {
	log.debug "setThermostatHoldMode({$value})"
	"$value"()
}


def off() {
	log.debug "off"
	sendEvent("name":"thermostatMode", "value":"off")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {00}"
}

def cool() {
	log.debug "cool"
	sendEvent("name":"thermostatMode", "value":"cool")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {03}"
}

def heat() {
	log.debug "heat"
	sendEvent("name":"thermostatMode", "value":"heat")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {04}"
}

def emergencyHeat() {
	log.debug "emergencyHeat"
	sendEvent("name":"thermostatMode", "value":"emergencyHeat")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x1C 0x30 {05}"
}

def on() {
	fanOn()
}

def fanOn() {
	log.debug "fanOn"
	sendEvent("name":"thermostatFanMode", "value":"fanOn")
	"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {04}"
}

def auto() {
	fanAuto()
}

def fanAuto() {
	log.debug "fanAuto"
	sendEvent("name":"thermostatFanMode", "value":"fanAuto")
	"st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {05}"
}

def holdOn() {
	log.debug "Set Hold On for thermostat"
	sendEvent("name":"thermostatHoldMode", "value":"holdOn")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x23 0x30 {01}"
}

def holdOff() {
	log.debug "Set Hold Off for thermostat"
	sendEvent("name":"thermostatHoldMode", "value":"holdOff")
	"st wattr 0x${device.deviceNetworkId} 1 0x201 0x23 0x30 {00}"
}

// Commment out below if no C-wire since it will kill the batteries.
def poll() {
//	log.debug "Executing 'poll'"
	refresh()
}


def configure() {

	log.debug "binding to Thermostat and Fan Control cluster"
	[
		"zdo bind 0x${device.deviceNetworkId} 1 1 0x000 {${device.zigbeeId}} {}", "delay 200",
		"zdo bind 0x${device.deviceNetworkId} 1 1 0x201 {${device.zigbeeId}} {}", "delay 200",
		"zdo bind 0x${device.deviceNetworkId} 1 1 0x202 {${device.zigbeeId}} {}", "delay 200",
		
		"zcl global send-me-a-report 1 0x20 0x20 3600 86400 {01}", "delay 100", //battery report request
		"send 0x${device.deviceNetworkId} 1 1", "delay 200"
	]
}


def refresh()
{
	log.debug "refresh called"
	[
		"st rattr 0x${device.deviceNetworkId} 1 0x000 0x07", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x201 0", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x201 0x11", "delay 200",
  		"st rattr 0x${device.deviceNetworkId} 1 0x201 0x12", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x201 0x1C", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x201 0x1E", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x201 0x23", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x001 0x20", "delay 200",
		"st rattr 0x${device.deviceNetworkId} 1 0x202 0"
	] + configure()
}


private getBatteryLevel(rawValue) {
	def intValue = Integer.parseInt(rawValue,16)
	def min = 2.1
    def max = 3.0
    def vBatt = intValue / 10
    return ((vBatt - min) / (max - min) * 100) as int
}


private hex(value) {
	new BigInteger(Math.round(value).toString()).toString(16)

}

1 Like