If you use mytotalconnectcomfort.com to control your thermostat, the device type will likely give you some if not all functionality.
Iâve been playing with Honeywellâs âAPIâ with python. I have a RTH9580WF. I donât use smartthings, but this thread seems to have the most information. Iâve been investigating determining idle vs running. The current codebase look incorrect IMO.
statusCool/statusHeat=0 when set to follow schedule
statusCool/statusHeat=1 when set on temporary hold
statusCool/statusHeat=2 when set on permanent hold
SystemSwitchPosition=1 when set to heat mode
SystemSwitchPosition=2 when set to off mode
SystemSwitchPosition=3 when set to cool mode
SystemSwitchPosition=4 when set to auto mode
I hate to say it, but honeywell doesnât publish running vs idle to their âAPIâ. I put in a request for this in September 2013 but they still havenât implemented it.
In regards to humidity, it should be fairly straight forward to code around these values;
âIndoorHumidityâ: 29.0,
âIndoorHumiditySensorAvailableâ: true,
âIndoorHumiditySensorNotFaultâ: true,
âOutdoorHumidityâ: 92.0,
âOutdoorHumidityAvailableâ: true,
Hope this helps.
I am trying to get the circulate running all I have is on and auto. MY code now is below, any help would be appreciated.
}
standardTile(âthermostatFanModeâ, âdevice.thermostatFanModeâ, inactiveLabel: false, canChangeIcon: true) {
state âautoâ, label:â${name}â, action:âthermostat.fanOnâ, icon: "http://xxxxx.xxx/Systemfan1.png"
state âonâ, label:â${name}â, action:âthermostat.fanAutoâ, icon: "http://xxxxx.xxx/Systemfan1.png"
state âcirculateâ, label:â${name}â, action:âthermostat.fanAutoâ, icon: âhttp://jxxxxx.xxx/Systemfan1.pngâ
}
I think line 4 is wrong, can someone look at it please? If anyone has circulate working please post your code so I can get my unit working again.
Just tested this, and youâre right. I might have to slap a multi somewhere on the unit itself to pick up vibrations for running/idle.
Or maybe I can rig something to the zone panelâŚ
If there is someone out there that has the fan modes all working ON, AUTO, CIRCULATE on A Honeywell RTH9580WF please look at this code and let me know where I screwed it up.
/**
* Total Comfort API2D
*
* Based on Code by Eric Thomas
*
* 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.
*
*/
preferences {
input("username", "text", title: "Username", description: "Your
Total Comfort User Name")
input("password", "password", title: "Password", description: "Your
Total Comfort password")
input("honeywelldevice", "text", title: "Device ID", description:
"Your Device ID")
}
metadata {
definition (name: "Total Comfort API2D", namespace: "Total Comfort
APIBH", author: "Eric Thomas") {
capability "Polling"
capability "Thermostat"
capability "Refresh"
capability "Temperature Measurement"
capability "Sensor"
capability "Relative Humidity Measurement"
command "heatLevelUp"
command "heatLevelDown"
command "coolLevelUp"
command "coolLevelDown"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
valueTile("temperature", "device.temperature", width: 2, height: 2,
canChangeIcon: true, canChangeBackground: true) {
state("temperature", label: '${currentValue}°F', unit:"F",
backgroundColors: [
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
standardTile("thermostatMode", "device.thermostatMode",
inactiveLabel: false, canChangeIcon: true) {
state "off", label:'${name}', action:"thermostat.cool", icon:
"http://
xxxxx.xxx/Unitturnedoff.png"
state "cool", label:'${name}', action:"thermostat.heat", icon:
"http://
xxxxx.xxx/Coolingmode.png"
state "heat", label:'${name}', action:"thermostat.auto", icon:
"http://
xxxxx.xxx/Heatingmode.png",
backgroundColor: '#E14902'
state "auto", label:'${name}', action:"thermostat.off", icon:
"http://
xxxxx.xxx/Automaticmode.png",
backgroundColor: '#44b621'
}
standardTile("thermostatFanMode", "device.thermostatFanMode",
inactiveLabel: false, canChangeIcon: true) {
state "auto", label:'${name}', action:"thermostat.fanOn", icon:
"http://
xxxxx.xxx/Systemfan1.png"
state "on", label:'${name}', action:"thermostat.fanAuto", icon:
"http://
xxxxx.xxx/Systemfan1.png",
backgroundColor: '#44b621'
state "circ", label:'${name}', action:"thermostat.fanCirc", icon:
"http://
xxxxx.xxx/Systemfan1.png"
}
controlTile("coolSliderControl", "device.coolingSetpoint", "slider",
height: 3, width: 1, inactiveLabel: false) {
state "setCoolingSetpoint", label:'Set temperarure to',
action:"thermostat.setCoolingSetpoint",
backgroundColors:[
[value: 31, color: "#214478"],
[value: 44, color: "#214478"],
[value: 59, color: "#214478"],
[value: 74, color: "#214478"],
[value: 84, color: "#214478"],
[value: 95, color: "#214478"],
[value: 96, color: "#214478"]
]
}
valueTile("coolingSetpoint", "device.coolingSetpoint",
inactiveLabel: false)
{
state "default", label:'Cool @${currentValue}°F', unit:"F",
backgroundColors:
[
[value: 31, color: "#214478"],
[value: 44, color: "#214478"],
[value: 59, color: "#214478"],
[value: 74, color: "#214478"],
[value: 84, color: "#214478"],
[value: 95, color: "#214478"],
[value: 96, color: "#214478"]
]
}
valueTile("heatingSetpoint", "device.heatingSetpoint",
inactiveLabel: false)
{
state "default", label:'Heat @${currentValue}°F', unit:"F",
backgroundColors:
[
[value: 31, color: "#840909"],
[value: 44, color: "#840909"],
[value: 59, color: "#840909"],
[value: 74, color: "#840909"],
[value: 84, color: "#840909"],
[value: 95, color: "#840909"],
[value: 96, color: "#840909"]
]
}
//tile added for operating state - Create the tiles for each
possible state, look at other examples if you wish to change the
icons here.
standardTile("thermostatOperatingState",
"device.thermostatOperatingState", inactiveLabel: false) {
state "running", label:'${name}', icon: "http://
xxxxx.xxx/Unitrunning.png"
state "idle", label:'${name}', icon: "http://
xxxxx.xxx/Unitoff.png"
}
standardTile("refresh", "device.thermostatMode", inactiveLabel:
false, decoration: "flat") {
state "default", action:"polling.poll", icon: "http://
xxxxx.xxx/Refresh2.png"
}
standardTile("heatLevelUp", "device.heatingSetpoint", canChangeIcon:
false, inactiveLabel: false) {
state "heatLevelUp", label:'Up', action:"heatLevelUp", icon:"http://
xxxxx.xxx/Heatup.png"
}
standardTile("heatLevelDown", "device.heatingSetpoint",
canChangeIcon: false, inactiveLabel: false) {
state "heatLevelDown", label:'Down', action:"heatLevelDown",
icon:"http://
xxxxx.xxx/Heatdown.png"
}
standardTile("coolLevelUp", "device.heatingSetpoint", canChangeIcon:
false, inactiveLabel: false) {
state "coolLevelUp", label:'Up', action:"coolLevelUp", icon:"http://
xxxxx.xxx/Coolup.png"
}
standardTile("coolLevelDown", "device.heatingSetpoint",
canChangeIcon: false, inactiveLabel: false) {
state "coolLevelDown", label:'Down', action:"coolLevelDown",
icon:"http://
xxxxx.xxx/Cooldown.png"
}
valueTile("relativeHumidity", "device.relativeHumidity",
inactiveLabel: false)
{
state "default", label:'Humidity ${currentValue}%', unit:"%",
backgroundColors:
[
[value: 31, color: "#447821"],
[value: 44, color: "#447821"],
[value: 59, color: "#447821"],
[value: 74, color: "#447821"],
[value: 84, color: "#447821"],
[value: 95, color: "#447821"],
[value: 96, color: "#447821"]
]
}
main "temperature"
details(["temperature", "thermostatMode", "thermostatFanMode",
"heatLevelUp", "heatingSetpoint" , "heatLevelDown",
"coolLevelUp","coolingSetpoint", "coolLevelDown"
,"refresh","relativeHumidity" ,"thermostatOperatingState"])
}
}
def coolLevelUp(){
int nextLevel = device.currentValue("coolingSetpoint") + 1
if( nextLevel > 99){
nextLevel = 99
}
log.debug "Setting cool set point up to: ${nextLevel}"
setCoolingSetpoint(nextLevel)
}
def coolLevelDown(){
int nextLevel = device.currentValue("coolingSetpoint") - 1
if( nextLevel < 50){
nextLevel = 50
}
log.debug "Setting cool set point down to: ${nextLevel}"
setCoolingSetpoint(nextLevel)
}
def heatLevelUp(){
int nextLevel = device.currentValue("heatingSetpoint") + 1
if( nextLevel > 90){
nextLevel = 90
}
log.debug "Setting heat set point up to: ${nextLevel}"
setHeatingSetpoint(nextLevel)
}
def heatLevelDown(){
int nextLevel = device.currentValue("heatingSetpoint") - 1
if( nextLevel < 40){
nextLevel = 40
}
log.debug "Setting heat set point down to: ${nextLevel}"
setHeatingSetpoint(nextLevel)
}
// parse events into attributes
def parse(String description) {
}
// handle commands
def setHeatingSetpoint(temp) {
data.SystemSwitch = 'null'
data.HeatSetpoint = temp
data.CoolSetpoint = 'null'
data.HeatNextPeriod = 'null'
data.CoolNextPeriod = 'null'
data.StatusHeat='1'
data.StatusCool='1'
data.FanMode = 'null'
setStatus()
if(data.SetStatus==1)
{
sendEvent(name: 'heatingSetpoint', value: temp as Integer)
}
}
def setCoolingSetpoint(temp) {
data.SystemSwitch = 'null'
data.HeatSetpoint = 'null'
data.CoolSetpoint = temp
data.HeatNextPeriod = 'null'
data.CoolNextPeriod = 'null'
data.StatusHeat='1'
data.StatusCool='1'
data.FanMode = 'null'
setStatus()
if(data.SetStatus==1)
{
sendEvent(name: 'coolingSetpoint', value: temp as Integer)
}
}
def setTargetTemp(temp) {
data.SystemSwitch = 'null'
data.HeatSetpoint = temp
data.CoolSetpoint = temp
data.HeatNextPeriod = 'null'
data.CoolNextPeriod = 'null'
data.StatusHeat='1'
data.StatusCool='1'
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) {
data.SystemSwitch = mode
data.HeatSetpoint = 'null'
data.CoolSetpoint = 'null'
data.HeatNextPeriod = 'null'
data.CoolNextPeriod = 'null'
data.StatusHeat=1
data.StatusCool=1
data.FanMode = 'null'
setStatus()
def switchPos
if(mode==1)
switchPos = 'heat'
if(mode==2)
switchPos = 'off'
if(mode==3)
switchPos = 'cool'
if(mode==4)
switchPos = 'auto'
if(data.SetStatus==1)
{
sendEvent(name: 'thermostatMode', value: switchPos)
}
}
def fanOn() {
setThermostatFanMode(1)
}
def fanAuto() {
setThermostatFanMode(0)
}
def setThermostatFanMode(mode) {
data.SystemSwitch = 'null'
data.HeatSetpoint = 'null'
data.CoolSetpoint = 'null'
data.HeatNextPeriod = 'null'
data.CoolNextPeriod = 'null'
data.StatusHeat='null'
data.StatusCool='null'
data.FanMode = mode
setStatus()
def fanMode
if(mode==0)
fanMode = 'auto'
if(mode==1)
fanMode = 'on'
if(mode==2)
fanMode = 'circ'
if(data.SetStatus==1)
{
sendEvent(name: 'thermostatFanMode', value: fanMode)
}
}
def poll() {
refresh()
}
def setStatus() {
data.SetStatus = 0
login()
log.debug "Executing 'setStatus'"
def today= new Date()
log.debug
âhttps://mytotalconnectcomfort.com/portal/Device/SubmitControlScreenChangesâ
def params = [
uri:
âhttps://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': 'rs.alarmnet.com',
'Referer':
âhttps://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': data.cookiess ],
body: [ DeviceID: "${settings.honeywelldevice}", SystemSwitch :
data.SystemSwitch ,HeatSetpoint : data.HeatSetpoint, CoolSetpoint:
data.CoolSetpoint, HeatNextPeriod:
data.HeatNextPeriod,CoolNextPeriod:data.CoolNextPeriod,StatusHeat:data.StatusHeat,StatusCool:data.StatusCool,FanMode:data.FanMode]
]
httpPost(params) { response ->
log.debug "Request was successful, $response.status"
}
log.debug "SetStatus is 1 now"
data.SetStatus = 1
}
def getStatus() {
log.debug "Executing 'getStatus'"
def today= new Date()
log.debug
def params = [
uri:
âhttps://mytotalconnectcomfort.com/portal/Device/CheckDataSession/${settings.honeywelldevice}â,
headers: [
'Accept': '*/*',
'DNT': '1',
'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': data.cookiess ],
]
httpGet(params) { response ->
log.debug "Request was successful, $response.status"
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
log.trace("IndoorHumidity:
${response.data.latestData.uiData.IndoorHumidity}")
log.trace("IndoorHumiditySensorAvailable:
${response.data.latestData.uiData.IndoorHumiditySensorAvailable}")
log.trace("IndoorHumiditySensorNotFault:
${response.data.latestData.uiData.IndoorHumiditySensorNotFault}")
log.trace("IndoorHumidStatus:
${response.data.latestData.uiData.IndoorHumidStatus}")
//Operating State Section
//Set the operating state to off
def operatingState = "idle"
if(statusCool == 1 ) {
operatingState = "running"
} else if (statusHeat == 1 ) {
operatingState = "running"
} else {
operatingState = "idle"
}
//End Operating State
log.debug curTemp
log.debug fanMode
log.debug switchPos
log.debug statusCool
log.debug statusHeat
//fan mode 0=auto, 2=circ, 1=on
if(fanMode==0)
fanMode = 'auto'
if(fanMode==1)
fanMode = 'on'
if(mode==2)
fanMode = 'circ'
if(switchPos==1)
switchPos = 'heat'
if(switchPos==2)
switchPos = 'off'
if(switchPos==3)
switchPos = 'cool'
if(switchPos==4)
switchPos = 'auto'
//Send events
sendEvent(name: 'thermostatOperatingState', value: operatingState)
sendEvent(name: 'thermostatFanMode', value: fanMode)
sendEvent(name: 'thermostatMode', value: switchPos)
sendEvent(name: 'coolingSetpoint', value: coolSetPoint as Integer)
sendEvent(name: 'heatingSetpoint', value: heatSetPoint as Integer)
sendEvent(name: 'temperature', value: curTemp as Integer, state:
switchPos)
sendEvent(name: 'relativeHumidity', value: curHumidity as Integer)
}
}
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'"
login()
getStatus()
}
def login() {
log.debug "Executing 'login'"
def params = [
uri: 'https://mytotalconnectcomfort.com/portal',
headers: [
'Content-Type': 'application/x-www-form-urlencoded',
'Accept':
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'sdch',
'Host': 'mytotalconnectcomfort.com',
'DNT': '1',
'Origin': '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']
]
data.cookiess = ''
httpPost(params) { response ->
log.debug "Request was successful, $response.status"
log.debug response.headers
response.getHeaders('Set-Cookie').each {
String cookie = it.value.split(';|,')[0]
log.debug "Adding cookie to collection: $cookie"
if(cookie != ".ASPXAUTH_TH_A=") {
data.cookiess = data.cookiess+cookie+';'
}
}
log.debug "cookies: $data.cookiess"
}
}
def isLoggedIn() {
if(!data.auth) {
log.debug "No data.auth"
return false
}
def now = new Date().getTime();
return data.auth.expires_in > now
}
Thank you Robert for the help, found extra data regarding fan that I didnât see before. Now it seems to work, have seen no errors. The one small problem is when I hit refresh and the fan is in circulate mode it changes to auto, but the fan is still in circulate. I checked by using the Honeywell app.
Thereâs a 4th fan mode for âfollow scheduleâ. Itâs value 3. Itâs only accessible if the schedule actually contains different fan settings.
So what you saying if I am correct if you add a ST multi sensor you would interact with the Thermostat to say it was running like I do with the Temp/Hum sensor to adjust for temp drop in bedroom. And this screws things up by putting the thermostat in temp hold from 11:00 PM to 7:00AM just wish the API was just clear cut, bu Honeywell isnât very forth coming.
Not sure what happened, but today I looked and it cleared itself.
Being a electrician adding a relay to the fan control board across the fan board output could give you a set of dry contacts, but how you would integrate that into the code I am lost. To me more important is the temp hold and the perm hold.
My idea would be to have the tile that has the Heat temp to have it say âHeat for normal scheduleâ, "Heat P "for Heat in Permanent hold and âHeat Tâ for temp hold. The same thing for the cooling. How that would get programmed that would be up to you guys that know how to code.
Yup
HeatNextPeriod/CoolNextPeriod determines the 15min interval of when the temporary schedule would end. For example, if statusCool/statusHeat=1 and CoolNextPeriod/HeatNextPeriod=69, that would indicate a temporary hold is in place until 5:15PM.
// system switch enumeration - use with ModelProperty.systemSwitchPosition
my.SystemSwitch = { EMHEAT: 0, HEAT: 1, OFF: 2, COOL: 3, AUTOHEAT: 4, AUTOCOOL: 5, SOUTHERN_AWAY: 6, UNKNOWN: 7 };
// setpoint status enumeration - use with ModelProperty.statusHeat and ModelProperty.statusCool properties
my.SetpointStatus = { SCHEDULED: 0, TEMPORARY: 1, HOLD: 2, VACATION_HOLD: 3 };
// fan mode enumeration - use with ModelProperty.fanMode
my.FanMode = { Auto: 0, On: 1, Circulate: 2, FollowSchedule: 3, Unknown: 4 };
1/4 = .25 or 00:15
30/4 = 7.5 or 07:30
69/4 = 17.25, or 17:15
95/4 = 23.75 or 23:45
Hereâs my perl code to convert to normal time. Itâs easy with sprintfâs. Hope you can adapt it or think of some logic. You could just delcare 0 = 00:00, 1=00:15, 2=00:30 ⌠95=23:45 . Thereâs only 96 to add.
$nexttime = 30;
$nexttime = $nexttime/4; # 7.5
$nexttimehr = sprintf("%.02d", $nexttime); # 07. sprintf converts the hours to 2 digit format and strips the decimals
# Subtract the hours to leave just the decimal places
$nexttimemn = $nexttime - $nexttimehr # .5
$nexttimemn = $nexttimemn * 4 * 15; # 30. This converts .0 to 0, .25 to 15, .5 to 30, .75 to 45
$nexttimemn = sprintf("%.02d", $nexttimemn); # Sets 0 to 00
$time = "$nexttimehr:$nexttimemn";
Hello everyone, I canât seem to get the thermostat tile to populate. Ive entered the username, password and device ID. I logged out and into the ST app to no avail. Any ideas on how to get this to work? I have three Honeywell wifi RTH9580WF that Im trying to integrate. Is there another code that I should have used. My understanding, the one listed below should have worked. Any help would be appreciated. I did read most of this tread. If i skimmed over the answer Im sorry.
This was the code I used.
( https://github.com/jodyalbritton/honeywell-rth6580wf/blob/master/thermostat.groovy )
Hit refresh and check the activity feed. If you have items in the activity feed it is working and you need to log out of the ST app and back in. Sometimes the values are there and they donât populate unless you force close the client.
It was that simple. Thank you. If its not painfully obvious, Iâm new to ST.
This looks great, can I get a copy of your code?
Yes, i have the 9580. my address is Christopher@myblake.com
Honeywell has an API (https://rs.alarmnet.com/TotalConnectComfort/ws/MobileV2.asmx), but they only expose it to valid AppIDâs (one is statically set in their iOS app). You can fuzz the SSL communications to pick it out; Honeywell canât change this without breaking all older apps out there. It would work much more reliably (real sessions designed to be used by an App) if someone could convert it. Iâve done some work to get login/location info in Perl (https://github.com/bdwilson/redlink-api) but I donât have the bandwidth to update these scripts. Ping me if you need hints regarding the AppID.
Hello - I have added the code to Device Types and added a honeywell device. It shows active on my account but the device does not show temp, etc in the ST app? Help