/************************************************************************************************ | Application Name: Solar Graphs | | Copyright (C) 2019 | | Authors: Eric S. (@E_sch) | | Modified April 3, 2023 | |************************************************************************************************/ //file:noinspection unused //file:noinspection GroovyUnusedAssignment //file:noinspection SpellCheckingInspection import groovy.json.* import java.text.SimpleDateFormat import groovy.time.* import groovy.transform.Field definition( name: "Solar Graphs", namespace: "E_Sch", author: "Eric S.", description: "This App is used to display device graphs for Envoy Solar", category: "Convenience", iconUrl: "", iconX2Url: "", iconX3Url: "", oauth: true) static String appVersion() { "0.0.5" } preferences { page(name: "startPage") page(name: "mainAutoPage") } mappings { path("/deviceTiles") {action: [GET: "renderDeviceTiles"]} } def startPage() { log.info "startPage" if(!state?.access_token) { Boolean a=getAccessToken() } if(!state?.access_token) { enableOauth(); Boolean a=getAccessToken() } mainAutoPage() } def mainAutoPage() { if(!state?.autoDisabled) { state.autoDisabled = false } return dynamicPage(name: "mainAutoPage", title: pageTitleStr("Automation Configuration"), uninstall: true, install: true, nextPage:null ) { section() { if(settings?.autoDisabledreq) { paragraph imgTitle(getAppImg("i_inst"), paraTitleStr("This Automation is currently disabled!\nTurn it back on to to make changes or resume operation")), required: true, state: null } else { if(getIsAutomationDisabled()) { paragraph imgTitle(getAppImg("i_inst"), paraTitleStr("This Automation is still disabled!\nPress Next and Done to Activate this Automation Again")), state: "complete" } } if(!getIsAutomationDisabled()) { input "energyDevice", "capability.energyMeter", title: imgTitle(getAppImg("lightning.png"), inputTitleStr("Envoy Device?")), required: true, multiple: false, submitOnChange: true if(energyDevice) { String myUrl = getAppEndpointUrl("deviceTiles") String myLUrl = getLocalEndpointUrl("deviceTiles") String myStr = """ Graph Url: ${energyDevice.label} (local) """ paragraph imgTitle(getAppImg("graph_icon.png"), paraTitleStr(myStr)) } } } section(sectionTitleStr("Automation Options:")) { input "autoDisabledreq", "bool", title: imgTitle(getAppImg("disable_icon2.png"), inputTitleStr("Disable this Automation?")), required: false, defaultValue: false /* state?.autoDisabled */, submitOnChange: true setAutomationStatus() input("showDebug", "bool", title: imgTitle(getAppImg("debug_icon.png"), inputTitleStr("Debug Option")), description: "Show ${app?.name} Logs in the IDE?", required: false, defaultValue: false, submitOnChange: true) if(showDebug) { input("advAppDebug", "bool", title: imgTitle(getAppImg("list_icon.png"), inputTitleStr("Show Verbose Logs?")), required: false, defaultValue: false, submitOnChange: true) } else { settingUpdate("advAppDebug", "false", "bool") } } section(paraTitleStr("Automation Name:")) { def newName = getAutoTypeLabel() if(!app?.label) { app?.updateLabel("${newName}") } label title: imgTitle(getAppImg("name_tag_icon.png"), inputTitleStr("Label this Automation: Suggested Name: ${newName}")), defaultValue: "${newName}", required: true //, wordWrap: true if(!state?.isInstalled) { paragraph "Make sure to name it something that you can easily recognize." } } } } Boolean isHubitat(){ return hubUID != null } void installed() { log.debug "${app.getLabel()} Installed with settings: ${settings}" // MUST BE log.debug if(isHubitat() && !app.id) return initialize() } void updated() { log.debug "${app.getLabel()} Updated...with settings: ${settings}" state?.isInstalled = true state?.lastUpdatedDt = getDtNow() initialize() } void uninstalled() { log.debug "uninstalled" uninstAutomationApp() } void initialize() { log.debug "${app.label} Initialize..." // Must be log.debug if(!state?.autoTyp) { state.autoTyp = "chart" } settingUpdate("showDebug", "true", "bool") settingUpdate("advAppDebug", "true", "bool") resetVars() if(!state?.isInstalled) { state?.isInstalled = true } runIn(6, "initAutoApp", [overwrite: true]) } void resetVars() { stateRemove("evalSched") stateRemove("autoRunInSchedDt") stateRemove("detailEventHistory") stateRemove("detailExecutionHistory") if(state?.eric) { state.eric = false // state.powerTable = [[7, 14, 8], [7, 59, 70], [8, 14, 128], [8, 29, 145], [8, 44, 167], [8, 59, 254], [9, 14, 418], [9, 29, 627], [9, 44, 858], [9, 59, 1106], [10, 14, 1355], [10, 29, 1589], [10, 44, 1803], [10, 59, 1996], [11, 14, 2168], [11, 29, 2328], [11, 44, 2451], [11, 59, 2545], [12, 14, 2648], [12, 29, 2736], [12, 44, 2813], [12, 59, 2874], [13, 14, 2942], [13, 29, 3004], [13, 44, 3004], [13, 59, 2902], [14, 14, 2922], [14, 29, 2885], [14, 44, 2762], [14, 59, 2661], [15, 14, 2562], [15, 29, 2444], [15, 44, 2290], [15, 59, 2051], [16, 14, 1647], [16, 29, 1085], [16, 44, 288], [16, 59, 97], [17, 59, 0]] // state.powerTableYesterday = [[7, 9, 4], [7, 54, 61], [8, 9, 101], [8, 24, 134], [8, 39, 174], [8, 54, 228], [9, 9, 396], [9, 24, 610], [9, 39, 912], [9, 54, 1114], [10, 9, 1146], [10, 24, 1478], [10, 39, 1644], [10, 54, 1824], [11, 9, 2046], [11, 24, 1933], [11, 39, 2183], [11, 54, 2442], [12, 9, 1908], [12, 24, 2058], [12, 39, 2641], [12, 54, 2732], [13, 9, 2553], [13, 24, 2546], [13, 39, 3114], [13, 54, 2831], [14, 9, 2752], [14, 24, 2432], [14, 39, 2398], [14, 54, 2052], [15, 9, 1311], [15, 24, 1080], [15, 39, 797], [15, 54, 1152], [16, 9, 1793], [16, 24, 1055], [16, 39, 382], [16, 54, 103], [17, 9, 48], [18, 9, 0]] // state.energyTable = [[7, 14, 0.001], [7, 59, 0.054], [8, 14, 0.085], [8, 29, 0.121], [8, 44, 0.161], [8, 59, 0.223], [9, 14, 0.324], [9, 29, 0.476], [9, 44, 0.685], [9, 59, 0.951], [10, 14, 1.278], [10, 29, 1.661], [10, 44, 2.096], [10, 59, 2.577], [11, 14, 3.1], [11, 29, 3.661], [11, 44, 4.257], [11, 59, 4.882], [12, 14, 5.533], [12, 29, 6.205], [12, 44, 6.896], [12, 59, 7.602], [13, 14, 8.322], [13, 29, 9.059], [13, 44, 9.798], [13, 59, 10.512], [14, 14, 11.229], [14, 29, 11.939], [14, 44, 12.619], [14, 59, 13.273], [15, 14, 13.902], [15, 29, 14.497], [15, 44, 15.051], [15, 59, 15.547], [16, 14, 15.944], [16, 29, 16.206], [16, 44, 16.276], [16, 59, 16.3], [17, 59, 16.3]] // state.energyTableYesterday = [[7, 9, 0.001], [7, 54, 0.045], [8, 9, 0.069], [8, 24, 0.101], [8, 39, 0.144], [8, 54, 0.199], [9, 9, 0.296], [9, 24, 0.444], [9, 39, 0.665], [9, 54, 0.933], [10, 9, 1.21], [10, 24, 1.568], [10, 39, 1.965], [10, 54, 2.405], [11, 9, 2.9], [11, 24, 3.369], [11, 39, 3.898], [11, 54, 4.495], [12, 9, 4.957], [12, 24, 5.456], [12, 39, 6.101], [12, 54, 6.771], [13, 9, 7.395], [13, 24, 8.018], [13, 39, 8.783], [13, 54, 9.475], [14, 9, 10.151], [14, 24, 10.745], [14, 39, 11.329], [14, 54, 11.827], [15, 9, 12.145], [15, 24, 12.406], [15, 39, 12.599], [15, 54, 12.878], [16, 9, 13.31], [16, 24, 13.566], [16, 39, 13.659], [16, 54, 13.684], [17, 9, 13.686], [18, 9, 13.686]] // state.today = "09" // state.eric = true } } def initAutoApp() { if(settings["chartFlag"]) { state.autoTyp = "chart" } unschedule() unsubscribe() setAutomationStatus() subscribeToEvents() scheduler() app.updateLabel(getAutoTypeLabel()) LogAction("Automation Label: ${getAutoTypeLabel()}", "info", true) scheduleAutomationEval(10) if(showDebug || advAppDebug) { runIn(1800, logsOff) } //revokeAccessToken() String devTilesUrl = getAppEndpointUrl("deviceTiles") Logger("initAutoApp: devTile: ${devTilesUrl}") } void subscribeToEvents() { if(settings?.energyDevice) { subscribe(energyDevice, "energy", automationGenericEvt) subscribe(energyDevice, "power", automationGenericEvt) } } void scheduler() { } void uninstAutomationApp() { } static String strCapitalize(String str) { return str ? str?.capitalize() : (String)null } void automationGenericEvt(evt) { Long startTime = now() Long eventDelay = startTime - evt.date.getTime() LogTrace("${evt.name.toUpperCase()} Event | Device: ${evt?.displayName} | Value: (${strCapitalize(evt?.value)}) with a delay of ${eventDelay}ms".toString()) doTheEvent(evt) } void doTheEvent(evt) { if(getIsAutomationDisabled()) { return } else { scheduleAutomationEval() storeLastEventData(evt) } } void storeLastEventData(evt) { if(evt) { Map newVal = ["name":evt.name, "displayName":evt.displayName, "value":evt.value, "date":formatDt((Date)evt.date), "unit":evt.unit] state?.lastEventData = newVal //log.debug "LastEvent: ${state?.lastEventData}" List list = state?.detailEventHistory ?: [] Integer listSize = 15 if(list?.size() < listSize) { Boolean a=list.push(newVal) } else if(list?.size() > listSize) { Integer nSz = (list?.size()-listSize) + 1 List nList = list?.drop(nSz) Boolean a=nList?.push(newVal) list = nList } else if(list?.size() == listSize) { List nList = list?.drop(1) Boolean a=nList?.push(newVal) list = nList } if(list) { state.detailEventHistory = list } } } def storeExecutionHistory(val, String method = (String)null) { //log.debug "storeExecutionHistory($val, $method)" if(method) { LogAction("${method} Execution Time: (${val} milliseconds)", "trace", false) } List list = state?.detailExecutionHistory ?: [] Integer listSize = 30 list = addToList([val, method, getDtNow()], list, listSize) if(list) { state?.detailExecutionHistory = list } } static List addToList(val, List list, Integer listSize) { if(list?.size() < listSize) { Boolean a=list.push(val) } else if(list?.size() > listSize) { Integer nSz = (list?.size()-listSize) + 1 List nList = list?.drop(nSz) Boolean a=nList?.push(val) list = nList } else if(list?.size() == listSize) { List nList = list?.drop(1) Boolean a=nList?.push(val) list = nList } return list } void setAutomationStatus(Boolean upd=false) { Boolean myDis = (settings?.autoDisabledreq == true) if(!getIsAutomationDisabled() && myDis) { LogAction("Automation Disabled at (${getDtNow()})", "info", true) state?.autoDisabledDt = getDtNow() } else if(getIsAutomationDisabled() && !myDis) { LogAction("Automation Enabled at (${getDtNow()})", "info", true) state?.autoDisabledDt = null } state?.autoDisabled = myDis if(upd) { app.update() } } static Integer defaultAutomationTime() { return 5 } void scheduleAutomationEval(Integer schedtime = defaultAutomationTime()) { Integer theTime = schedtime if(theTime < defaultAutomationTime()) { theTime = defaultAutomationTime() } String autoType = getAutoType() def random = new Random() Integer random_int = random.nextInt(6) // this randomizes a bunch of automations firing at same time off same event Boolean waitOverride = false switch(autoType) { case "chart": if(theTime == defaultAutomationTime()) { theTime += random_int } Integer schWaitVal = settings?.schMotWaitVal?.toInteger() ?: 60 if(schWaitVal > 120) { schWaitVal = 120 } Integer t0 = getAutoRunSec() if((schWaitVal - t0) >= theTime ) { theTime = (schWaitVal - t0) waitOverride = true } //theTime = Math.min( Math.max(theTime,defaultAutomationTime()), 120) break } if(!state.evalSched) { runIn(theTime, "runAutomationEval", [overwrite: true]) state.autoRunInSchedDt = getDtNow() state.evalSched = true state.evalSchedLastTime = theTime } else { String str = "scheduleAutomationEval: " Integer t0 = state.evalSchedLastTime if(t0 == null) { t0 = 0 } Integer timeLeftPrev = t0 - getAutoRunInSec() if(timeLeftPrev < 0) { timeLeftPrev = 100 } String str1 = " Schedule change: from (${timeLeftPrev}sec) to (${theTime}sec)" if(timeLeftPrev > (theTime + 5) || waitOverride) { if(Math.abs(timeLeftPrev - theTime) > 3) { runIn(theTime, "runAutomationEval", [overwrite: true]) LogTrace(str+'Performing'+str1) state.autoRunInSchedDt = getDtNow() state.evalSched = true state.evalSchedLastTime = theTime } } else { LogTrace(str+'Skipping'+str1) } } } Integer getAutoRunSec() { return !state.autoRunDt ? 100000 : GetTimeDiffSeconds((String)state.autoRunDt, null, "getAutoRunSec").toInteger() } Integer getAutoRunInSec() { return !state.autoRunInSchedDt ? 100000 : GetTimeDiffSeconds((String)state.autoRunInSchedDt, null, "getAutoRunInSec").toInteger() } void runAutomationEval() { LogTrace("runAutomationEval") Long execTime = now() String autoType = getAutoType() state.evalSched = false state.evalSchedLastTime = null state.autoRunInSchedDt = null switch(autoType) { case "chart": if(settings.energyDevice) { getSomeData(settings.energyDevice) } break default: LogAction("runAutomationEval: Invalid Option Received ${autoType}", "warn", true) break } storeExecutionHistory((now()-execTime), "runAutomationEval") } String getCurAppLbl() { return (String)app.label } static String appLabel() { return "Solar Graphs" } static String appName() { return appLabel() } String getAutoTypeLabel() { //LogTrace("getAutoTypeLabel()") String type = state?.autoTyp String appLbl = getCurAppLbl() String newName = appName() == appLabel() ? "NST Graphs" : appName() String typeLabel = "" def newLbl String dis = getIsAutomationDisabled() ? "\n(Disabled)" : "" typeLabel = "Solar Location ${location.name} Graphs" //Logger("getAutoTypeLabel: ${type} ${appLbl} ${appName()} ${appLabel()} ${typeLabel}") if(appLbl != "" && appLbl && appLbl != "Solar Graphs" && appLbl != "${appLabel()}") { if(appLbl?.contains("\n(Disabled)")) { newLbl = appLbl?.replaceAll('\\\n\\(Disabled\\)', '') } else { newLbl = appLbl } } else { newLbl = typeLabel } return newLbl+dis } //ERS void checkCleanups() { def inuse = [] theDev = settings?.energyDevice if(theDev) { inuse += theDev.id } def data = [] def regex1 = /Wtoday/ ["Wtoday"]?.each { oi-> state?.each { if(it.key.toString().startsWith(oi)) { data?.push(it.key.replaceAll(regex1, "")) } } } def regex2 = /thermStor/ ["thermStor"]?.each { oi-> state?.each { if(it.key.toString().startsWith(oi)) { data?.push(it.key.replaceAll(regex2, "")) } } } //Logger("data is ${data}") def toDelete = data.findAll { !inuse.contains(it) } //Logger("toDelete is ${toDelete}") toDelete?.each { item -> cleanState(item.toString()) } } void cleanState(id) { LogTrace("cleanState: ${id}") stateRemove("Wtoday${id}") stateRemove("WhumTblYest${id}") stateRemove("WdewTblYest${id}") stateRemove("WtempTblYest${id}") stateRemove("WhumTbl${id}") stateRemove("WdewTbl${id}") stateRemove("WtempTbl${id}") stateRemove("today${id}") stateRemove("thermStor${id}") stateRemove("tempTblYest${id}") stateRemove("tempTbl${id}") stateRemove("oprStTblYest${id}") stateRemove("oprStTbl${id}") stateRemove("humTblYest${id}") stateRemove("humTbl${id}") stateRemove("hspTblYest${id}") stateRemove("hspTbl${id}") stateRemove("cspTblYest${id}") stateRemove("cspTbl${id}") stateRemove("fanTblYest${id}") stateRemove("fanTbl${id}") } static String sectionTitleStr(title) { return "

$title

" } static String inputTitleStr(title) { return "$title" } static String pageTitleStr(title) { return "

$title

" } static String paraTitleStr(title) { return "$title" } static String imgTitle(imgSrc, titleStr, color=null, imgWidth=30, imgHeight=null) { def imgStyle = "" imgStyle += imgWidth ? "width: ${imgWidth}px !important;" : "" imgStyle += imgHeight ? "${imgWidth ? " " : ""}height: ${imgHeight}px !important;" : "" if(color) { return """
${titleStr}
""" } else { return """ ${titleStr}""" } } static String icons(String name, String napp="App") { def icon_names = [ "i_dt": "delay_time", "i_not": "notification", "i_calf": "cal_filter", "i_set": "settings", "i_sw": "switch_on", "i_mod": "mode", "i_hmod": "hvac_mode", "i_inst": "instruct", "i_err": "error", "i_cfg": "configure", "i_t": "temperature" //ERS ] String t0 = icon_names?."${name}" //LogAction("t0 ${t0}", "warn", true) if(t0) return "https://raw.githubusercontent.com/${gitPath()}/Images/$napp/${t0}_icon.png".toString() else return "https://raw.githubusercontent.com/${gitPath()}/Images/$napp/${name}".toString() } static String gitRepo() { return "tonesto7/nest-manager"} static String gitBranch() { return "master" } static String gitPath() { return "${gitRepo()}/${gitBranch()}".toString()} String getAppImg(imgName, Boolean on=null) { return (!disAppIcons || on) ? icons(imgName) : "" } String getDevImg(imgName, Boolean on=null) { return (!disAppIcons || on) ? icons(imgName, "Devices") : "" } void logsOff() { log.warn "debug logging disabled..." settingUpdate("showDebug", "false", "bool") settingUpdate("advAppDebug", "false", "bool") } def getSettingsData() { def sets = [] settings?.sort().each { st -> sets << st } return sets } def getSettingVal(String var) { return settings[var] ?: null } def getStateVal(String var) { return state[var] ?: null } void settingUpdate(String name, value, String type=null) { //LogTrace("settingUpdate($name, $value, $type)...") if(name) { if(value == "" || value == null || value == []) { settingRemove(name) return } } if(name && type) { app?.updateSetting("$name", [type: "$type", value: value]) } else if (name && type == null) { app?.updateSetting(name.toString(), value) } } void settingRemove(String name) { //LogTrace("settingRemove($name)...") if(name) { app?.clearSetting(name.toString()) } } def stateUpdate(String key, value) { if(key) { state."${key}" = value; return true } //else { LogAction("stateUpdate: null key $key $value", "error", true); return false } } void stateRemove(key) { //if(state?.containsKey(key)) { state.remove(key?.toString()) } state.remove(key?.toString()) } String getAutomationType() { return state?.autoTyp ?: null } String getAutoType() { return getAutomationType() } Boolean getIsAutomationDisabled() { Boolean dis = state.autoDisabled return (dis != null && dis) } def renderDeviceTiles(String type=null, theDev=null) { String devHtml = "" String navHtml = "" String scrStr = "" def allDevices = [] if(theDev) { allDevices << theDev } else { allDevices << settings.energyDevice } def devices = allDevices Integer devNum = 1 def myType = type ?: "Envoy Device" devices.sort {it.getLabel()}.each { dev -> def navMap = [:] Boolean hasHtml = true // (dev?.hasHtml() == true) if( ( (dev?.typeName in ["Enlighten Envoy (local)"]) && (hasHtml && !type) || (hasHtml && type && dev?.typeName == type)) ) { LogTrace("renderDeviceTiles: ${dev.id} ${dev.name} ${theDev?.name} ${dev.typeName}") navMap = ["key":dev?.getLabel(), "items":[]] def navItems = navHtmlBuilder(navMap, devNum) String myTile = getSDeviceTile(devNum, dev) if(navItems?.html) { navHtml += navItems?.html } if(navItems?.js) { scrStr += navItems?.js } devHtml += """

${dev?.getLabel()}

${myTile}
""" devNum = devNum+1 } } String html = """ ${getWebHeaderHtml(myType, true, true, true, true)}
${devHtml}
""" render contentType: "text/html", data: html } Map navHtmlBuilder(navMap, idNum) { Map res = [:] String htmlStr; jsStr htmlStr = "" jsStr = "" if(navMap?.key) { htmlStr += """ """ res["html"] = htmlStr res["js"] = jsStr return res } static String navJsBuilder(String btnId, String divId) { String res = """ \$("#${btnId}").click(function() { \$("html, body").animate({scrollTop: \$("#${divId}").offset().top - hdrHeight - 20},500); closeNavMenu(); toggleMenuBtn(); }); """ return "\n${res}" } String getSDeviceTile(Integer devNum, dev) { //Logger("W1") String updateAvail = !state.updateAvailable ? "" : """
Device Update Available!
""" String clientBl = state?.clientBl ? """
Your Manager client has been blacklisted!\nPlease contact the Nest Manager developer to get the issue resolved!!!
""" : "" //Logger("W2") def energyStr = dev.currentState("energy").value def efficiencyToday = dev.currentState("efficiency").value def energyYesterday, efficiencyYesterday, energyLast7Days, efficiencyLast7Days energyYesterday = 0 efficiencyYesterday = 0 energyLast7Days = 0 efficiencyLast7Days = 0 if (dev.currentState("energy_yesterday") != null){ energyYesterday = dev.currentState("energy_yesterday").value efficiencyYesterday = dev.currentState("efficiency_yesterday").value } if (dev.currentState("energy_last7days") != null){ energyLast7Days = dev.currentState("energy_last7days").value efficiencyLast7Days = dev.currentState("efficiency_last7days").value } def energyLife = dev.currentState("energy_life").value def efficiencyLifetime = dev.currentState("efficiency_lifetime")?.value //

Event History

//

${location.name}

String mainHtml = """

Solar Conditions

Energy Today: ${energyStr} kWh
Efficiency Today: ${efficiencyToday} kWh/kW
Energy Yesterday: ${energyYesterday} kWh
Efficiency Yesterday: ${efficiencyYesterday} kWh/kW
Energy Last 7 days: ${energyLast7Days} kWh
Efficiency Last 7 days: ${efficiencyLast7Days} kWh/kW
Lifetime Energy: ${energyLife} MWh
Lifetime Efficiency: ${efficiencyLifetime} kWh/kW
${historyGraphHtml(devNum,dev)}
""" // render contentType: "text/html", data: mainHtml, status: 200 } String historyGraphHtml(Integer devNum, dev) { //Logger("HistoryG 1") String html html = "" if(true) { //Logger("HistoryG 2") html = """

Event History

""" } return html } String getWebHeaderHtml(String title, Boolean clipboard=true, Boolean vex=false, Boolean swiper=false, Boolean charts=false) { String html = """ Envoy Solar Charts ('${location.name}') - ${title} """ html += clipboard ? """""" : "" html += vex ? """""" : "" html += swiper ? """""" : "" html += vex ? """""" : "" html += vex ? """""" : "" html += swiper ? """""" : "" html += charts ? """""" : "" html += vex ? """""" : "" return html } String hideChartHtml() { String data = """

Event History


Waiting for more data to be collected...

This may take a few hours

""" return data } String getDataString(Integer seriesIndex) { String dataString; dataString = "" List dataTable; dataTable = [] switch (seriesIndex) { case 1: dataTable = (List)state.energyTableYesterday break case 2: dataTable = (List)state.powerTableYesterday break case 3: dataTable = (List)state.energyTable break case 4: dataTable = (List)state.powerTable break } LogTrace("getDataString: ${seriesIndex}, ${dataTable?.size()}") dataTable.each() { List dataArray = [[it[0],it[1],0],null,null,null,null] dataArray[seriesIndex] = it[2] dataString += dataArray.toString() + "," } return dataString } void getSomeData(dev, Boolean devpoll = false) { // LogTrace("getSomeData ${app} ${dev.label} ${dev.id}") def energyToday = dev.currentState("energy").value.toFloat() /* def energyYesterday = dev.currentState("energy_yesterday").value def energyLast7Days = dev.currentState("energy_last7days").value def energyLife = dev.currentState("energy_life").value */ Integer currentPower = dev.currentState("power").value.toInteger() /* def efficiencyToday = dev.currentState("efficiency").value def efficiencyYesterday = dev.currentState("efficiency_yesterday").value def efficiencyLast7Days = dev.currentState("efficiency_last7days").value def efficiencyLifetime = dev.currentState("efficiency_lifetime")?.value */ Integer numInverters = dev.currentState("numInverters").value.toInteger() Integer inverterSize = dev.currentState("inverterSize").value.toInteger() Integer panelSize = dev.currentState("panelSize").value.toInteger() state.maxPower = numInverters * inverterSize Date today = new Date() String todayDay = today.format("dd",location.timeZone) Integer hr = today.format("H", location.timeZone) as Integer Integer mins = today.format("m", location.timeZone) as Integer // LogTrace("getSomeData: ${today} ${todayDay} ${dev.id}") List powerTable, energyTable powerTable = (List)state?.powerTable energyTable = (List)state?.energyTable if (!state.today || state.today != todayDay) { state.peakpower = currentPower state.today = todayDay state.powerTableYesterday = powerTable state.energyTableYesterday = energyTable powerTable = [] energyTable = [] state.powerTable = powerTable state.energyTable = energyTable state.lastPower = 0 } Integer previousPower = state.lastPower != null ? state.lastPower : currentPower Integer powerChange = currentPower - previousPower state.lastPower = currentPower if (state.peakpower <= currentPower) { state.peakpower = currentPower state.peakpercentage = (100*state.peakpower/state.maxPower).toFloat() } // get power data for yesterday and today so we can create a graph if (state.powerTableYesterday == null || state.energyTableYesterday == null || powerTable == null || energyTable == null) { Date startOfToday = timeToday("00:00", location.timeZone) def newValues if (state.powerTableYesterday == null || state.energyTableYesterday == null) { log.trace "Querying DB for yesterday's data…" List dataTable; dataTable = [] List powerData powerData = [] //device.statesBetween("power", startOfToday - 1, startOfToday, [max: 288]) // 24h in 5min intervals should be more than sufficient… // work around a bug where the platform would return less than the requested number of events (as of June 2016, only 50 events are returned) if (powerData.size()) { /* while ((newValues = [:] //device.statesBetween("power", startOfToday - 1, powerData.last().date, [max: 288])).size()) { powerData += newValues } powerData.reverse().each() { dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue]) } */ } state.powerTableYesterday = dataTable dataTable = [] Map energyData energyData = [:] //device.statesBetween("energy", startOfToday - 1, startOfToday, [max: 288]) if (energyData.size()) { /* while ((newValues = [:] //device.statesBetween("energy", startOfToday - 1, energyData.last().date, [max: 288])).size()) { energyData += newValues } // we drop the first point after midnight (0 energy) in order to have the graph scale correctly energyData.reverse().drop(1).each() { dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue]) } */ } state.energyTableYesterday = dataTable } if (powerTable == null || energyTable == null) { log.trace "Querying DB for today's data…" powerTable = [] List powerData powerData = [] //device.statesSince("power", startOfToday, [max: 288]) if (powerData.size()) { /* while ((newValues = [:] //device.statesBetween("power", startOfToday, powerData.last().date, [max: 288])).size()) { powerData += newValues } powerData.reverse().each() { powerTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue]) } */ } energyTable = [] Map energyData energyData = [:] //device.statesSince("energy", startOfToday, [max: 288]) if (energyData.size()) { /* while ((newValues = [:] //device.statesBetween("energy", startOfToday, energyData.last().date, [max: 288])).size()) { energyData += newValues } energyData.reverse().drop(1).each() { energyTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue]) } */ } } } // add latest power & energy readings for the graph if (currentPower > 0 || powerTable.size() != 0) { state.powerTable = addValue(powerTable, hr, mins, currentPower) state.energyTable = addValue(energyTable, hr, mins, energyToday) } } private cast(value, dataType) { switch(dataType) { case "number": if (value == null) return (Integer)0 if (value instanceof String) { if (value.isInteger()) return value.toInteger() if (value.isFloat()) return (Integer) Math.floor(value.toFloat()) if (value in trueStrings) return (Integer) 1 } def result result = (Integer)0 try { result = (Integer)value } catch(ignored) { result = (Integer)0 } return result ? result : (Integer) 0 case "decimal": if (value == null) return (Float) 0 if (value instanceof String) { if (value.isFloat()) return (Float) value.toFloat() if (value.isInteger()) return (Float) value.toInteger() if (value in trueStrings) return (Float) 1 } def result = (Float) 0 try { result = (Float) value } catch(ignored) { } return result ? result : (Float) 0 } } //ERS String getAppEndpointUrl(String subPath) { return "${getFullApiServerUrl()}${subPath ? "/${subPath}" : ""}?access_token=${state.access_token}".toString() } String getLocalEndpointUrl(String subPath) { return "${getFullLocalApiServerUrl()}${subPath ? "/${subPath}" : ""}?access_token=${state.access_token}".toString() } Boolean getAccessToken() { try { if(!state.access_token) { state.access_token = createAccessToken() } return true } catch (ex) { String msg = "Error: OAuth is not Enabled for ${app?.name}!." // sendPush(msg) log.error "getAccessToken Exception ${ex?.message}" LogAction("getAccessToken Exception | $msg", "warn", true) return false } } void enableOauth() { Map params = [ uri: "http://localhost:8080/app/edit/update?_action_update=Update&oauthEnabled=true&id=${app.appTypeId}", headers: ['Content-Type':'text/html;charset=utf-8'] ] try { httpPost(params) { resp -> //LogTrace("response data: ${resp.data}") } } catch (e) { log.debug "enableOauth something went wrong: ${e}" } } void resetAppAccessToken() { LogAction("Resetting getAppDebugDesc Access Token....", "info", true) revokeAccessToken() state.access_token = null if(getAccessToken()) { LogAction("Reset App Access Token... Successful", "info", true) // settingUpdate("resetAppAccessToken", "false", "bool") } } static List addValue(List table, hr, mins, val) { List newTable newTable = table if(table?.size() > 2) { def last = table.last()[2] def secondtolast = table[-2][2] if(val == last && val == secondtolast) { newTable = table.take(table.size() - 1) } } newTable.add([hr, mins, val]) return newTable } Integer getStartTime(String tbl1, String tbl2) { Integer startTime startTime = 24 LogTrace("tbl1: ${state."tbl1"?.size()} tbl2: ${state."tbl2"?.size()} ") if (state."${tbl1}"?.size()) { startTime = ((List)state."${tbl1}").min{it[0].toInteger()}[0].toInteger() } if (state."${tbl2}"?.size()) { startTime = Math.min(startTime, ((List)state."${tbl2}").min{it[0].toInteger()}[0].toInteger()) } return startTime } static String hideWeatherHtml() { String data data = """



The Required Weather data is not available yet...



Please refresh this page after a couple minutes...





""" // render contentType: "text/html", data: data, status: 200 } TimeZone getTimeZone() { TimeZone tz tz = null if(location?.timeZone) { tz = (TimeZone)location?.timeZone } if(!tz) { LogAction("getTimeZone: Hub or Nest TimeZone not found", "warn", true) } return tz } String getDtNow() { Date now = new Date() return formatDt(now) } String formatDt(Date dt) { SimpleDateFormat tf = new SimpleDateFormat("E MMM dd HH:mm:ss z yyyy") if(getTimeZone()) { tf.setTimeZone(getTimeZone()) } else { LogAction("HE TimeZone is not set; Please open your location and Press Save", "warn", true) } return tf.format(dt) } Integer GetTimeDiffSeconds(String strtDate, String stpDate=(String)null, String methName=(String)null) { //LogTrace("[GetTimeDiffSeconds] StartDate: $strtDate | StopDate: ${stpDate ?: "Not Sent"} | MethodName: ${methName ?: "Not Sent"})") if((strtDate && !stpDate) || (strtDate && stpDate)) { //if(strtDate?.contains("dtNow")) { return 10000 } Date now = new Date() String stopVal = stpDate ? stpDate.toString() : formatDt(now) Long start = Date.parse("E MMM dd HH:mm:ss z yyyy", strtDate).getTime() Long stop = Date.parse("E MMM dd HH:mm:ss z yyyy", stopVal).getTime() Integer diff = (Integer) ((stop - start) / 1000L) LogTrace("[GetTimeDiffSeconds] Results for '$methName': ($diff seconds)") return diff } else { return null } } /************************************************************************************************ | LOGGING AND Diagnostic | *************************************************************************************************/ void LogTrace(String msg, logSrc=null) { Boolean trOn = (showDebug && advAppDebug) if(trOn) { Logger(msg, "trace") } } void LogAction(String msg, String type="debug", Boolean showAlways=false, logSrc=null) { Boolean isDbg = showDebug if(showAlways || isDbg) { Logger(msg, type) } } void Logger(String msg, String type="debug", logSrc=null, Boolean noSTlogger=false) { if(msg && type) { String labelstr = "" if(state.dbgAppndName == null) { Boolean tval = parent ? parent.getSettingVal("dbgAppndName") : settings?.dbgAppndName state.dbgAppndName = (tval || tval == null) } String t0 = app.label if(state.dbgAppndName) { labelstr = app.label+' | ' } String themsg = "${labelstr}${msg}".toString() //log.debug "Logger remDiagTest: $msg | $type | $logSrc" if(!noSTlogger) { switch(type) { case "debug": log.debug themsg break case "info": log.info '| '+themsg break case "trace": log.trace '| '+themsg break case "error": log.error '| '+themsg break case "warn": log.warn '|| '+themsg break default: log.debug themsg break } } } else { log.error "${labelstr}Logger Error - type: ${type} | msg: ${msg} | logSrc: ${logSrc}".toString() } }