Honeywell TCC Total Connect Comfort Drivers

suddenly get cert error in the app.. the website direct login still works.. any ideas

dev:2912020-12-07 06:48:57.306 pm warnSomething went wrong: javax.net.ssl.SSLPeerUnverifiedException: Host name 'www.mytotalconnectcomfort.com' does not match the certificate subject provided by the peer (CN=mytotalconnectcomfort.com, O="Resideo Technologies, Inc", L=Golden Valley, ST=Minnesota, C=US)

dev:2912020-12-07 06:48:55.545 pm warnSomething went wrong during login: javax.net.ssl.SSLPeerUnverifiedException: Host name 'www.mytotalconnectcomfort.com' does not match the certificate subject provided by the peer (CN=mytotalconnectcomfort.com, O="Resideo Technologies, Inc", L=Golden Valley, ST=Minnesota, C=US)

i fixed it change the line that hat tccsite and removed the www.

public static String tccSite() { return "mytotalconnectcomfort.com" }

LOL, don't look at me! I can't code. LOL

But I do see the same error. @csteele any ideas? Possibly something in the API changed?

Double ninja edit to add, their site works fine from a web browser and the phone app.

Appears the solution is to remove the www from line 70, so update to:

public static String tccSite() { return "mytotalconnectcomfort.com" }

1 Like

That seems to have to fixed it. Wonder what else they will break to kick us out?

ahh thats what is said and posted.

1 Like

Has anyone in Europe (UK) got TC working ?

Post the error that u are getting.

So, new owner here. Thanks for this work. I got it up and running without a hitch. Even added the external temp and humidity as apps too.

But, the name of the tile is always [current indoor temp] and unknown. Clearly not reading the humidity for some reason. I see that seems to be an issue with the firmware.

Maybe split out the tiles one for temp and one for humidity? Using the temperature and humidity tiles instead of the multi-sensor tile.

Does it read correctly from the thermostat's device settings page? Usually in the upper right corner of that page.

I put the external temperature and humidity readings from the thermostat in separate tiles. They work fine. It reads the modes (e.g., Fan auto, heat on) and the setting fine. The current internal temperature is the header of the tile (where it might say "switch" for a generic switch). But the humidity doesn't read on tile. It reads on the thermostat. I used the "Thermostat" default template. Is there a better one to use here?

EDIT: So, playing around, if I set up "Weather" template to my thermostat, it picks up the internal temp and humidity. So, clearly it's reading it. The template just doesn't like it.

I used the humidity template to get humidity on one of my dashboards.

new features for my steam humidifer

ok its the nastiest code i've written but it works.. this is my version in my github not the latest..
does query to api comes back in raw html, parses and gets status and settings for whole house humidifier setup through total comfort.

summary of changes necessary:

   attribute "humidifierStatus", "string"
        attribute "humidifierSetPoint", "number"
        attribute "humidifierUpperLimit", "number"
        attribute "humidifierLowerLimit", "number"

     input name: "haveHumidifier", type: "enum", title: "Do you have an option whole house humidifier and want to enable it?", options: ["Yes", "No"], required: true, defaultValue: "No"
  
def refresh() {
    device.data.unit = "°${location.temperatureScale}"
    if (debugOutput) log.debug "here Honeywell TCC 'refresh', pollInterval: $pollInterval, units: = $device.data.unit"
    login()
    getHumidifierStatus()
    getStatus()
}


 
def getHumidifierStatus()
{   
   if (debugOutput)  log.debug "in get humid status enable humidity = $enableHumidity"
	if (haveHumidifier == 'No') return
	def params = [
        uri: "https://${tccSite()}/portal/Device/Menu/${settings.honeywelldevice}",
        headers: [
            'Accept': '*/*', // */ comment
            'DNT': '1',
            'dataType': 'json',
            'cache': 'false',
            'Accept-Encoding': 'plain',
            'Cache-Control': 'max-age=0',
            'Accept-Language': 'en-US,en,q=0.8',
            'Connection': 'keep-alive',
            'Host': 'rs.alarmnet.com',
            'Referer': 'https://${tccSite()}/portal/',
            'X-Requested-With': 'XMLHttpRequest',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36',
            'Cookie': device.data.cookiess
        ],
	  timeout: 10
    ]

    if (debugOutput) log.debug "sending gethumidStatus request: $params"

    def HumStatusLine = [:]  
    def CancelLine = [:]
    def Number HumLevel
    def Number HumMin
    def Number HumMax
    def HumStatus = [:]
    try {
     httpGet(params) { response ->
        if (debugOutput) log.debug "GetHumidity Request was successful, $response.status"
        if (debugOutput) log.debug "response = $response.data"
         
         def data = response.getData().toString()
         data.split("\n").each {
        
          //if (debugOutput) log.debug "working on \"${it}\""
        if (it.contains("CancelMin")) {
            CancelLine = it.trim()
          def pair = CancelLine.split(" ");
             if (debugOutput)   log.debug "got cancel min line: $CancelLine"
           // log.debug "pair = $pair"
             def p0 = pair[0]
             def p1 = pair[1]
             def p2 = pair[2]
  
           // log.debug "p0 = $p0"
           // log.debug "p1 = $p1"
           // log.debug "p2 = $p2"
            
            def pair2 = p1.split("%")
            //log.debug "pair2 = $pair2"
            def p20 = pair2[0]
            def p21 = pair2[1]
            def p22 = pair2[2]
            
            //log.debug "p20 = $p20"
            // log.debug "p21 = $p21"
            //log.debug "p22 = $p22"
                   
            HumLevel = p21.toInteger()
            HumMin = p20.toInteger()
            
           def pair3 = p2.split("%")
           //log.debug "pair3 = $pair3"
            def p30 = pair3[0]
           // log.debug "p30 = $p30"
          
            HumMax = p30.toInteger() 
            
            log.debug "-----------------------"
            log.debug "Got current humidifier level = $HumLevel"
            log.debug "Got Current humidifier Min = $HumMin"
            log.debug "Got Current humidifier Max= $HumMax"
        }
             
          if (it.contains("Humidifier operates")) {
            HumStatusLine = it.trim()
            if (debugOutput)  log.debug "Got Humidifier Status Line = $HumStatusLine"
            def start = HumStatusLine.indexOf("Humidifier is ")
            
              if (debugOutput) log.debug "got Start = $start"
              if ( (start) && (start > 0))
              {
               HumStatus = HumStatusLine.substring(start+14,start+17) 
               log.debug "got HumStatus String = $HumStatus" 
               log.debug "-----------------------"   
              }
        }     
      }
         
     	//Send events 
		sendEvent(name: 'humidifierStatus', value: HumStatus)
		sendEvent(name: 'humidifierSetPoint', value: HumLevel as Integer, unit:"%")
		sendEvent(name: 'humidifierUpperLimit', value: HumMax as Integer, unit:"%")
		sendEvent(name: 'humidifierLowerLimit', value: HumMin as Integer, unit:"%") 

    }
    } 
    catch (e) {
    	log.error "Something went wrong: $e"
    }

}

whole file in github:

So this will work with all humidifiers I wonder? Does it emulate the humidity settings in TCC?

So would that in turn mean the dehumidify settings also might be able to be added?

Cool work, hope that it can be added to the main code at some point. I hope you put in a pull request!

scratch that it is working .. Their was a typo (case in a variable name)

new version but i had to use the other parameters as mentioned as the off, or auto in the humid page of the website is just whether or not it is disabled not running..
For humidifyer running i now
check

  1. the fan mode is in auto
    2, the heat/cool is not on
  2. you enable the humidifier in the pulldown
    and finally
  3. the fan is actually on..

obviously this wont work if it is on at the same time as the heat.

But even in the mobile app or website it doesnt actually say the humidifier is on.. This is obviously and oversight.. It does show it under the humidifyer menu on the therm though so it knows it.

Hi all

I am using Webcore to control my Honeywell thermostats that are using version 1.3.12 of the Total Comfort API C driver. When I have Webcore issue a simple command to set the thermostat mode to Heat (changing from Off), it works but it only sets a Temporary Hold when I want it to set a Permanent Hold. Webcore itself doesn't give me any option to set the Hold type. Any ideas how I can have any changes issued by Webcore be set to Permanent Hold please?

Thanks.

What is this set to?

Screen Shot 2021-01-01 at 10.31.06 AM

Temporary. :flushed:

That fixed it, thanks.

1 Like

due to the flakeyness of the honeywell web site i have made changes to my own version of the driver. that when it gets and read timeout or unauthorized error, schedule a refresh automatically 5 minutes out.. it seems to be working fine for me... Let me know if anyone wants to get a copy of it or if you want the code to integrate into the stock version it looks something like this
also here is the fx to get the steam humidifier settings.

} catch (e) {
log.warn "Something went wrong (getstatus): $e"

def String eStr = e.toString()
def pair = eStr.split(" ")
def p1 = pair[0]
def p2 = pair[1]

   if ((p2 == "Unauthorized") || (p2 == "Read"))
    {
        log.debug "in something went wrong"
        if (fromUnauth)
        {
          log.debug "2nd Unauthorized failure ... giving up!"
        }
        else
        {
          log.debug "Scheduling a retry in 5 minutes due to Unauthorized!"
          runIn(300,"refreshFromRunin")
        }
    }
}

}

...

} catch (e) {
log.warn "Something went wrong during login: $e"

def String eStr = e.toString()
def pair = eStr.split(" ")
def p1 = pair[0]
def p2 = pair[1]
    
   if ((p2 == "Unauthorized") || (p2 == "Read"))
    {
        if (fromUnauth)
        {
          log.debug "2nd Unauthorized failure ... giving up!"
        }
        else
        {
          log.debug "Scheduling a retry in 5 minutes due to Unauthorized!"
          runIn(300,"refreshFromRunin")
        }
    } 
}

}
...

def refreshFromRunin()
{
log.debug "Calling refresh after Unauthorize failure!"
refresh(true)
}

def refresh(Boolean fromUnauth = false) {
log.debug "Executing 'refresh'"
def unit = location.temperatureScale
logDebug "pollInterval: $pollInterval, units: = $unit"
login(fromUnauth)
getHumidifierStatus(fromUnauth)
getStatus(fromUnauth)
}

def getHumidifierStatus(Boolean fromUnauth = false)
{
//'Referer': 'https://mytotalconnectcomfort.com/portal/Menu/${settings.honeywelldevice}',

log.debug "in get humid status!"
def params = [
    uri: "https://mytotalconnectcomfort.com/portal/Device/Menu/GetHumData/${settings.honeywelldevice}",
    headers: [
        'Accept': '*/*',
        'DNT': '1',
        'dataType': 'json',
        'cache': 'false',
        'Accept-Encoding': 'plain',
        'Cache-Control': 'max-age=0',
        'Accept-Language': 'en-US,en,q=0.8',
        'Connection': 'keep-alive',
        'Host': 'rs.alarmnet.com',
        'Referer': 'https://mytotalconnectcomfort.com/portal',
        'X-Requested-With': 'XMLHttpRequest',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36',
        'Cookie': device.data.cookiess        ],
]

  log.debug "sending getHumiditys request"

log.debug "url = $params"

try {
 httpGet(params) { response ->
    log.debug "GetHumidity Request was successful, $response.status"
    log.debug "Humidifier data response = $response.data"

      logDebug "ld = $response.data.latestData"
      logDebug "humdata = $response.data.latestData.humData"

    logInfo("lowerLimit: ${response.data.latestData.humData.lowerLimit}")        
    logInfo("upperLimit: ${response.data.humData.upperLimit}")        
    logInfo("SetPoint: ${response.data.humData.Setpoint}")        
    logInfo("DeviceId: ${response.data.humData.DeviceId}")        
    logInfo("IndoorHumidity: ${response.data.humData.IndoorHumidity}")        

}
} 
catch (e) {
	log.error "Something went wrong (gethumidstatus): $e"

def String eStr = e.toString()
def pair = eStr.split(" ")
def p1 = pair[0]
def p2 = pair[1]

   if ((p2 == "Unauthorized") || (p2 == "Read"))
    {
      if (fromUnauth)
        {
          log.debug "2nd Unauthorized failure ... giving up!"
        }
        else
        {
          log.debug "Scheduling a retry in 5 minutes due to Unauthorized!"
          runIn(300,"refreshFromRunin")
        }
    }
}

}

I use this driver and would be interested to test your modifications. More reliability & determinism is always a good thing.