Custom Device Drivers [Wiki]

I got one last week and paired fine... as a device I believe. Tried the Aeon Outlet, didn't work. I also have the Dome Smart Switch and so I tried that DH on the Aeon... and it's working!

It's on my washing machine to notify us of "Done". The Dome is on the dryer.

Which model? Aeon and Aeotec are the same company so not sure which one you mean.

Is Dome rated for the dryer? I would be careful with that one. I know Fibaro is rated for high Amp devices as is the Zooz Heavy Duty appliance switch but not sure about Dome. The dome I know about is for small appliances and rated for 13Amp, 0.13W
https://www.thesmartesthouse.com/collections/smart-switches-and-plugs/products/dome-z-wave-plus-miniature-on-off-plug-dmof1

Gas Dryer.

energy : 1.80
energyCost : 0.38
energyDuration : 11.18 Days
power : 0.00
powerH : 11.03
powerL : 0.00
status : on
switch : on

OK never mind. I assumed Electric. I forget about Gas since moving from NY to Hawaii because they don't use much gas here.

Aeon Outlet device driver. One of Hubitat's internal drivers. Didn't work for MY needs. Probably worked as intended.

zooZ Motion is in the list, but the ZSE02 doesn't work and is listed in the incompatible thread as well ( I found that thread too late)

Rats! Be sure to use search when checking device compatibility. If I type just ZSE02, both compatible and incompatible lists are the first and second results.

Hi @mike.maxwell,

Does Hubitat support Honeywell Wifi thermostat integration?

We do not have a built in driver for that device right now.

Try porting?

I took a few to start the porting process and this driver does switch my Honeywell WiFi Thermostat between cool and off and back to cool. It takes at least 5 seconds Round Trip Time. But that was all the testing time I had this evening. :frowning:

> /**
> *  Total Comfort API
> *   
> *  Based on Code by Eric Thomas, Edited by Bob Jase
> *
> *  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
> *   lgk v 3 added optional outdoor temp sensors and preferences for it, also made api login required.
> *  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.
> * lgk version 4 supports celsius and fahrenheit with option, and now colors.
> * lgk version 5, due to intermittant update failures added last update date/time tile so that you can see when it happended
> * not there is a new input tzoffset which defaults to my time ie -5 which you must set .
> * lgk version 6 add support for actually knowing the fan is on or not (added tile),
> * and also the actual operating state ie heating,cooling or idle via new response variables.
> * lgk version 7, change the new operating state to be a value vs standard tile
> * to work around a bug smartthings caused in the latest 2.08 release with text wrapping.
> * related also added icons to the operating state, and increase the width of the last update
> * to avoid wrapping.
> 
> * lgk version 8 figured out how to do time without user input of time zone offset.. and this works with and without
> * daylight saving time.
> *
> * lgk version 9 an indicator on the bottom which indicates if following schedule or vacation or temp hold.
> * it also displays green for following schedule and red for other modes. 
> * you can also select it to cancel a hold and go back to following schedule.
> * however, the temp will not update till next refresh even though you have cancelled the hold.
> * One problem, is that the colors are not working correctly in ios currently and the label is not 
> * wrapping in  android as it is supposed to.
> *
> * (Bob) version 10 deals with the fact that Honeywell decided to poison the well with expired cookies
> * I also changed it so that it polls for an update every 60 seconds
> */
> preferences {
>     input("username", "text", title: "Username", description: "Your Total Comfort User Name", required: true)
>     input("password", "password", title: "Password", description: "Your Total Comfort password",required: true)
>     input("honeywelldevice", "text", title: "Device ID", description: "Your Device ID", required: true)
>     input ("enableOutdoorTemps", "enum", title: "Do you have the optional outdoor temperature sensor and want to enable it?", options: ["Yes", "No"], required: false, defaultValue: "No")
>     input ("tempScale", "enum", title: "Fahrenheit or Celsius?", options: ["F", "C"], required: true)
>     //input("tzOffset", "number", title: "Time zone offset +/-xx?", required: false, defaultValue: -5, description: "Time Zone Offset ie -5.")  
> }
> 
> metadata {
>     definition (name: "Total Comfort API B", namespace: 
>                 "Total Comfort API", author: "Eric Thomas, modified lg kahn") {
>         capability "Polling"
>         capability "Thermostat"
>         capability "Refresh"
>         capability "Temperature Measurement"
>         capability "Sensor"
>         capability "Relative Humidity Measurement"    
>         command "heatLevelUp"
>         command "heatLevelDown"
>         command "coolLevelUp"
>         command "coolLevelDown"
>         command "setFollowSchedule"
>         attribute "outdoorHumidity", "number"
>         attribute "outdoorTemperature", "number"
>         attribute "lastUpdate", "string"
>         attribute "followSchedule", "string"
>     }
> 
>         main "temperature"
>         details(["temperature", "thermostatMode", "thermostatFanMode",   
>                  "heatLevelUp", "heatingSetpoint" , "heatLevelDown", "coolLevelUp", 
>                  "coolingSetpoint", "coolLevelDown" ,"thermostatOperatingState","fanOperatingState",
>                  "refresh","relativeHumidity","outdoorTemperature","outdoorHumidity", "followSchedule","status"])
> 
> }
> 
> def coolLevelUp()
> {
>     state.DisplayUnits = settings.tempScale
>     if (state.DisplayUnits == "F")
>     {
>         int nextLevel = device.currentValue("coolingSetpoint") + 1
> 
>         if( nextLevel > 99){
>             nextLevel = 99
>         }
>         log.debug "Setting cool set point up to: ${nextLevel}"
>         setCoolingSetpoint(nextLevel)
>     }
>     else
>     {
>         int nextLevel = device.currentValue("coolingSetpoint") + 0.5
> 
>         if( nextLevel > 37){
>             nextLevel = 37
>         }
>         log.debug "Setting cool set point up to: ${nextLevel}"
>         setCoolingSetpoint(nextLevel)
> 
>     }
> }
> 
> def coolLevelDown()
> {
>     state.DisplayUnits = settings.tempScale
>     if (state.DisplayUnits == "F")
>     {
>         int nextLevel = (device.currentValue("coolingSetpoint") - 1)
> 
>         if( nextLevel < 50){
>             nextLevel = 50
>         }
>         log.debug "Setting cool set point down to: ${nextLevel}"
>         setCoolingSetpoint(nextLevel)
>     }
> 
>     else
> 
>     {
>         double nextLevel = device.currentValue("coolingSetpoint") - 0.5
> 
>         if( nextLevel < 10){
>             nextLevel = 10
>         }
>         log.debug "Setting cool set point down to: ${nextLevel}"
>         setCoolingSetpoint(nextLevel)
> 
>     }
> }
> 
> def heatLevelUp()
> {
>     state.DisplayUnits = settings.tempScale
>     if (state.DisplayUnits == "F")
>     {
>         log.debug "in fahrenheit level up"
>         int nextLevel = device.currentValue("heatingSetpoint") + 1
> 
>         if( nextLevel > 90){
>             nextLevel = 90
>         }
>         log.debug "Setting heat set point up to: ${nextLevel}"
>         setHeatingSetpoint(nextLevel)
>     }
> 
>     else
>     {
> 
>         log.debug "in celsius level uo"
>         double nextLevel = device.currentValue("heatingSetpoint") + 0.5
> 
>         if( nextLevel > 33){
>             nextLevel = 33
>         }
>         log.debug "Setting heat set point up to: ${nextLevel}"
>         setHeatingSetpoint(nextLevel)
>     }
> 
> }
> 
> 
> 
> def heatLevelDown()
> {
>     state.DisplayUnits = settings.tempScale
>     if (state.DisplayUnits == "F")
>     {
>         log.debug "in fahrenheit level down"
>         int nextLevel = device.currentValue("heatingSetpoint") - 1
> 
>         if( nextLevel < 40){
>             nextLevel = 40
>         }
>         log.debug "Setting heat set point down to: ${nextLevel}"
>         setHeatingSetpoint(nextLevel)
>     }
> 
>     else
>     {
> 
>         log.debug "in celsius level down"
>         double nextLevel = device.currentValue("heatingSetpoint") - 0.5
> 
>         if( nextLevel < 4){
>             nextLevel = 4
>         }
>         log.debug "Setting heat set point down to: ${nextLevel}"
>         setHeatingSetpoint(nextLevel)
>     }
> 
> }
> 
> 
> 
> // parse events into attributes
> def parse(String description) {
> 
> }
> 
> // handle commands
> 
> def setHeatingSetpoint(Double temp)
> {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = temp
>     device.data.CoolSetpoint = 'null'
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'heatingSetpoint', value: temp as double)
> 
>     }	
> }
> 
> def setHeatingSetpoint(temp) {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = temp
>     device.data.CoolSetpoint = 'null'
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'heatingSetpoint', value: temp as Integer)
> 
>     }
> 
> }
> 
> 
> def setFollowSchedule() {
>     log.debug "in set follow schedule"
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = 'null'
>     device.data.CoolSetpoint = 'null'
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='0'
>     device.data.StatusCool='0'
>     device.data.FanMode = 'null'
>     setStatus()
> 
>     if(device.data.SetStatus==1)
>     {
>         log.debug "Successfully sent follow schedule.!"
>         runIn(60,"getStatus")
>     }
> 
> }
> 
> 
> def setCoolingSetpoint(double temp) {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = 'null'
>     device.data.CoolSetpoint = temp
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'coolingSetpoint', value: temp as double)
> 
>     }
> }
> 
> def setCoolingSetpoint(temp) {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = 'null'
>     device.data.CoolSetpoint = temp
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'coolingSetpoint', value: temp as Integer)
> 
>     }
> }
> 
> def setTargetTemp(temp) {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = temp
>     device.data.CoolSetpoint = temp
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> }
> 
> 
> 
> def setTargetTemp(double temp) {
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = temp
>     device.data.CoolSetpoint = temp
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='1'
>     device.data.StatusCool='1'
>     device.data.FanMode = 'null'
>     setStatus()
> }
> 
> 
> def off() {
>     setThermostatMode(2)
> }
> 
> def auto() {
>     setThermostatMode(4)
> }
> 
> def heat() {
>     setThermostatMode(1)
> }
> 
> def emergencyHeat() {
> 
> }
> 
> def cool() {
>     setThermostatMode(3)
> }
> 
> def setThermostatMode(mode) {
>     device.data.SystemSwitch = mode 
>     device.data.HeatSetpoint = 'null'
>     device.data.CoolSetpoint = 'null'
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat=1
>     device.data.StatusCool=1
>     device.data.FanMode = 'null'
> 
>     setStatus()
> 
>     def switchPos
> 
>     if(mode==1)
>     switchPos = 'heat'
>     if(mode==2)
>     switchPos = 'off'
>     if(mode==3)
>     switchPos = 'cool'
>     /* lgk modified my therm has pos 5 for auto vision pro */
>     if(mode==4 || swithPos == 5)
>     switchPos = 'auto'
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'thermostatMode', value: switchPos)
>     }
> 
> }
> 
> def fanOn() {
>     setThermostatFanMode(1)
> }
> 
> def fanAuto() {
>     setThermostatFanMode(0)
> }
> 
> def fanCirculate() {
>     setThermostatFanMode(2)
> }
> 
> 
> def setThermostatFanMode(mode) {    
> 
>     device.data.SystemSwitch = 'null' 
>     device.data.HeatSetpoint = 'null'
>     device.data.CoolSetpoint = 'null'
>     device.data.HeatNextPeriod = 'null'
>     device.data.CoolNextPeriod = 'null'
>     device.data.StatusHeat='null'
>     device.data.StatusCool='null'
>     device.data.FanMode = mode
> 
>     setStatus()
> 
>     def fanMode
> 
>     if(mode==0)
>     fanMode = 'auto'
>     if(mode==1)
>     fanMode = 'on'
>     if(mode==2)
>     fanMode = 'circulate'
> 
>     if(device.data.SetStatus==1)
>     {
>         sendEvent(name: 'thermostatFanMode', value: fanMode)    
>     }
> 
> }
> 
> 
> def poll() {
>     refresh()
>     runIn(60, poll) // refresh status every 60 seconds
> }
> 
> 
> def setStatus() {
> 
>     device.data.SetStatus = 0
> 
>     login()
>     log.debug "Executing 'setStatus'"
>     def today= new Date()
>     log.debug "https://www.mytotalconnectcomfort.com/portal/Device/SubmitControlScreenChanges"
>     log.debug "setting heat setpoint to $device.data.HeatSetpoint"
>     log.debug "setting cool setpoint to $device.data.CoolSetpoint"
> 
>     def params = [
>         uri: "https://www.mytotalconnectcomfort.com/portal/Device/SubmitControlScreenChanges",
>         headers: [
>             'Accept': 'application/json, text/javascript, */*; q=0.01',
>             'DNT': '1',
>             'Accept-Encoding': 'gzip,deflate,sdch',
>             'Cache-Control': 'max-age=0',
>             'Accept-Language': 'en-US,en,q=0.8',
>             'Connection': 'keep-alive',
>             'Host': 'mytotalconnectcomfort.com',
>             'Referer': "https://www.mytotalconnectcomfort.com/portal/Device/Control/${settings.honeywelldevice}",
>             '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        ],
>         body: [ DeviceID: "${settings.honeywelldevice}", SystemSwitch : device.data.SystemSwitch ,HeatSetpoint : 
>                device.data.HeatSetpoint, CoolSetpoint: device.data.CoolSetpoint, HeatNextPeriod: 
>                device.data.HeatNextPeriod,CoolNextPeriod:device.data.CoolNextPeriod,StatusHeat:device.data.StatusHeat,
>                StatusCool:device.data.StatusCool,FanMode:device.data.FanMode,ThermostatUnits: settings.tempScale]
> 
>     ]
> 
>     log.debug "params = $params"
>     httpPost(params) { response ->
>         log.debug "Request was successful, $response.status"
> 
>     }
> 
>     log.debug "SetStatus is 1 now"
>     device.data.SetStatus = 1
> 
> }
> 
> def getStatus() {
>     log.debug "Executing getStatus"
>     log.debug "enable outside temps = $enableOutdoorTemps"
>     def today= new Date()
>     log.debug "https://www.mytotalconnectcomfort.com/portal/Device/CheckDataSession/${settings.honeywelldevice}?_=$today.time"
> 
>     def params = [
>         uri: "https://www.mytotalconnectcomfort.com/portal/Device/CheckDataSession/${settings.honeywelldevice}",
>         headers: [
>             'Accept': '*/*',
>             'DNT': '1',
>             'Cache' : 'false',
>             'dataType': 'json',
>             'Accept-Encoding': 'plain',
>             'Cache-Control': 'max-age=0',
>             'Accept-Language': 'en-US,en,q=0.8',
>             'Connection': 'keep-alive',
>             'Referer': 'https://www.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 "doing request"
> 
>     httpGet(params) { response ->
>         log.debug "Request was successful, $response.status"
>         //log.debug "data = $response.data"
>         log.debug "ld = $response.data.latestData"
> 
>         def curTemp = response.data.latestData.uiData.DispTemperature
>         def fanMode = response.data.latestData.fanData.fanMode
>         def switchPos = response.data.latestData.uiData.SystemSwitchPosition
>         def coolSetPoint = response.data.latestData.uiData.CoolSetpoint
>         def heatSetPoint = response.data.latestData.uiData.HeatSetpoint
>         def statusCool = response.data.latestData.uiData.StatusCool
>         def statusHeat = response.data.latestData.uiData.StatusHeat
>         def curHumidity = response.data.latestData.uiData.IndoorHumidity
>         def Boolean hasOutdoorHumid = response.data.latestData.uiData.OutdoorHumidityAvailable
>         def Boolean hasOutdoorTemp = response.data.latestData.uiData.OutdoorTemperatureAvailable
>         def curOutdoorHumidity = response.data.latestData.uiData.OutdoorHumidity
>         def curOutdoorTemp = response.data.latestData.uiData.OutdoorTemperature
>         def displayUnits = response.data.latestData.uiData.DisplayUnits
>         def fanIsRunning = response.data.latestData.fanData.fanIsRunning
>         def equipmentStatus = response.data.latestData.uiData.EquipmentOutputStatus
> 
>         def holdTime = response.data.latestData.uiData.TemporaryHoldUntilTime
>         def vacationHold = response.data.latestData.uiData.IsInVacationHoldMode
> 
>         log.debug "got holdTime = $holdTime"
>         log.debug "got Vacation Hold = $vacationHold"
> 
>         if (holdTime != 0) 
>         {  log.debug "sending temporary hold"
>          sendEvent(name: 'followSchedule', value: "TemporaryHold")
>         }
> 
>         if (vacationHold == true)
>         { log.debug "sending vacation hold"
>          sendEvent(name: 'followSchedule', value: "VacationHold")
>         }
> 
>         if (vacationHold == false && holdTime == 0)
>         {
>             log.debug "Sending following schedule"
>             sendEvent(name: 'followSchedule', value: "FollowingSchedule")
>         }
> 
>         /*ld = [fanData:[fanModeCirculateAllowed:true, fanModeAutoAllowed:true, fanModeFollowScheduleAllowed:false, 
> fanIsRunning:false, fanModeOnAllowed:true, fanMode:0], 
> drData:[Load:127.5, HeatSetpLimit:0,
> OptOutable:false, DeltaHeatSP:-0.01, CoolSetpLimit:0, Phase:-1, DeltaCoolSP:-0.01], 
> uiData:[OutdoorTemperature:128.0000, TemporaryHoldUntilTime:0, ScheduleHeatSp:67.0000, 
> DeviceID:453824, DispTemperatureAvailable:true, VacationHold:0, VacationHoldUntilTime:0,
> CoolSetpoint:76.0000, ScheduleCoolSp:76.0000, SwitchHeatAllowed:true, CoolNextPeriod:67, 
> IndoorHumidity:31.0000, SwitchAutoAllowed:true, SetpointChangeAllowed:true, HeatLowerSetptLimit:40.0000,
> OutdoorHumidStatus:128, SwitchOffAllowed:true, OutdoorHumidityAvailable:false,
> StatusCool:0, OutdoorTemperatureAvailable:false, EquipmentOutputStatus:0, StatusHeat:0, 
> CurrentSetpointStatus:0, HoldUntilCapable:true, CoolUpperSetptLimit:99.0000, SwitchCoolAllowed:true, 
> OutdoorHumidity:128.0000, DualSetpointStatus:false, SwitchEmergencyHeatAllowed:false, Commercial:false, 
> CoolLowerSetptLimit:50.0000, OutdoorHumiditySensorNotFault:true, IndoorHumiditySensorAvailable:true,
> ScheduleCapable:true, DisplayUnits:F, DispTemperature:70.0000, Deadband:3.0000, HeatUpperSetptLimit:90.0000, 
> IsInVacationHoldMode:false, OutdoorTemperatureSensorNotFault:true, HeatSetpoint:67.0000, DispTemperatureStatus:0,
> HeatNextPeriod:67, IndoorHumiditySensorNotFault:true, OutdoorTempStatus:128, IndoorHumidStatus:0, 
> SystemSwitchPosition:4], canControlHumidification:false, hasFan:true] 
> 
> log.trace("Fan operating state: ${response.data.latestData.fanData.fanIsRunning}")
> log.trace("EquipmentOutputStatus: ${response.data.latestData.uiData.EquipmentOutputStatus}")
> log.trace("IndoorHumidity: ${response.data.latestData.uiData.IndoorHumidity}")
> 
> log.trace("OutdoorTemp = ${response.data.latestData.uiData.OutdoorTemperature}")
> log.trace("fanMode: ${response.data.latestData.fanData.fanMode}")
> log.trace("SystenSwitchPosition: ${response.data.latestData.uiData.SystemSwitchPosition}")
> log.trace("StatusCool: ${response.data.latestData.uiData.StatusCool}")
> log.trace("StatusHeat: ${response.data.latestData.uiData.StatusHeat}")
> 
> log.trace("IndoorHumiditySensorAvailable: ${response.data.latestData.uiData.IndoorHumiditySensorAvailable}")        
> log.trace("IndoorHumidityAvailable: ${response.data.latestData.uiData.IndoorHumidityAvailable}")        
> 
> log.debug "OutdoorHumidityAvailable: response.data.latestData.uiData.OutdoorHumidityAvailable"        
> log.debug "OutdoorTemperatureAvailable: $response.data.latestData.uiData.OutdoorTemperatureAvailable"        
> 
> log.debug "OutdoorHumiditySensorNotFault = $response.data.latestData.uiData.OutdoorHumiditySensorNotFault"
> log.debug "OutdoorTemperatureSensorNotFault = $response.data.latestData.uiData.OutdoorTemperatureSensorNotFault"
> 
> log.debug "IndoorHumiditySensorNotFault: $response.data.latestData.uiData.IndoorHumiditySensorNotFault"        
> log.debug "IndoorHumidStatus: $response.data.latestData.uiData.IndoorHumidStatus"       
> log.debug "OutdoorHumidStatus: $response.data.latestData.uiData.OutdoorHumidStatus"   
> log.debug "OutdoorHumidity: = $response.data.latestData.uiData.OutdoorHumidity"
> log.debug "OutdoorTemperature = $response.data.latestData.uiData.OutdoorTemperature"
> 
> log.debug "got curOutdoorTemp = $curOutdoorTemp"
> log.debug "got curOutdoorHumidity = $curOutdoorHumidity"
> log.debug "hasOutdoorHumid = $hasOutdoorHumid"
> log.debug "hasOutdoorTemp =  $hasOutdoorTemp"
> */
> 
>         //  log.debug "displayUnits = $displayUnits"
>         state.DisplayUnits = $displayUnits
> 
>         //Operating State Section 
>         //Set the operating state to off 
> 
>         def operatingState = "Unknown"
> 
>         // lgk operating state not working here.. shows both on ie 1 when heat doesnt go on to 67 and heat till 76  and current is 73 
>         //Check the status of heat and cool 
> 
>         // lgk old method now use equipment status
>         if (equipmentStatus == 1) {
>             operatingState = "Heating"
>         } else if (equipmentStatus == 2) {
>             operatingState = "Cooling"
>         } else if (equipmentStatus == 0) {
>             operatingState = "Idle"
>         } else {
>             operatingState = "Unknown"
>         }
> 
>         /*
> if(statusCool == 1 && (switchPos == 3 || switchPos == 5 || swithPos == 4)) {
> operatingState = "cooling"
> } else if (statusHeat == 1 && (switchPos == 1 || switchPos == 5 || switchPos == 4)) {  
> operatingState = "heating"
> } else if (statusCool == 0 && statusHeat == 0) {
> operatingState = "idle"
> 
> } else {
> operatingState = "unknown"
> }
> */
> 
>         log.trace("Set operating State to: ${operatingState}")        
> 
>         // set fast state
>         def fanState = "Unknown"
> 
>         if (fanIsRunning == true)
>         fanState = "On"
>         else fanState = "Idle" 
> 
>         log.trace("Set Fan operating State to: ${fanState}")        
> 
>         //End Operating State
> 
>         //  log.debug curTemp
>         // log.debug fanMode
>         // log.debug switchPos
> 
>         //fan mode 0=auto, 2=circ, 1=on
> 
>         if(fanMode==0)
>         fanMode = 'auto'
>         if(fanMode==1)
>         fanMode = 'on'
>         if(fanMode==2)
>         fanMode = 'circulate'
> 
>         if(switchPos==1)
>         switchPos = 'heat'
>         if(switchPos==2)
>         switchPos = 'off'
>         if(switchPos==3)
>         switchPos = 'cool'
>         if(switchPos==4 || switchPos==5)
>         switchPos = 'auto'
> 
>         def formattedCoolSetPoint = String.format("%5.1f", coolSetPoint)
>         def formattedHeatSetPoint = String.format("%5.1f", heatSetPoint)
>         def formattedTemp = String.format("%5.1f", curTemp)
> 
>         def finalCoolSetPoint = formattedCoolSetPoint as BigDecimal
>         def finalHeatSetPoint = formattedHeatSetPoint as BigDecimal
>         def finalTemp = formattedTemp as BigDecimal
> 
>         //Send events 
>         sendEvent(name: 'thermostatOperatingState', value: operatingState)
>         sendEvent(name: 'fanOperatingState', value: fanState)
>         sendEvent(name: 'thermostatFanMode', value: fanMode)
>         sendEvent(name: 'thermostatMode', value: switchPos)
>         sendEvent(name: 'coolingSetpoint', value: finalCoolSetPoint )
>         sendEvent(name: 'heatingSetpoint', value: finalHeatSetPoint )
>         sendEvent(name: 'temperature', value: finalTemp, state: switchPos)
>         sendEvent(name: 'relativeHumidity', value: curHumidity as Integer)
> 
> 
>         //log.debug "location = $location.name tz = $location.timeZone"
>         def now = new Date().format('MM/dd/yyyy h:mm a',location.timeZone)
> 
>         //def now = new Date()
>         //def tf = new java.text.SimpleDateFormat("MM/dd/yyyy h:mm a")
>         //tf.setTimeZone(TimeZone.getTimeZone("GMT${settings.tzOffset}"))
>         //def newtime = "${tf.format(now)}" as String   
>         // sendEvent(name: "lastUpdate", value: newtime, descriptionText: "Last Update: $newtime")
>         sendEvent(name: "lastUpdate", value: now, descriptionText: "Last Update: $now")
> 
> 
>         if (enableOutdoorTemps == "Yes")
>         {
> 
>             if (hasOutdoorHumid)
>             {
>                 sendEvent(name: 'outdoorHumidity', value: curOutdoorHumidity as Integer)
>             }
> 
>             if (hasOutdoorTemp)
>             {
>                 sendEvent(name: 'outdoorTemperature', value: curOutdoorTemp as Integer)
>             }
>         }
> 
> 
> 
> 
>     }
> }
> 
> 
> def getHumidifierStatus()
> {
>     /*
> $.ajax({
> url: humUrl,
> type: 'POST',
> cache: false,
> dataType: "json",
> success: function(data) {
> /portal/Device/Menu/GetHumData/454832';
> */
>     def params = [
>         uri: "https://www.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://www.mytotalconnectcomfort.com/portal/Menu/${settings.honeywelldevice}',
>             '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        ],
>     ]
>     httpGet(params) { response ->
>         log.debug "GetHumidity Request was successful, $response.status"
>         log.debug "response = $response.data"
> 
>         //  log.debug "ld = $response.data.latestData"
>         //  log.debug "humdata = $response.data.latestData.humData"
> 
>         log.trace("lowerLimit: ${response.data.latestData.humData.lowerLimit}")        
>         log.trace("upperLimit: ${response.data.humData.upperLimit}")        
>         log.trace("SetPoint: ${response.data.humData.Setpoint}")        
>         log.trace("DeviceId: ${response.data.humData.DeviceId}")        
>         log.trace("IndoorHumidity: ${response.data.humData.IndoorHumidity}")        
> 
>     }
> }
> 
> def api(method, args = [], success = {}) {
> 
> }
> 
> // Need to be logged in before this is called. So don't call this. Call api.
> def doRequest(uri, args, type, success) {
> 
> }
> 
> def refresh() {
>     log.debug "Executing 'refresh'"
>     def unit = getTemperatureScale()
>     log.debug "units = $unit"
>     login()
>     //getHumidifierStatus()
>     getStatus()
> }
> 
> def login() {  
>     log.debug "Executing 'login'"
> 
>     def params = [
>         uri: 'https://www.mytotalconnectcomfort.com/portal',
>         headers: [
>             'Content-Type': 'application/x-www-form-urlencoded',
>             'Accept': 'application/json, text/javascript, */*; q=0.01',
>             'Accept-Encoding': 'sdch',
>             'Host': 'www.mytotalconnectcomfort.com',
>             'DNT': '1',
>             'Origin': 'www.mytotalconnectcomfort.com/portal/',
>             'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36'
>         ],
>         body: [timeOffset: '240', UserName: "${settings.username}", Password: "${settings.password}", RememberMe: 'false']
>     ]
> 
>     device.data.cookiess = ''
> 
>     httpPost(params) { response ->
>         log.debug "Request was successful, $response.status"
>         log.debug response.headers
>         String allCookies = ""
> 
>         //response.getHeaders('Set-Cookie').each {
>         //              log.debug "---Set-Cookie: ${it.value}"
>         //      }
> 
>         response.getHeaders('Set-Cookie').each {
>             String cookie = it.value.split(';|,')[0]
>             Boolean skipCookie = false
>             def expireParts = it.value.split('expires=')
> 
>             try {
>                 def cookieSegments = it.value.split(';')
>                 for (int i = 0; i < cookieSegments.length; i++) {
>                     def cookieSegment = cookieSegments[i]
>                     String cookieSegmentName = cookieSegment.split('=')[0]
> 
>                     if (cookieSegmentName.trim() == "expires") {
>                         String expiration = cookieSegment.split('=')[1] 
> 
>                         Date expires = new Date(expiration)
>                         Date newDate = new Date() // right now
> 
>                         if (expires < newDate ) {
>                             skipCookie=true
>                             //log.debug "-skip cookie: $it.value"
>                         } else {
>                             //log.debug "+not skipping cookie: expires=$expires. now=$newDate. cookie: $it.value"
>                         }
> 
>                     } 
>                 }
>             }
>             catch (e) {
>                 log.debug "!error when checking expiration date: $e ($expiration) [$expireParts.length] {$it.value}"
>             }
> 
>             allCookies = allCookies + it.value + ';'
> 
>             if(cookie != ".ASPXAUTH_TH_A=") {
>                 if (it.value.split('=')[1].trim() != "") {
>                     if (!skipCookie) {
>                         log.debug "Adding cookie to collection: $cookie"
>                         device.data.cookiess = device.data.cookiess+cookie+';'
>                     }
>                 }
>             }
>         }
> 
>         log.debug "cookies: $device.data.cookiess"
>     }
> }
> 
> def isLoggedIn() {
>     if(!device.data.auth) {
>         log.debug "No device.data.auth"
>         return false
>     }
> 
>     def now = new Date().getTime();
>     return device.data.auth.expires_in > now
> }
> 
> 
> def updated()
> {
>     log.debug "in updated"
>     state.DisplayUnits = settings.tempScale
>     log.debug "display units now = $state.DisplayUnits"
> 
> }
> 
> def installed() {
>     state.DisplayUnits = settings.tempScale
> 
>     log.debug "display units now = $state.DisplayUnits"
> }

Pressing Cool Down has an error:
:errorjava.lang.NullPointerException: Cannot invoke method minus() on null object on line 106 (coolLevelDown)
but I ran out of time.

Thanks @csteele,

This is a good start, but not being able to get the Heat ON in the winter would make this code useless.

I will connect it tomorrow or tonight if family permitted.

Heat might work fine, I just never hit that button. I just did and yep, it works too. Set Follow Schedule worked, but none of the fan buttons worked. Some buttons don't have a matching feature on the particular Thermostat I have, "Auto" would be one example. I think that's an Auto Cool/Heat.

I'm seeing an occasional error of:
error groovyx.net.http.HttpResponseException: Unauthorized on line 491 (getStatus)
Which I know is a failed login to Honeywell - My Total Connect Comfort...
It's an oddity because it worked the previous and again the next time.

But I'm at work now and won't get back to debugging for a while.

1 Like

Will try that tonight. Thanks.

Hi @csteele and @SmartHomePrimer,

I added the device driver just now and set the hub to search for it and not able to find it. Can you help?

Looking at the Driver code posted above, I believe you will need to manually add this new device. It will not be 'Discovered' by Hubitat as that is only for Zigbee and Z-Wave devices.

On your 'Devices' page, click 'Add Virtual Device' and then follow through the process of manually adding a device. Be sure to assign this virtual device the correct Driver that you added. Once added, you will be able to configure all of the settings of this device.

2 Likes

Just to add to these tips, you will find custom device drivers at the end of the list

3 Likes

Thank @SmartHomePrimer,

I got that part last night when I added up all of the Xiaomi drivers and than paired all of my motion sensors to Hubitat. The devices are not responsive as of this morning. Will investigate when I will get back home.

Try just tapping their pair buttons to see if the “wake up”. Distance is an issue with Xiaomi, and the Motion and Contact sensors seem to be very sensative to this. It’s not a standard Zigbee, and distance seems to compound the issue with the Motion and Door sensors in particular.

Also, it can seem like they’re Active, but not responding as well. Remember that these do not report inactive state. Inactive is being reported by the driver based on a timeout that @veeceeoh added to compensate for that discrepancy. Be sure to wait for a minute to see a changeafter active state if you have not changed the timeout in the drive settings yourself.

1 Like