Super! Thanks Bruce … this is really helpful. I’m just getting started with ST development but this helped me get the Indoor/Outdoor humidity working.
I made a couple of other changes to support Celsius, changed some icons etc.
The only remaining thing I’m trying to figure out is how to display decimals in the UI. The logs are showing the thermostat reporting a temperature of 24.5 degrees Celsius (Curent Temp: 24.5000) but the UI rounds this to 25 degrees Celsius.
Any ideas on how to change this???
/**
* Total Comfort API
*
* 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: "Honeywell Thermostat", author: "") {
capability "Polling"
capability "Thermostat"
capability "Refresh"
capability "Temperature Measurement"
capability "Sensor"
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) {
state("temperature", label: '${currentValue}°C', unit:"C", backgroundColors: [
[value: 0, color: "#153591"],
[value: 10, color: "#1e9cbb"],
[value: 20, color: "#90d2a7"],
[value: 22, color: "#44b621"],
[value: 26, color: "#f1d801"],
[value: 30, color: "#d04e00"],
[value: 35, color: "#bc2323"]
]
)
}
//Thermostat Control
standardTile("thermostatMode", "device.thermostatMode", canChangeIcon: true, inactiveLabel: false) {
state "heat", label:'', action:"thermostat.off", icon: "st.thermostat.heat", backgroundColor: '#E14902'
state "off", label:'', action:"thermostat.cool", icon: "st.thermostat.heating-cooling-off"
state "cool", label:'', action:"thermostat.heat", icon: "st.thermostat.cool", backgroundColor: '#003CEC'
}
standardTile("thermostatFanMode", "device.thermostatFanMode", canChangeIcon: true, inactiveLabel: false) {
state "auto", label:'', action:"thermostat.fanOn", icon: "st.thermostat.fan-auto"
state "on", label:'', action:"thermostat.fanCirculate", icon: "st.thermostat.fan-on"
state "circulate", label:'', action:"thermostat.fanAuto", icon: "st.thermostat.fan-circulate"
}
// Heat Temperature Setting
standardTile("heatLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
state "heatLevelDown", label:'Down', action:"heatLevelDown", icon:"st.thermostat.thermostat-down"
}
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false)
{
state "default", label:'${currentValue}°C', unit:"C",
backgroundColors:
[
[value: 0, color: "#153591"],
[value: 10, color: "#1e9cbb"],
[value: 20, color: "#90d2a7"],
[value: 22, color: "#44b621"],
[value: 26, color: "#f1d801"],
[value: 30, color: "#d04e00"],
[value: 35, color: "#bc2323"]
]
}
standardTile("heatLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
state "heatLevelUp", label:'Up', action:"heatLevelUp", icon:"st.thermostat.thermostat-up"
}
// Cool Temperature Setting
standardTile("coolLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
state "coolLevelDown", label:'Down', action:"coolLevelDown", icon:"st.thermostat.thermostat-down"
}
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false)
{
state "default", label:'${currentValue}°C', unit:"C",
backgroundColors:
[
[value: 0, color: "#153591"],
[value: 10, color: "#1e9cbb"],
[value: 20, color: "#90d2a7"],
[value: 22, color: "#44b621"],
[value: 26, color: "#f1d801"],
[value: 30, color: "#d04e00"],
[value: 35, color: "#bc2323"]
]
}
standardTile("coolLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false) {
state "coolLevelUp", label:'Up', action:"coolLevelUp", icon:"st.thermostat.thermostat-up"
}
//Humidity Reporting
valueTile("indoorhumidity", "device.indoorhumidity", inactiveLabel: false, decoration: "flat") {
state "default", label:'${currentValue}% Indoor Humidity', unit:"IndoorHumidity"
}
valueTile("outdoorhumidity", "device.outdoorhumidity", inactiveLabel: false, decoration: "flat") {
state "default", label:'${currentValue}% Outdoor Humidity', unit:"OutdoorHumidity"
}
standardTile("refresh", "device.thermostatMode", decoration: "flat") {
state "default", action:"polling.poll", icon:"st.secondary.refresh"
}
main "temperature"
details(["temperature", "thermostatMode", "thermostatFanMode", "heatLevelDown", "heatingSetpoint" , "heatLevelUp", "coolLevelDown","coolingSetpoint", "coolLevelUp", "indoorhumidity", "outdoorhumidity", "refresh",])
}
}
// parse events into attributes
def parse(String description) {
}
def heatLevelDown(){
def nextLevel = device.currentValue("heatingSetpoint") - 0.5
if( nextLevel < 15){
nextLevel = 15
}
log.debug "Setting heat set point down to: ${nextLevel}"
setHeatingSetpoint(nextLevel)
}
def heatLevelUp(){
def nextLevel = device.currentValue("heatingSetpoint") + 0.5
if( nextLevel > 30){
nextLevel = 30
}
log.debug "Setting heat set point up to: ${nextLevel}"
setHeatingSetpoint(nextLevel)
}
def coolLevelDown(){
def nextLevel = device.currentValue("coolingSetpoint") - 0.5
if( nextLevel < 20){
nextLevel = 20
}
log.debug "Setting cool set point down to: ${nextLevel}"
setCoolingSetpoint(nextLevel)
}
def coolLevelUp(){
def nextLevel = device.currentValue("coolingSetpoint") + 0.5
if( nextLevel > 35){
nextLevel = 35
}
log.debug "Setting cool set point up to: ${nextLevel}"
setCoolingSetpoint(nextLevel)
}
// 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)
}
}
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)
}
}
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 heat() {
setThermostatMode(1)
}
def cool() {
setThermostatMode(3)
}
def auto() {
setThermostatMode(4)
}
def emergencyHeat() {
setThermostatMode(0)
}
def setThermostatMode(mode) {
log.debug "-------------------------------------"
log.debug "Executing 'setThermostateMode' $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==0)
switchPos = 'emergency_heat'
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 fanCirculate() {
setThermostatFanMode(2)
}
def setThermostatFanMode(mode) {
log.debug "-------------------------------------"
log.debug "Executing 'setThermostFanMode'"
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==2)
fanMode = 'circulate'
if(mode==1)
fanMode = 'on'
if(data.SetStatus==1)
{
sendEvent(name: 'thermostatFanMode', value: fanMode)
}
}
def poll() {
refresh()
}
def setStatus() {
log.debug "-------------------------------------"
log.debug "Executing 'setStatus'"
data.SetStatus = 0
login()
def params = [
uri: "https://rs.alarmnet.com/TotalConnectComfort/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://rs.alarmnet.com/TotalConnectComfort/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 "'setStatus' Request was successful, $response.status"
}
log.debug "SetStatus is 1 now"
log.debug "-------------------------------------"
data.SetStatus = 1
}
def getStatus() {
log.debug "-------------------------------------"
log.debug "Executing 'getStatus'"
def params = [
uri: "https://rs.alarmnet.com/TotalConnectComfort/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://rs.alarmnet.com/TotalConnectComfort/',
'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 "'getStatus' Request was successful, $response.status"
def curTemp = response.data.latestData.uiData.DispTemperature
def coolSetPoint = response.data.latestData.uiData.CoolSetpoint
def heatSetPoint = response.data.latestData.uiData.HeatSetpoint
def fanMode = response.data.latestData.fanData.fanMode
//fan mode 0=auto, 1=on, 2=circ
if(fanMode==0)
fanMode = 'auto'
if(fanMode==1)
fanMode = 'on'
if(fanMode==2)
fanMode = 'circulate'
def switchPos = response.data.latestData.uiData.SystemSwitchPosition
//switchPos 1=heat, 2=off, 3=cool, 4=auto, 0=emergency heat
if(switchPos==1)
switchPos = 'heat'
if(switchPos==2)
switchPos = 'off'
if(switchPos==3)
switchPos = 'cool'
if(switchPos==4)
switchPos = 'auto'
if(switchPos==0)
switchPos = 'emergency_heat'
def IndoorHumiditySensorAvailable = response.data.latestData.uiData.IndoorHumiditySensorAvailable
def IndoorHumidity = response.data.latestData.uiData.IndoorHumidity
IndoorHumidity = IndoorHumidity
def OutdoorHumidityAvailable = response.data.latestData.uiData.OutdoorHumidityAvailable
def OutdoorHumidity = response.data.latestData.uiData.OutdoorHumidity
OutdoorHumidity = OutdoorHumidity
log.debug "-------------------------------------"
log.debug "Thermometer Readings from Total Connect"
log.debug "-------------------------------------"
log.debug "Curent Temp: $curTemp"
log.debug "Fan Mode: $fanMode"
log.debug "Switch Pos: $switchPos"
log.debug "Cool Set Point: $coolSetPoint"
log.debug "Heat Set Point: $heatSetPoint"
log.debug "Indoor Humidity: $IndoorHumidity"
log.debug "Outdoor Humidity: $OutdoorHumidity"
log.debug "-------------------------------------"
sendEvent(name: 'thermostatFanMode', value: fanMode)
sendEvent(name: 'thermostatMode', value: switchPos)
sendEvent(name: 'coolingSetpoint', value: coolSetPoint)
sendEvent(name: 'heatingSetpoint', value: heatSetPoint)
sendEvent(name: 'temperature', value: curTemp as Double, state: switchPos)
sendEvent(name: 'indoorhumidity', value: IndoorHumidity as Integer)
sendEvent(name: 'outdoorhumidity', value: OutdoorHumidity 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 "-------------------------------------"
log.debug "Executing 'refresh'"
login()
getStatus()
}
def login() {
log.debug "-------------------------------------"
log.debug "Executing 'login'"
def params = [
uri: 'https://rs.alarmnet.com/TotalConnectComfort/',
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': 'rs.alarmnet.com',
'DNT': '1',
'Origin': 'https://rs.alarmnet.com/TotalComfort/',
'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 "'login' 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.cookies"
}
}
def isLoggedIn() {
if(!data.auth) {
log.debug "No data.auth"
return false
}
def now = new Date().getTime();
return data.auth.expires_in > now
}