/* Copyright 2020 csromei * * 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. * */ definition( name: "Thermostat Tracker", namespace: "Craig.Romei", author: "Craig Romei", description: "Track Thermostat usage", category: "Tracking", iconUrl: "https://raw.githubusercontent.com/napalmcsr/SmartThingsStuff/master/smartapps/keenect/clipart-thermometer-thermometer-clipart-free-6-thermometer-clip-art-clipartix-free-clipart.jpg", iconX2Url: "https://raw.githubusercontent.com/napalmcsr/SmartThingsStuff/master/smartapps/keenect/clipart-thermometer-thermometer-clipart-free-6-thermometer-clip-art-clipartix-free-clipart.jpg", iconX3Url : "https://raw.githubusercontent.com/napalmcsr/SmartThingsStuff/master/smartapps/keenect/clipart-thermometer-thermometer-clipart-free-6-thermometer-clip-art-clipartix-free-clipart.jpg" ) preferences { page(name: "pageConfig") // Doing it this way elimiates the default app name/mode options. page(name: "pageStatus") } def pageConfig() { dynamicPage(name: "", title: "", install: true, uninstall: true, refreshInterval:0) { section("Report") { href "pageStatus", title: "Thermostat Usage", description: "Tap to see Thermostat usage" input "DashboardTileUpdate", "capability.sensor", title: "Display Tile:", required: false input name : "TStatStatesToReport", title: "Thermostat States to display:",multiple: true,required : true, type : "enum", options: getTstatStates(),submitOnChange : false,defaultValue : "1" } section("Thermostat") { input "tstat", "capability.thermostat", title: "Thermostat:", required: true } section("Logging") { input name : "logLevel",title : "IDE logging level",multiple : false,required : true,type : "enum",options : getLogLevels(),submitOnChange : false,defaultValue : "0" } section() { label title: "Enter a name for this automation", required: false } } } def pageStatus(){ dynamicPage( name : "Status" ,title : "Thermostat Usage" ,install : false ,uninstall : false ) { section(){ def reportString ="Day"+"\t\t\t" if (TStatStatesToReport.contains("1")){ reportString +="idle"+"\t\t" } if (TStatStatesToReport.contains("2")){ reportString +="cooling"+"\t" } if (TStatStatesToReport.contains("3")){ reportString +="heating"+"\t" } if (TStatStatesToReport.contains("4")){ reportString +="fan only"+"\t" } if (TStatStatesToReport.contains("5")){ reportString +="pend cool" } if (TStatStatesToReport.contains("6")){ reportString +="\t"+"pend heat" } if (TStatStatesToReport.contains("7")){ reportString +="\t"+ "vent econ" } reportString +="\n" + "Today" +"\t\t" if (TStatStatesToReport.contains("1")){ reportString +=convtime(state.StatusMapToday["idle"])+"\t" } if (TStatStatesToReport.contains("2")){ reportString +=convtime(state.StatusMapToday["cooling"]) +"\t" } if (TStatStatesToReport.contains("3")){ reportString +=convtime(state.StatusMapToday["heating"]) +"\t" } if (TStatStatesToReport.contains("4")){ reportString +=convtime(state.StatusMapToday["fan only"]) +"\t" } if (TStatStatesToReport.contains("5")){ reportString +=convtime(state.StatusMapToday["pending cool"]) +"\t\t" } if (TStatStatesToReport.contains("6")){ reportString +=convtime(state.StatusMapToday["pending heat"]) +"\t\t" } if (TStatStatesToReport.contains("7")){ reportString +=convtime(state.StatusMapToday["vent economizer"]) } reportString +="\n" for(int i = 0;i<=6;i++) { reportString += i+" day(s) ago"+"\t" if (TStatStatesToReport.contains("1")){ reportString +=convtime(state.Last7Days[i]["idle"])+"\t" } if (TStatStatesToReport.contains("2")){ reportString +=convtime(state.Last7Days[i]["cooling"]) +"\t" } if (TStatStatesToReport.contains("3")){ reportString += convtime(state.Last7Days[i]["heating"]) +"\t" } if (TStatStatesToReport.contains("4")){ reportString +=convtime(state.Last7Days[i]["fan only"]) +"\t" } if (TStatStatesToReport.contains("5")){ reportString += convtime(state.Last7Days[i]["pending cool"]) +"\t\t" } if (TStatStatesToReport.contains("6")){ reportString += convtime(state.Last7Days[i]["pending heat"]) +"\t\t" } if (TStatStatesToReport.contains("7")){ reportString += convtime(state.Last7Days[i]["vent economizer"]) } reportString += "\n" } paragraph reportString } } } def installed() { initialize() } def updated() { unsubscribe() initialize() } def initialize() { infolog "Initializing" def now = new Date().getTime().intdiv(1000) unsubscribe() debuglog "Getting Thermostat running State" state.mainTstatState = state.mainTstatState ?: getMainTstatState() state.stateChangeTime = state.stateChangeTime ?: now subscribe(tstat, "thermostatOperatingState", tstatStateHandler) debuglog "${TimeToRun}" schedule("0 0 0 * * ?", Timehandler) if(!state.StatusMapToday){ debuglog "Initializing a new Day" state.StatusMapToday = [:] InitializeNewDay() } if(!state.Last7Days) { state.Last7Days = new Map[7] for(int i = 6;i>=0;i--) { state.Last7Days[i] = [:] } } if(DashboardTileUpdate) { CreateVerticalDashboardNow() CreateVerticalDashboardHistory() } infolog "initialize- complete" } def tstatStateHandler(evt){ infolog "tstatStateHandler- evt name: ${evt.name}, value: ${evt.value}" UpdateTimeRunning() state.mainTstatState = evt.value.toUpperCase() if(DashboardTileUpdate) { CreateVerticalDashboardNow() } debuglog "tstatStateHandler- state.mainTstatState : ${state.mainTstatState}" infolog "tstatStateHandler- complete" } def UpdateTimeRunning() { def now = new Date().getTime().intdiv(1000) def timeRunning = now - state.stateChangeTime.toInteger() debuglog "UpdateTimeRunning- Time Running: ${timeRunning}" switch (state.mainTstatState){ case "HEATING" : state.StatusMapToday["heating"] += timeRunning break case "COOLING" : state.StatusMapToday["cooling"] += timeRunning break case "IDLE" : state.StatusMapToday["idle"] += timeRunning break case "FAN ONLY" : state.StatusMapToday["fan only"] += timeRunning break case "VENT ECONOMIZER" : state.StatusMapToday["vent economizer"] += timeRunning break case "PENDING HEAT" : state.StatusMapToday["pending heat"] += timeRunning break case "PENDING COOL" : state.StatusMapToday["pending cool"] += timeRunning break default : infolog "UpdateTimeRunning- Error, bad tstat state" break } state.stateChangeTime = now } def Timehandler() { infolog "Timehandler - started" UpdateTimeRunning() for(int i = 5;i>=0;i--) { state.Last7Days[i+1]=state.Last7Days[i].clone() } state.Last7Days[0] = state.StatusMapToday.clone() InitializeNewDay() if(DashboardTileUpdate) { CreateVerticalDashboardNow() CreateVerticalDashboardHistory() DashboardTileUpdate.SetDailyCooling(state.Last7Days[0]["cooling"]/(60*60)) DashboardTileUpdate.SetDailyIdle(state.Last7Days[0]["idle"]/(60*60)) DashboardTileUpdate.SetDailyFan(state.Last7Days[0]["fan only"]/(60*60)) DashboardTileUpdate.SetDailyHeating(state.Last7Days[0]["heating"]/(60*60)) DashboardTileUpdate.SetDailyPendCool(state.Last7Days[0]["pending cool"]/(60*60)) DashboardTileUpdate.SetDailyVentEcon(state.Last7Days[0]["vent economizer"]/(60*60)) DashboardTileUpdate.SetDailyPendHeat(state.Last7Days[0]["pending heat"]/(60*60)) } infolog "Timehandler - complete" } def InitializeNewDay() { state.StatusMapToday["idle"] = 0 state.StatusMapToday["cooling"] = 0 state.StatusMapToday["heating"] = 0 state.StatusMapToday["fan only"] = 0 state.StatusMapToday["vent economizer"] = 0 state.StatusMapToday["pending heat"] = 0 state.StatusMapToday["pending cool"] = 0 } def getMainTstatState(){ def TstatState = tstat.currentValue("thermostatOperatingState") if (TstatState!=null){ TstatState = TstatState.toUpperCase() } else { TstatState = "NULL" } debuglog "getMainTstatState Main TstatState : ${TstatState}" return TstatState } def convtime(seconds) { if(!seconds) { seconds = 0 } def hour = seconds.intdiv(60*60) // hour = Math.round(hour) def minutes = (seconds%(60*60))/60 minutes = Math.round(minutes) return String.format('%02d',hour)+":"+String.format('%02d',minutes) } def CreateVerticalDashboardNow() { debuglog "CreateVerticalDashboardNow - Start" debuglog "${TStatStatesToReport}" def reportString ="Today
" if (TStatStatesToReport.contains("1")){ reportString +="idle:"+"\t\t\t"+convtime(state.StatusMapToday["idle"])+"
" } if (TStatStatesToReport.contains("2")){ reportString +="cooling:"+"\t\t"+convtime(state.StatusMapToday["cooling"]) +"
" } if (TStatStatesToReport.contains("3")){ reportString += "heating"+"\t\t"+convtime(state.StatusMapToday["heating"]) +"
" } if (TStatStatesToReport.contains("4")){ reportString += "fan only"+"\t\t"+convtime(state.StatusMapToday["fan only"]) +"
" } if (TStatStatesToReport.contains("5")){ reportString += "pend cool"+"\t"+convtime(state.StatusMapToday["pending cool"]) +"
" } if (TStatStatesToReport.contains("6")){ reportString += "pend heat"+"\t"+convtime(state.StatusMapToday["pending heat"]) +"
" } if (TStatStatesToReport.contains("7")){ reportString += "vent econ"+"\t"+convtime(state.StatusMapToday["vent economizer"]) +"
" } DashboardTileUpdate.SetToday(reportString) debuglog "CreateVerticalDashboardNow - ${reportString}" } def CreateVerticalDashboardHistory() { debuglog "CreateVerticalDashboardNow - Start" def reportString = "" for(int i = 0;i<=6;i++) { if(i==0) { reportString = "Yesterday
" } else { reportString = (i+1)+" days ago
" } if (TStatStatesToReport.contains("1")){ reportString +="idle:"+"\t\t\t"+convtime(state.Last7Days[i]["idle"])+"
" } if (TStatStatesToReport.contains("2")){ reportString +="cooling:"+"\t\t"+convtime(state.Last7Days[i]["cooling"]) +"
" } if (TStatStatesToReport.contains("3")){ reportString += "heating"+"\t\t"+convtime(state.Last7Days[i]["heating"]) +"
" } if (TStatStatesToReport.contains("4")){ reportString += "fan only"+"\t\t"+convtime(state.Last7Days[i]["fan only"]) +"
" } if (TStatStatesToReport.contains("5")){ reportString += "pend cool"+"\t"+convtime(state.Last7Days[i]["pending cool"]) +"
" } if (TStatStatesToReport.contains("6")){ reportString += "pend heat"+"\t"+convtime(state.Last7Days[i]["pending heat"]) +"
" } if (TStatStatesToReport.contains("7")){ reportString += "vent econ"+"\t"+convtime(state.Last7Days[i]["vent economizer"]) +"
" } switch (i){ case 0 : DashboardTileUpdate.SetYesterday(reportString) break case 1 : DashboardTileUpdate.SetTwoDaysAgo(reportString) break case 2 : DashboardTileUpdate.SetThreeDaysAgo(reportString) break case 3 : DashboardTileUpdate.SetFourDaysAgo(reportString) break case 4 : DashboardTileUpdate.SetFiveDaysAgo(reportString) break case 5 : DashboardTileUpdate.SetSixDaysAgo(reportString) break case 6 : DashboardTileUpdate.SetSevenDaysAgo(reportString) break default : debuglog "CreateVerticalDashboardNow - Not a valid day!" break } } } def getTstatStates(){ return[["1":"idle"],["2":"cooling"],["3":"heating"],["4":"fan only"],["5":"pending cool"],["6":"pending heat"],["7":"vent economizer"]] } def debuglog(statement) { def logL = 0 if (logLevel) logL = logLevel.toInteger() if (logL == 0) {return}//bail else if (logL >= 2) { log.debug(statement) } } def infolog(statement) { def logL = 0 if (logLevel) logL = logLevel.toInteger() if (logL == 0) {return}//bail else if (logL >= 1) { log.info(statement) } } def getLogLevels(){ return [["0":"None"],["1":"Running"],["2":"NeedHelp"]] } def setVersion(){ state.version = "2.2.0" // Version number of this app state.InternalName = "TstatTracker" // this is the name used in the JSON file for this app }