/*******************************************************************
*** 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.7",
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("Please consider making a small donation to support the developers application via PayPal™.
" +
"Copyright \u00a9 2012-${currentYear} SandersSoft™ - All rights reserved.
")
}
}
public makeWebLink (link, label, hoverTitle="Click Me") {
// Check if a local path on the hub
if (link.startsWith('/')) {
link = "http://${location.hub.localIP}${link}"
}
hoverTitle="title='${hoverTitle}'"
def boxGraphic = " ${BOX_ARROW} "
def line = "${label}${boxGraphic}"
return line
}
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 "${str}"
}
String fmtDesc(String str) {
return "
${str}
"
}
def getImage(type) {
def loc = "
"
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 "
"
if(type == "title") return "${myText}
"
if (type.contains('-')) {
switch (type.split('-')[0]) {
case "button":
return "${myText}"
break
case "text":
return "${myText}"
break
case "header":
return "${myText}
"
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")
}