[DEPRECATED] Tado Cooling Integration

good catch, i didnt realise.

No. I copied the code using the raw tab. Very strange.

@Fuzzyligic would it be hard to change the temperture from Celsius to Fahrenheit? I f it easy could you walk me through it as I would like to learn. Thanks

this in fact would be very very easy as the tado web service reports back both Celsius and Fahrenheit. Probably what would be best would be to add a preference for which one you would like. Let me take a look later today if i have time and I will give you step by step on how I do it.

OK well i knocked up the code, however i have hit a major snag i cannot overcome unless i change completely how i do this.

the problem is that i cannot change the value of the tiles to either C or Fahrenheit as i cannot change the display value of the device type dynamically. to do this i would need to create a smartapp and install a different device type whether you choose C or F as your display value… this is too much work for me to figure out now.

but this is on my todo list as i want to write an encompassing Smartapp to install the device type and run the query, at the moment i am running far too many individual queries to Tado where i could do this all in one.

but for the moment i will explain how i work out things when logging into Tado.com, i use Fiddler 4 installed on my PC and then just log into Tado. this captures and displays the traffic sent and received from Tado

so in the original code i was running was getting the temperature display from the insideTemp Value from /mobile/1.6/GetCurrentState

but this value is in Celsius and not Fahrenheit, however the hvacstate shows the following

as you can see the InsideTemperature Field here shows both Celsius and Fahrenheit Values.

so in the original code you can see in the sendCommand function that we query a few different URL’s to get the values we need

private sendCommand(method, args = ) {
def methods = [
‘status’: [
uri: “https://my.tado.com”,
path: “/mobile/1.6/getCurrentState”,
requestContentType: “application/json”,
query: [username:settings.username, password:settings.password]
],
‘hvacStatus’: [
uri: “https://my.tado.com”,
path: “/api/v1/home/” + state.homeId + “/hvacState”,
requestContentType: “application/json”,
query: [username:settings.username, password:settings.password]
],
‘temperature’: [
uri: “https://my.tado.com”,
path: “/api/v2/homes/” + state.homeId + “/zones/1/overlay”,
requestContentType: “application/json”,
query: [username:settings.username, password:settings.password],
body: args[0]
],
‘weatherStatus’: [
uri: “https://my.tado.com”,
path: “/api/v1/home/” + state.homeId + “/weather”,
requestContentType: “application/json”,
query: [username:settings.username, password:settings.password]
]
]

def request = methods.getAt(method)

log.debug "Http Params ("+request+")"

try{
    log.debug "Executing 'sendCommand'"
    
    if (method == "status"){
        httpGet(request) { resp ->            
            parseResponse(resp)
        }
    }else if (method == "hvacStatus"){
        httpGet(request) { resp ->            
            parseHvacResponse(resp)
        }
	}else if (method == "temperature"){
        httpPut(request) { resp ->            
            parseputResponse(resp)
        }
    }else if (method == "weatherStatus"){
        log.debug "calling weatherStatus Method"
        httpGet(request) { resp ->            
            parseweatherResponse(resp)
        }
    }else{
        httpGet(request)
    }
} catch(Exception e){
    debug("___exception: " + e)
}

}

If you break down this the only things we are interested in is the Status and HVACStatus. if you look at the method evaluation you can see that we run either the parseResponse or the parseHvacResponse functions for the values from the selected methods.

so the original parseResponse where we get the Temp from shows

// Parse incoming device messages to generate events
private parseResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
def temperatureUnit = “C”
def humidityUnit = “%”
if(resp.status == 200) {
log.debug(“Executing parseResponse.successTrue”)

    def temperature = Math.round(resp.data.insideTemp)
    log.debug("Read temperature: " + temperature)
    sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit)
    log.debug("Send Temperature Event Fired")
	
	state.homeId = resp.data.homeId
    log.debug("Got HomeID Value: " + state.homeId)

    def autoOperation = resp.data.autoOperation
    if(resp.data.operation == "NO_FREEZE"){
    	autoOperation = "OFF"
    }else if(resp.data.operation == "MANUAL"){
    	autoOperation = "MANUAL"
    }
    log.debug("Read autoOperation: " + autoOperation)
    sendEvent(name: 'autoOperation', value: autoOperation)
    log.debug("Send autoOperation Event Fired")
    
}else if(resp.status == 201){
    log.debug("Something was created/updated")
}

}

the modified code removes the temperature evaluation from this function as this is only in celsius

// Parse incoming device messages to generate events
private parseResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
def temperatureUnit = “${settings.tempunit}”
def humidityUnit = “%”
if(resp.status == 200) {
log.debug(“Executing parseResponse.successTrue”)

	state.homeId = resp.data.homeId
    log.debug("Got HomeID Value: " + state.homeId)

    def autoOperation = resp.data.autoOperation
    if(resp.data.operation == "NO_FREEZE"){
    	autoOperation = "OFF"
    }else if(resp.data.operation == "MANUAL"){
    	autoOperation = "MANUAL"
    }
    log.debug("Read autoOperation: " + autoOperation)
    sendEvent(name: 'autoOperation', value: autoOperation)
    log.debug("Send autoOperation Event Fired")
    
}else if(resp.status == 201){
    log.debug("Something was created/updated")
}

}

so we know we need to get the temperature from HVACStatus as it displays in both Celsius and Fahrenheit, so the original ParseHvacResponse was

private parseHvacResponse(resp) {
log.debug("Executing parseHvacResponse: "+resp.data)
log.debug("Output status: "+resp.status)
def temperatureUnit = “C”
def humidityUnit = “%”
if(resp.status == 200) {
log.debug(“Executing parseHvacResponse.successTrue”)

    def humidity 
    if (resp.data.humidity.percentage != null){
    	humidity = resp.data.humidity.percentage
    }else{
    	humidity = "--"
    }
    log.debug("Read humidity: " + humidity)
		       
    sendEvent(name: 'humidity', value: humidity,unit: humidityUnit)
}	
def ACMode
def ACFanSpeed
def setPointTemp
if (resp.data.acSetting.power == "OFF"){
   	ACMode = "OFF"
    log.debug("Read ACMode: " + ACMode)
	ACFanSpeed = "OFF"
    log.debug("Read acFanSpeed: " + ACFanSpeed)
	setPointTemp = "0"
    log.debug("Read setPointTemp: " + setPointTemp)
}
else if (resp.data.acSetting.power == "ON"){
   	ACMode = resp.data.acSetting.mode
	log.debug("Read ACMode: " + ACMode)
	ACFanSpeed = resp.data.acSetting.fanSpeed
    log.debug("Read acFanSpeed: " + ACFanSpeed)
    if (ACMode == "DRY"){
    	setPointTemp = "--"
    }else if (ACMode == "AUTO"){
            	setPointTemp = "--"
    }else{
    	setPointTemp = Math.round(resp.data.acSetting.temperature.celsius)
    }
    log.debug("Read setPointTemp: " + setPointTemp)
}
else{
    log.debug("Executing parseHvacResponse.successFalse")
}
sendEvent(name: 'ACFanSpeed', value: ACFanSpeed)
log.debug("Send ACFanSpeed Event Fired")
sendEvent(name: 'ACMode', value: ACMode)
log.debug("Send ACMode Event Fired")
sendEvent(name: 'setPointTemp', value: setPointTemp, unit: temperatureUnit)
log.debug("Send setPointTemp Event Fired")	

}

but the updated code is

private parseHvacResponse(resp) {
log.debug("Executing parseHvacResponse: "+resp.data)
log.debug("Output status: "+resp.status)
def temperatureUnit = “F”
def temperatureUnitName = fahrenheit

def humidityUnit = "%"
if(resp.status == 200) {
	log.debug("Executing parseHvacResponse.successTrue")
    
	def temperature = Math.round(resp.data.insideTemperature."${temperatureUnitName}")
    log.debug("Read temperature: " + temperature)
    sendEvent(name: 'temperature', value: temperature, unit: temperatureUnit)
    log.debug("Send Temperature Event Fired")
	
    def humidity 
    if (resp.data.humidity.percentage != null){
    	humidity = resp.data.humidity.percentage
    }else{
    	humidity = "--"
    }
    log.debug("Read humidity: " + humidity)
		       
    sendEvent(name: 'humidity', value: humidity,unit: humidityUnit)
}	
def ACMode
def ACFanSpeed
def setPointTemp
if (resp.data.acSetting.power == "OFF"){
   	ACMode = "OFF"
    log.debug("Read ACMode: " + ACMode)
	ACFanSpeed = "OFF"
    log.debug("Read acFanSpeed: " + ACFanSpeed)
	setPointTemp = "0"
    log.debug("Read setPointTemp: " + setPointTemp)
}
else if (resp.data.acSetting.power == "ON"){
   	ACMode = resp.data.acSetting.mode
	log.debug("Read ACMode: " + ACMode)
	ACFanSpeed = resp.data.acSetting.fanSpeed
    log.debug("Read acFanSpeed: " + ACFanSpeed)
    if (ACMode == "DRY"){
    	setPointTemp = "--"
    }else if (ACMode == "AUTO"){
            	setPointTemp = "--"
    }else{
    	setPointTemp = Math.round(resp.data.acSetting.temperature."${temperatureUnitName}")
    }
    log.debug("Read setPointTemp: " + setPointTemp)
}
else{
    log.debug("Executing parseHvacResponse.successFalse")
}
sendEvent(name: 'ACFanSpeed', value: ACFanSpeed)
log.debug("Send ACFanSpeed Event Fired")
sendEvent(name: 'ACMode', value: ACMode)
log.debug("Send ACMode Event Fired")
sendEvent(name: 'setPointTemp', value: setPointTemp, unit: temperatureUnit)
log.debug("Send setPointTemp Event Fired")

}

take note on how we are getting the temperature value and substituting the Json resp path to the value.

this is the main point of how we get the data, the remainder of the changes revolve around changing where is says C or Celsius to be F & Fahrenheit respectively…

the full code of the changes is below , word of warning though, i have not tested this at all due to time constraints, please try and let me know the results, alternatively if there is any debugging needed please attempt and let me know if you get stuck. I hope this helps?

https://github.com/fuzzysb/SmartThings/blob/master/DeviceTypes/fuzzysb/tado-Cooling-AC-F.src/tado-cooling-ac-f.groovy

@Fuzzyligic Hey i just want to thank you for explaining the changes to me and the effort you put forward which is alot more that I expected. I will try it this weekend when I get home. Thanks I will have to buy you a pint or so at your favorite pub. Thanks

Hi,

Just wanted to thank you for the tado support working great … wanted to ask a question…

I am using smart tiles and noticed the temperature seems to never check automatically I need to launch “things” and manually push refresh to get the temperature updated have I missed a step ?

thankd

you haven’t missed anything, the device type doesn’t schedule an update yet, just use pollster to the updates for the device.

1 Like

I had tado for a while but out in the drawer waiting for St integration. Currently using sensibo which works ok but it’s unstable as the distance to the pod is a bit too far for zigbee.

How is this device type working on st ? Anyone using it with thermostat director in smartthings? Would you recommend it ? Hopefully tado is stable. As its wifi should be.

Tado for me is stable, I don’t have issues with these device types but as for using with thermostat director I don’t know. I still use the Tado schedules etc. And just use these to override the behaviour when needed. I think that is a safer option with ST’s unreliability, however the device types do conform to the thermostat capability if you decided to remove all schedules from Tado and use ST for the scheduling I am sure it should just work.

I have in the pipeline a Tado smartapp to tie all the products together, however I am still trying to hack through their oAuth procedure as Tado were unwilling to assist me

Thanks for that. Sucks they are not willing to help. Wonder why, resources perhaps.

I’ll give it a try soon and see how it goes with thermostat director !

@Fuzzyligic after copy the taco-cooling-ac.groovy to Device handler type I’m SmartThing IDE and save/published for me, then ? what is next step ?
I can not see the device show in my SmartThings ?, Is that because i am using the iPhone, and this source code currently working for Android device ?, Please advise, If you can have detail install instruction, that will be great ? By the way, There is no SmartApp to control this device ?, Only Device Type need create ?

once the device type has been created you then need to click on your My Devices tab in the IDE and then choose add new device. then you add your device and name it what you want and then choose the Tado Thermostat Device Handler from the Type Menu as shown

then click create then all you need to do is find the device in the SmartThings app and look at the device properties, and enter your Tado username and password once done all you need to do is hit refresh and the device should update.

these releases don’t schedule the refresh too well, so it would be best to add the device into the pollster smartapp to refresh the status every 5 minutes.

I am working on updated device handlers and smartapps but time has been short recently and ive been struggling with the motivation, but i will have something ready i imagine in the next few weeks

just replied via PM, please read and action accordingly

@Fuzzyligic, Thanks for your remind, I have change the password for safety issue. but I have check the source I copy are exactly same as you mention, and It still same message as I face, even I create the device again…see below message, Error in line 393, What is that mean. I am using the iPhone 6s+ , iOS 9.0.2, and SmartThing and Tado Apps are all latest version.

1503f0b1-9349-48cf-b36d-c90933bc6d14 下午10:10:24: error groovy.lang.MissingMethodException: No signature of method: script14597788066271803256152.debug() is applicable for argument types: (java.lang.String) values: [___exception: groovyx.net.http.HttpResponseException: Not Found]
Possible solutions: input(java.lang.String), getAt(java.lang.String), getLog(), dry(), run(), dump() @ line 393
1503f0b1-9349-48cf-b36d-c90933bc6d14 下午10:10:23: debug Executing 'sendCommand’
1503f0b1-9349-48cf-b36d-c90933bc6d14 下午10:10:23: debug Http Params ([uri:https://my.tado.com, path:/api/v2/homes/null/zones/1/overlay, requestContentType:application/json, query:[username:mitXXXXXX@gXXX.com, password:xxxxxxxxxx], body:{“setting”:{“type”:“AIR_CONDITIONING”,“power”:“OFF”},“termination”:{“type”:“TADO_MODE”}}])
1503f0b1-9349-48cf-b36d-c90933bc6d14 下午10:10:23: debug Executing 'sendCommand.offCommand’
1503f0b1-9349-48cf-b36d-c90933bc6d14 下午10:10:23: debug Executing ‘off’

@Fuzzyligic. It work with your newest code, However, I notice that we only can read Temperature and Humidity from the device, but we can not set the temp, Turn on of Turn off the AC, either in change mode to AC, Fan, Dry,Do you plan to add those function in the future ?

I am able to turn on and off the AC using the on and off buttons and set the temps for the heating and cooling modes, however you cannot set the temps in Fan, Dry or Auto Modes in the Tado App either this is a limitation of those modes and the API does not accept these commands. ideally i would like to hide the setpoint tiles if you are in the heat and dry modes, but ST currently does not support this.

i do have further improvements planned but i am still limited by Tado’s API

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

You might want to edit this post to remove your user Id and password

ok it seems when you are sending the commands you are getting the exception: groovyx.net.http.HttpResponseException: Unprocessable Entity

Looking at this it seems the issue is that your device is different to mine as the fanspeed does not support AUTO mode so if you search your device handler and replace

fanSpeed:"AUTO"
with
fanSpeed:“HIGH”

the on and off and temp setpoints should begin to work

The problem with this whole endevour is that there is no documented API so everything i am doing is by trial and error, but yours is a good example where there is marked differences between the locations