/******************************************************************* *** SanderSoft - Core App/Device Helpers *** /*******************************************************************/ library ( base: "app", author: "Kurt Sanders", category: "Apps", description: "Core functions for SanderSoft applications.", name: "SanderSoft-Library", namespace: "kurtsanders", documentationLink: "https://github.com/KurtSanders/", version: "0.0.5", disclaimer: "This core library is only for use with SanderSoft Apps and Drivers." ) import groovy.json.* import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.time.TimeCategory import groovy.transform.Field import hubitat.helper.RMUtils import java.text.DecimalFormat import java.text.SimpleDateFormat import java.util.Date import java.util.TimeZone import groovyx.net.http.HttpResponseException @Field static final String GITHUB_IMAGES_LINK = "https://raw.githubusercontent.com/kurtsanders/HubitatPackages/master/resources/images/" def uninstalled() { unschedule() removeChildDevices(getChildDevices()) } def returnVar(var) { def dataType = "String" def returnValue if (!(settings."${var}" == null)) { returnValue = settings."${var}" } if (!(state."${var}" == null)) { returnValue = state."${var}" } if (!(atomicState."${var}" == null)) { returnValue = atomicState."${var}" } def dateTest = returnValue =~ /\d\d\d\d-\d\d-\d\dT\d\d:/ if (dateTest) { dataType = "Date" } if (returnValue == "true") { dataType = "Boolean" } if (returnValue == "false") { dataType = "Boolean" } if (returnValue == true) { dataType = "Boolean" } if (returnValue == false) { dataType = "Boolean" } if (dataType == "Date") {returnValue = Date.parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", returnValue)} logDebug ("returnVar(${var}), DataType:${dataType}, Value: ${returnValue}") if (returnValue == null || returnValue == "") {} return returnValue } private removeChildDevices(delete) { delete.each {deleteChildDevice(it.deviceNetworkId)} } def displayPaypal() { section() { paragraph("<a href='https://www.paypal.com/donate/?hosted_button_id=E4WXT86RTPXDC'>Please consider making a small donation to support the developers application via PayPal™.</a><br>" + "<small><i>Copyright \u00a9 2012-${currentYear} SandersSoft™ - All rights reserved.</i></small><br>") } } public String convertToCurrentTimeZone(String dateStr) { TimeZone utc = TimeZone.getTimeZone("UTC"); SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); SimpleDateFormat destFormat = new SimpleDateFormat('EEE MMM d, h:mm a'); sourceFormat.setTimeZone(utc); Date convertedDate = sourceFormat.parse(dateStr); return destFormat.format(convertedDate); } //get the current time zone public String getCurrentTimeZone(){ TimeZone tz = Calendar.getInstance().getTimeZone(); return tz.getID(); } String nowFormatted(dtFormat=null) { dtFormat = dtFormat?dtFormat:'yyyy-MMM-dd h:mm:ss a' if(location.timeZone) return new Date().format(dtFormat, location.timeZone) else return new Date().format(dtFormat) } String fmtTitle(String str) { return "<strong>${str}</strong>" } String fmtDesc(String str) { return "<div style='font-size: 85%; font-style: italic; padding: 1px 0px 4px 2px;'>${str}</div>" } def getImage(type) { def loc = "<img src=" + GITHUB_IMAGES_LINK if(type == "Blank") return "${loc}blank.png height=40 width=5}>" if(type == "checkMarkGreen") return "${loc}checkMarkGreen2.png height=20 width=20>" if(type == "optionsGreen") return "${loc}options-green.png height=30 width=30>" if(type == "optionsRed") return "${loc}options-red.png height=30 width=30>" if(type == "instructions") return "${loc}instructions.png height=30 width=30>" if(type == "logo") return "${loc}logo.png height=40>" if(type == "qmark") return "${loc}question-mark-icon.png height=16>" if(type == "qmark2") return "${loc}question-mark-icon-2.jpg height=16>" if(type == "button-red") return "${loc}/button-red.png height=30 width=30>" if(type == "qmark") return "${loc}question-mark-icon.png height=16>" if(type == "qmark2") return "${loc}question-mark-icon-2.jpg height=16>" } def getFormat(type, myText="") { if(type == "line") return "<hr style='background-color:#1A77C9; height: 1px; border: 0;'>" if(type == "title") return "<h2 style='color:#1A77C9;font-weight: bold'>${myText}</h2>" if (type.contains('-')) { switch (type.split('-')[0]) { case "button": return "<a style='color:white;text-align:center;font-size:20px;font-weight:bold;background-color:${type.split('-')[1]};border:1px solid #000000;box-shadow:3px 4px #8B8F8F;border-radius:10px' href='${page}'>${myText}</a>" break case "text": return "<span style=color:${type.split('-')[1]}>${myText}</span>" break case "header": return "<div style='color:#ffffff;font-weight: bold;background-color:${type.split('-')[1]};border: 1px solid;box-shadow: 2px 3px #A9A9A9'>${myText}</div>" default: def error = "getFormat() contained a '-' but invalid/missing type" log.error error return error break } } } def sectionHeader(title){ return getFormat("header-blue", "${getImage("Blank")}"+" ${title}") } public static String formatSeconds(int timeInSeconds) { int hours = timeInSeconds / 3600; int secondsLeft = timeInSeconds - hours * 3600; int minutes = secondsLeft / 60; int seconds = secondsLeft - minutes * 60; String formattedTime = ""; if (hours < 10) formattedTime += "0"; formattedTime += hours + ":"; if (minutes < 10) formattedTime += "0"; formattedTime += minutes + ":"; if (seconds < 10) formattedTime += "0"; formattedTime += seconds ; return formattedTime; } //Logging Level Options @Field static final Map LOG_LEVELS = [0:"Off", 1:"Error", 2:"Warn", 3:"Info", 4:"Debug", 5:"Trace"] @Field static final Map LOG_TIMES = [0:"Indefinitely", 1:"1 Minute", 5:"5 Minutes", 10:"10 Minutes", 15:"15 Minutes", 30:"30 Minutes", 60:"1 Hour", 120:"2 Hours", 180:"3 Hours", 360:"6 Hours", 720:"12 Hours", 1440:"24 Hours"] @Field static final String LOG_DEFAULT_LEVEL = 0 //Call this function from within updated() and configure() with no parameters: checkLogLevel() void checkLogLevel(Map levelInfo = [level:null, time:null]) { //Set Defaults unschedule(logsOff) if (app) { if (settings.logLevel == null) app.updateSetting("logLevel",[value:LOG_DEFAULT_LEVEL, type:"enum"]) if (settings.logLevelTime == null) app.updateSetting("logLevelTime",[value:"0", type:"enum"]) } else { if (settings.logLevel == null) device.updateSetting("logLevel",[value:LOG_DEFAULT_LEVEL, type:"enum"]) if (settings.logLevelTime == null) device.updateSetting("logLevelTime",[value:"0", type:"enum"]) } //Schedule turn off and log as needed if (levelInfo.level == null) levelInfo = getLogLevelInfo() String logMsg = "Logging Level is: ${LOG_LEVELS[levelInfo.level]}" if (levelInfo.level >= 1 && levelInfo.time > 0) { logMsg += " for ${LOG_TIMES[levelInfo.time]}" logInfo "A 'logsOff' cron job has been scheduled in ${formatSeconds(60*levelInfo.time)}" runIn(60*levelInfo.time, logsOff, [overwrite: true]) } if (levelInfo.time == 0) logMsg += " (${LOG_TIMES[levelInfo.time]})" logInfo(logMsg) } void syncLogLevel(level, time) { logTrace "syncLogLevel(${level}, ${time})" logTrace "0) UpdateSetting ${device.displayName} logLevel:${level} and logLevelTime:${time}" device.updateSetting("logLevel",[value:"${level}", type:"enum"]) device.updateSetting("logLevelTime",[value:"${time}", type:"enum"]) checkLogLevel() getChildDevices().eachWithIndex { childDev, index -> logTrace "${index+1}) UpdateSetting ${childDev.displayName} logLevel:${level} and logLevelTime:${time}" childDev.updateSetting("logLevel",[value:"${level}", type:"enum"]) childDev.updateSetting("logLevelTime",[value:"${time}", type:"enum"]) logTrace "${index+1}) Calling ${childDev.displayName}.checkLogLevel()" childDev.checkLogLevel() } } //Function for optional command void setLogLevel(String levelName, String timeName=null) { Integer level = LOG_LEVELS.find{ levelName.equalsIgnoreCase(it.value) }.key Integer time = LOG_TIMES.find{ timeName.equalsIgnoreCase(it.value) }.key if (app) { app.updateSetting("logLevel",[value:"${level}", type:"enum"]) app.updateSetting("logLevelTime",[value:"${time}", type:"enum"]) } else { device.updateSetting("logLevel",[value:"${level}", type:"enum"]) device.updateSetting("logLevelTime",[value:"${time}", type:"enum"]) } checkLogLevel(level: level, time: time) } Map getLogLevelInfo() { Integer level = settings.logLevel as Integer ?: 0 Integer time = settings.logLevelTime as Integer ?: 0 return [level: level, time: time] } void logsOff() { logInfo "Logging auto disabled" setLogLevel("Off","Indefinitely") }