/* * Hub Information Display * * * Licensed Virtual 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, WIyTHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Date Who Description * ------------- ------------------- --------------------------------------------------------- * 23Mar2025 thebearmay v0.0.1 - Original code * 04Apr2025 v0.1.0 - Beta Ready Code * 05Apr2025 v0.1.1 - Freememory fix for MB * 07Apr2025 v0.1.2 - Add Memory History * v0.1.3 - Fix graph sizes * 08Apr2025 v0.1.4 - Change Windy URL * 10Apr2025 v0.1.5 - Add CPU Temperature chart * 12May2025 v0.1.6 - Add Full Screen option * 25May2025 v0.1.7 - Change capability to device.HubInformationDriverv3 * 19Dec2025 v0.1.8 - Trap the start up error when the Hub Info device hasn't fully populated */ static String version() { return '0.1.8' } import groovy.json.JsonSlurper import groovy.json.JsonOutput import groovy.transform.Field //include thebearmay.uiInputElements definition ( name: "Hub Detailed Information Display", namespace: "thebearmay", author: "Jean P. May, Jr.", description: "App to combine the Hub Details page with information collected by the Hub Information Driver v3", category: "Utility", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/hubInfoDisp.groovy", installOnOpen: true, oauth: false, iconUrl: "", iconX2Url: "" ) preferences { page name: "pageRender" page name: "configPage" } mappings { /* path("/refresh") { action: [POST: "refresh", GET: "refresh"] } */ } def installed() { // log.trace "installed()" state?.isInstalled = true initialize() } def updated(){ // log.trace "updated()" if(!state?.isInstalled) { state?.isInstalled = true } if(debugEnabled) runIn(1800,logsOff) } def initialize(){ } void logsOff(){ app.updateSetting("debugEnabled",[value:"false",type:"bool"]) } def configPage(){ dynamicPage (name: "configPage", title: "

${app.getLabel()} v${version()}

", install: true, uninstall: true) { section (name:'cPageHndl', title:'Configuration Page'){ db = getInputElemStr(name:'debugEnabled', type:'bool', width:'15em', radius:'12px', background:'#e6ffff', title:'Debug Enabled', defaultValue: "${settings['debugEnabled']}") /* if(state.accessToken == null) createAccessToken() apiSection = getInputElemStr(name:'api', type:'divHide', width:'15em', radius:'12px', background:'#669999', title:'API Information', divName:'apiSection', hidden:true) String pStr = "

Local Server API: ${getFullLocalApiServerUrl()}/refresh?access_token=${state.accessToken}

" pStr+="

Cloud Server API: ${getFullApiServerUrl()}/refresh?access_token=${state.accessToken}

" pStr+="

Access Token: ${state.accessToken}

" paragraph "${apiSection}${pStr}" */ fs = getInputElemStr(name:'fullScreen', type:'bool', width:'15em', radius:'12px', background:'#e6ffff', title:'Display Full Screen', defaultValue: "${settings['fullScreen']}") fileList = getFiles() state.jsInstalled = false fileList.each { if("$it" == "chart.js") state.jsInstalled = true } if(!state?.jsInstalled) paragraph getInputElemStr(name:'jsInstall', type:'button', width:'15em', radius:'12px', background:'#e6ffff', title:'Install ChartJS') String sDev = getInputElemStr(name:'selectedDev', type:'device.HubInformationDriverv3', width:'15em', radius:'12px', background:'#e6ffff', title:'Select Hub Info Device') String aRename = getInputElemStr(name:"nameOverride", type:"text", title: "New Name for Application", multiple: false, defaultValue: app.getLabel(), width:'15em', radius:'12px', background:'#e6ffff') paragraph "
${db}${fs}${aRename}
${sDev}
" if(selectedDev) { state.configured = true ci = btnIcon(name:'computer', size:'14px') paragraph getInputElemStr(name:"pRender", type:'href', title:"${ci} Hub Information", destPage:'pageRender', width:'10em', radius:'15px', background:'#669999') } else state.configured = false if(nameOverride != app.getLabel()) app.updateLabel(nameOverride) } } } def pageRender(){ dynamicPage (name: "pageRender", title: "

${app.getLabel()} v${version()}

", install: false, uninstall: false) { section(name:"visualDisp",title:"", hideable: false, hidden: false){ if(!state.configured) configPage() else { //log.debug "${state.lastLoad} : ${new Date().getTime() - (5*60*1000)} ${state.lastLoad < (new Date().getTime() - (5*60*1000))}" if(state.lastLoad) { tNow = new Date().getTime() if(state.lastLoad < (tNow - (5*60*1000))){ settings.each { if(it.key != 'debugEnabled' && it.key != 'selectedDev' && it.key != 'nameOverride' && it.key !='fullScreen') app.removeSetting("${it.key}") } } } state.lastLoad = new Date().getTime() paragraph "${buildPage()}" if(state.hiRefresh) { state.hiRefresh = false settings.each { if(it.key != 'debugEnabled' && it.key != 'selectedDev' && it.key != 'nameOverride' && it.key !='fullScreen') app.removeSetting("${it.key}") } selectedDev.refresh() paragraph "" } if(state.saveBasic){ state.saveBasic = false saveBaseData() } } } } } ArrayList getFiles(){ fileList =[] params = [ uri : "http://127.0.0.1:8080", path : "/hub/fileManager/json", headers: [ accept : "application/json" ], ] httpGet(params) { resp -> resp.data.files.each { fileList.add(it.name) } } return fileList.sort() } def appButtonHandler(btn) { switch(btn) { case "jsInstall": fetchJS() state.jsInstalled = true break case "clearOptions": state.clearReq = true break case 'getRefresh': state.hiRefresh = true break case 'saveBasic': state.saveBasic = true break default: log.error "Undefined button $btn pushed" break } } HashMap jsonResponse(retMap){ return JsonOutput.toJson(retMap) } String buildPage(){ HashMap hubDataMap = getHubJson() String basicData = buildBase(hubDataMap) String c1 = '

CPU Percentage Not Available - check Hub Info Device

' String c2 = '

Free Memory Not Available - check Hub Info Device

' String c3 = '

Hub Temperature Not Available - check Hub Info Device

' if(selectedDev.currentValue('cpuPct') != null) c1 = buildChart([attrSelect:'cpuPct',cList:['\"#0efb1c\"','\"#fdf503\"','\"#fd0a03\"'],wList:[8,20,100],i:0]) else c1 = '

CPU Percentage Not Available - check Hub Info Device

' if(selectedDev.currentValue('freeMemory') != null){ String fmUnit = selectedDev.currentState('freeMemory')?.unit if(fmUnit == 'MB') cScale = 1 else if(fmUnit == 'GB') cScale = 0.001 else cScale = 1000 c2 = buildChart([attrSelect:'freeMemory',cList:['\"#fd0a03\"','\"#fdf503\"','\"#0efb1c\"'],wList:[100,200,2000], scale:cScale, i:1]) } else c2 = '

Free Memory Not Available - check Hub Info Device

' if(selectedDev.currentValue('temperature') != null){ String tUnit = selectedDev.currentState('temperature')?.unit if(tUnit.contains('C')){ c3 = buildChart([attrSelect:'temperature',cList:['\"#0efb1c\"','\"#fdf503\"','\"#fd0a03\"'],wList:[65,80,104],i:2]) }else { c3 = buildChart([attrSelect:'temperature',cList:['\"#0efb1c\"','\"#fdf503\"','\"#fd0a03\"'],wList:[150,176,220],i:2]) } } else c3 = '

Hub Temperature Not Available - check Hub Info Device

' String t1 = buildTrend() String lat = selectedDev.currentValue('latitude') String lon = selectedDev.currentValue('longitude') //String ifrm = "" String ifrm = "" String aToF = getAttr('a'..'f') String gToP = getAttr('g'..'p') String qToZ = getAttr('q'..'z') String headDiv = getInputElemStr(name:'headDiv', type:'divHide', width:'5em', radius:'12px', background:'#669999', title:'Charts', divName:'chartSection', hidden:false) String basicDivB = getInputElemStr(name:'basicDiv', type:'divHide', width:'5em', radius:'12px', background:'#669999', title:'Basic', divName:'basicDiv', hidden:false) String aToFdivB = getInputElemStr(name:'aToFdiv', type:'divHide', width:'5em', radius:'12px', background:'#669999', title:'A-F', divName:'aToFdiv', hidden:true) String gToPdivB = getInputElemStr(name:'gToPdiv', type:'divHide', width:'5em', radius:'12px', background:'#669999', title:'G-P', divName:'gToPdiv', hidden:true) String qToZdivB = getInputElemStr(name:'qToZdiv', type:'divHide', width:'5em', radius:'12px', background:'#669999', title:'Q-Z', divName:'qToZdiv', hidden:true) String hiRefreshBtn = getInputElemStr(name:'getRefresh', type:'button', width:'5em', radius:'12px', background:'#669999', title:'Refresh') String cPage = getInputElemStr(name:'cPage',type:'href', radius:'15px', width:'2em',title:"settings_applications", destPage:'configPage') String fullScrn = "" String pContent = "
" pContent += "
${hubDataMap.hubName}
${headDiv}${basicDivB}${aToFdivB}${gToPdivB}${qToZdivB} ${hiRefreshBtn}${cPage}
" pContent += "" pContent += "
${c1} ${c2} ${c3}
${t1}${ifrm}
" pContent += genClockWidget(hubDataMap.timeFormat) pContent += "
${basicData}
" pContent += "
${aToF}
" pContent += "
${gToP}
" pContent += "
${qToZ}
" pContent += "" pContent += "
${headDiv}${basicDivB}${aToFdivB}${gToPdivB}${qToZdivB} ${hiRefreshBtn}${cPage}
" if(fullScreen) pContent += "${fullScrn}" return pContent } String buildBase(hubDataMap){ String tVal = "${hubDataMap.mdnsName}" if(settings['mdnsName']) tVal = settings['mdnsName'] String mDNS = getInputElemStr( [name:"mdnsName", type:"text", title:"mDNS Name", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}"]) tVal = "${hubDataMap.hubName}" if(settings['hubName']) tVal = settings['hubName'] String hubName = getInputElemStr( [name:"hubName", type:"text", title:"Hub Name", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}"]) tVal = "${hubDataMap.tempScale}" if(settings['temperatureScale']) tVal = settings['temperatureScale'] String tempScale = getInputElemStr( [name:"temperatureScale", type:"enum", title:"Temperature Scale", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}",options:["F","C"]]) tVal = "${hubDataMap.zipCode}" if(settings['zipCode']) tVal = settings['zipCode'] String zipCode = getInputElemStr( [name:"zipCode", type:"text", title:"Postal Code", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}"]) tVal = "${hubDataMap.latitude}" if(settings['latitude']) tVal = settings['latitude'] String latitude = getInputElemStr( [name:"latitude", type:"text", title:"Latitude", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}"]) tVal = "${hubDataMap.longitude}" if(settings['longitude']) tVal = settings['longitude'] String longitude = getInputElemStr( [name:"longitude", type:"text", title:"Longitude", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}"]) tVal = "${hubDataMap.timeFormat}" if(settings['clock']) tVal = settings['clock'] String timeFormat = getInputElemStr( [name:"clock", type:"enum", title:"Time Format", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}",options:["12","24"]]) tVal = "${hubDataMap.timeZone}" if(settings['timeZone']) tVal = settings['timeZone'] tzOpt = [] hubDataMap.timeZones.each{ tzOpt.add(["${it.id}":"${it.label}"]) } String timeZone = getInputElemStr( [name:"timeZone", type:"enum", title:"Time Zone", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}",options:tzOpt]) tVal = "${hubDataMap.ttsCurrent}" if(settings['voice']) tVal = settings['voice'] ttsOpt = [] hubDataMap.ttsVoices.each{ ttsOpt.add(["${it.id}":"${it.label}"]) } String ttsCurrent = getInputElemStr( [name:"voice", type:"enum", title:"Default text to speech (TTS) Voice", width:"15em", background:"#ADD8E6", radius:"15px",defaultValue:"${tVal}",options:ttsOpt]) String basicData ="" basicData += "" basicData += "" String bi = btnIcon([name:'save', size:'14px']) basicData += "
${hubName}${mDNS}${tempScale}
${zipCode}${latitude}${longitude}
${timeFormat}${timeZone}${ttsCurrent}
${getInputElemStr(name:'saveBasic', type:'button', width:'5em', radius:'12px', background:'#00cc00', title:"${bi} Save")}
" basicData += "" basicData += "" basicData += "" basicData += "" basicData += "" basicData += "" hubDataMap.users.each { String itRole = 'unknown' if(it.admin == true) itRole = 'admin' else itRole = 'user' basicData += "" } basicData += "
 
MAC Address ${hubDataMap.macAddress} Hub UID ${hubDataMap.hubUID}
Hub Registered ${hubDataMap.hubRegistered} IP Address ${hubDataMap.ipAddress}

Associated Emails
Email Role
${it.email} ${itRole}

" return basicData } void saveBaseData(){ bodyMap = [:] settings.each { //log.debug "${it.properties}" if(it.key != 'debugEnabled' && it.key != 'selectedDev' && it.key != 'nameOverride' && it.key != 'hubName') { bodyMap.put(it.key, "${it.value}") } else if(it.key == 'hubName') bodyMap.put('name', "${it.value}") } if(debugEnabled) log.debug bodyMap try{ params = [ uri: "http://127.0.0.1:8080/location/update", headers: [ "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" ], body:bodyMap ] if(debugEnabled) log.debug "$params" httpPost(params){ resp -> if(debugEnabled) log.debug "$resp.data" if(resp.data.message != null) log.error resp.data.message } }catch (e){ log.error "$e" } } String getAttr(aRange){ ArrayList attrList = [] selectedDev.supportedAttributes.each{ String attr = it.toString() if(aRange.contains(attr.substring(0,1)) && it.toString() != 'html' && it.toString() != 'type'){ tMap = [key:"${it}", value:"${selectedDev.currentValue("$it", true)}"] attrList.add(tMap) } } ArrayList attrSort = attrList.sort{ it.key } String retVal = '' Integer i=0 String prevKey = '' attrSort.each{ if( it.key != prevKey) { if(i==0) { retVal += "" i = 1 } else { retVal += "" i=0 } } prevKey = it.key } if(i==1) retVal += "" retVal+="
${it.key}: ${it.value} ${it.key}:${it.value}
" return retVal } String buildChart(opts) { ArrayList cList = opts.cList ArrayList wList = opts.wList attrSelect = opts.attrSelect if(!opts.scale || opts.scale == null) opts.scale = 1 i=opts.i valueScaled = selectedDev.currentValue(attrSelect)/opts.scale String visualRep = """

""" //if(debugEnabled) uploadHubFile ("pageBuildWork.txt",visualRep.toString().getBytes("UTF-8")) return(visualRep) } String getHistory(){ def params = [ uri: "http://127.0.0.1:8080", path: "/hub/advanced/freeOSMemoryHistory", contentType: "text/html", textParser: true ] try { httpGet(params) { resp -> if(resp!= null) { return """${resp.data}""" } else { log.error "getHistory - Null Response" return null } } } catch (exception) { log.error "getHistory Error: ${exception.message}" return null } } def buildTrend() { hData = getHistory() ArrayList tLbl = [] ArrayList mem = [] ArrayList jvm = [] ArrayList splitRec = [] String respData ArrayList tData = [] tData = hData.split('\n') Boolean firstRec = true tData.each{ splitRec = it.split(',') if(!firstRec){ tLbl.add("\'${splitRec[0]}\'") mem.add(splitRec[1]) if(cols > 3){ jvm.add(splitRec[4]) } } else { firstRec = false cols = splitRec.size() } } Integer i = 4 visualRep = """
""" if(debugEnabled) uploadHubFile ("pageBuildWork2.txt",visualRep.toString().getBytes("UTF-8")) return visualRep } def refresh(){ visualRep = buildPage() contentBlock = [ contentType: 'text/html', data: "$visualRep", gzipContent: true, status:200 ] render(contentBlock) } String genClockWidget(tFormat){ String htmlStr = """
Initializing...
""" } else { htmlStr += """ function updateClock() { const now = new Date(); let hours = checkTime(now.getHours()); const minutes = checkTime(now.getMinutes()); const seconds = checkTime(now.getSeconds()); const ampm = hours >= 12 ? ' pm' : ' am'; hours = hours % 12; hours = hours ? hours : 12; // the hour '0' should be '12' formattedTime = hours+':'+minutes+':'+seconds+ampm; return "Current Time "+formattedTime; } """ } //uploadHubFile("clockWidget.html",htmlStr.getBytes("UTF-8")) return htmlStr } HashMap getHubJson(){ def params = [ uri: 'http://127.0.0.1:8080/hub/details/json', contentType: "application/json" ] try { httpGet(params) { resp -> if(resp!= null) { return resp.data } else { log.error "Read Json - Null Response" return null } } } catch (exception) { log.error "Read JSON error ${exception.message}" } } String readExtFile(fName){ def params = [ uri: fName, contentType: "text/html", textParser: true ] try { httpGet(params) { resp -> if(resp!= null) { return """${resp.data}""" } else { log.error "Read External - Null Response" return null } } } catch (exception) { log.error "Read Ext Error: ${exception.message}" return null } } String insertJS(){ return """${new String(downloadHubFile('chart.js'))}""" } def fetchJS(){ jsFile = readExtFile("https://raw.githubusercontent.com/thebearmay/hubitat/main/libraries/chart.js") if(jsFile){ bArray = (jsFile.getBytes("UTF-8")) uploadHubFile("chart.js",bArray) } else log.error "chart.js not found" } String cBlock(cStr) { if(cStr !='yellow') return "$cStr" else return "$cStr" } @Field static String divStyle = 'style=\"padding:0;margin:0;background-color:#e6ffff;border-radius:12px;\"' @Field static ArrayList bandColor = ['violet','indigo','blue','green','yellow','orange','red'] /* * * Set of methods for UI elements * * * Licensed Virtual 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, WIyTHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Date Who Description * ---------- -------------- ------------------------------------------------------------------------- * 11Mar2025 thebearmay Add checkbox uiType, add trackColor and switchColor for type = bool * 13Mar2025 Added hoverText, code cleanup * 15Mar2025 Expand btnIcon to handle he- and fa- icons * 18Mar2025 Add btnDivHide to hide/display div's (uiType='divHide') * 03Apr2025 Enable a default value for enums * 04Apr2025 Size option for icons */ import groovy.transform.Field import java.text.SimpleDateFormat library ( base: "app", author: "Jean P. May Jr.", category: "UI", description: "Set of methods that allow the customization of the INPUT UI Elements", name: "uiInputElements", namespace: "thebearmay", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/libraries/uiInputElements.groovy", version: "0.0.7", documentationLink: "" ) /************************************************************************ * Note: If using hoverText, you must add $ttStyleStr to at least one * * element display * ************************************************************************/ String getInputElemStr(HashMap opt){ switch (opt.type){ case "text": return inputItem(opt) break case "number": return inputItem(opt) break case "decimal": return inputItem(opt) break case "date": return inputItem(opt) break case "time": return inputItem(opt) break case "password": return inputItem(opt) break case "color": return inputItem(opt) break case "enum": return inputEnum(opt) break case "mode": return inputEnum(opt) break case "bool": return inputBool(opt) break case "checkbox": return inputCheckbox(opt) break case "button": return buttonLink(opt) break case "icon": return btnIcon(opt) break case "href": return buttonHref(opt) break case "divHide": return btnDivHide(opt) break default: if(opt.type && (opt.type.contains('capability') || opt.type.contains('device'))) return inputCap(opt) else return "Type ${opt.type} is not supported" break } } String appLocation() { return "http://${location.hub.localIP}/installedapp/configure/${app.id}/" } /***************************************************************************** * Returns a string that will create an input element for an app - limited to * * text, password, number, date and time inputs currently * * * * HashMap fields: * * name - (required) name to store the input as a setting, no spaces or * * special characters * * type - (required) input type * * title - displayed description for the input element * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * multiple - true/ * * defaultValue - default for the field * * radius - CSS border radius value (rounded corners) * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputItem(HashMap opt) { if(!opt.name || !opt.type) return "Error missing name or type" if(settings[opt.name] != null){ if(opt.type != 'time') { opt.defaultValue = settings[opt.name] } else { SimpleDateFormat sdf = new SimpleDateFormat('HH:mm') SimpleDateFormat sdfIn = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") opt.defaultValue = sdf.format(sdfIn.parse(settings[opt.name])) } } typeAlt = opt.type if(opt.type == 'number') { step = ' step=\"1\" ' } else if (opt.type == 'decimal') { step = ' step=\"any\" ' typeAlt = 'number' } else { step = ' ' } String computedStyle = '' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(!opt.multiple) opt.multiple = false if(opt.hoverText && opt.hoverText != 'null'){ opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" } String retVal = "
" retVal+="
" retVal+="
" retVal+="
Save
" return retVal } /***************************************************************************** * Returns a string that will create an input capability element for an app * * * * HashMap fields: * * name - (required) name to store the input as a setting, no spaces or * * special characters * * type - (required) capability type, 'capability.' * * title - displayed description for the input element * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * multiple - true/ * * radius - CSS border radius value (rounded corners) * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputCap(HashMap opt) { String computedStyle = '' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize}" if(opt.radius) computedStyle += "border-radius:${opt.radius};" else opt.radius = '1px' if(!opt.multiple) opt.multiple = false String dList = '' String idList = '' int i=0 if(settings["${opt.name}"]){ ArrayList devNameId = [] settings["${opt.name}"].each{ devNameId.add([name:"${it.displayName}", devId:it.deviceId]) } ArrayList devNameIdSorted = devNameId.sort(){it.name} devNameIdSorted.each{ if(i>0) { dList +='
' idList += ',' } dList+="${it.name}" idList+="${it.devId}" i++ } } else { dList = 'Click to set' } String capAlt = opt.type.replace('.','') if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" String retVal = "
" retVal += "
"//${computedStyle} //retVal += "
" retVal += "" retVal += "
Update
" return retVal } /***************************************************************************** * Returns a string that will create an input enum or mode element for an app * * * * HashMap fields: * * name - (required) name to store the input as a setting, no spaces or * * special characters * * type - (required) capability type, * * title - displayed description for the input element * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * multiple - true/ * * options - list of values for the enum (modes will autofill) * * defaultValue - default for the field * * radius - CSS border radius value (rounded corners) * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputEnum(HashMap opt){ String computedStyle = '' if(opt.type == 'mode') opt.options = location.getModes() if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(!opt.multiple) { opt.multiple = false mult = ' ' } else { mult = 'multiple' } if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" String retVal = "
" retVal += "
" retVal += "" return retVal } /***************************************************************************** * Returns a string that will create an input boolean element for an app * * * * HashMap fields: * * name - (required) name to store the input as a setting, no spaces or * * special characters * * type - (required) capability type, * * title - displayed description for the input element * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * defaultValue - default for the field * * radius - CSS border radius value (rounded corners) * * trackColor - CSS color descriptor for the switch track * * switchColor - CSS color descriptor for the switch knob * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputBool(HashMap opt) { if(!opt.name || !opt.type) return "Error missing name or type" if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" String computedStyle = '' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(!opt.multiple) opt.multiple = false String trackColor = ' ' String switchColor = ' ' if(opt.trackColor) trackColor = "background-color:$opt.trackColor" if(opt.switchColor) switchColor = "background-color:$opt.switchColor" if(settings["${opt.name}"]) opt.defaultValue = settings["${opt.name}"] String retVal = "
" retVal += "" retVal+="
" retVal += "" retVal += "
" retVal += "" retVal += "
" return retVal } /***************************************************************************** * Returns a string that will create an input checkbox element for an app * * * * HashMap fields: * * name - (required) name to store the input as a setting, no spaces or * * special characters * * type - (required) capability type, * * title - displayed description for the input element * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * defaultValue - default for the field * * radius - CSS border radius value (rounded corners) * * cBoxColor - CSS color descriptor for the checkbox color * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputCheckbox(HashMap opt) { if(!opt.name || !opt.type) return "Error missing name or type" if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" opt.type = 'bool' String computedStyle = '' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(!opt.multiple) opt.multiple = false if(settings["${opt.name}"]) opt.defaultValue = settings["${opt.name}"] else opt.defaultValue = false if(!opt.cBoxColor) opt.cBoxColor = '#000000' String cbClass = 'he-checkbox-unchecked' if(opt.defaultValue) cbClass = 'he-checkbox-checked' String retVal = "
" retVal += "" retVal+="
" retVal += "" retVal += "
" retVal += "" retVal += "
" return retVal } /***************************************************************************** * Returns a string that will create an button element for an app * * * * HashMap fields: * * name - (required) name to identify the button, no spaces or * * special characters * * title - (required) button label * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * radius - CSS border radius descriptor (corner rounding) * * hoverText - Text to display as a tool tip * *****************************************************************************/ String buttonLink(HashMap opt) { //modified slightly from jtp10181's code if(!opt.name || !opt.title ) return "Error missing name or title" String computedStyle = 'cursor:pointer;text-align:center;box-shadow: 2px 2px 4px #71797E;' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" return "
${opt.title}
" } /***************************************************************************** * Returns a string that will create an button HREF element for an app * * * * HashMap fields: * * name - (required) name to identify the button, no spaces or * * special characters * * title - (required) button label * * destPage - (required unless using destUrl) name of the app page to go to * * destUrl - (required unless using destPage) URL for the external page * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * radius - CSS border radius descriptor (corner rounding) * *****************************************************************************/ String buttonHref(HashMap opt) { //modified jtp10181's code if(!opt.name || !opt.title ) return "Error missing name or title" if(!opt.destPage && !opt.destUrl) return "Error missing Destination info" String computedStyle = 'cursor:pointer;text-align:center;box-shadow: 2px 2px 4px #71797E;' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(opt.destPage) { inx = appLocation().lastIndexOf("/") dest = appLocation().substring(0,inx)+"/${opt.destPage}" } else if(opt.destUrl) { dest=opt.destUrl } if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" return "
${opt.title}
" } /***************************************************************************** * Returns a string that will create an button element to hide/display a div * * for an app * * HashMap fields: * * name - (required) name to identify the button, no spaces or * * special characters * * title - (required) button label * * divName - (require) name of the division to hide or display * * hidden - if true will hide the div immediately * * width - CSS descriptor for field width * * background - CSS color descriptor for the input background color * * color - CSS color descriptor for text color * * fontSize - CSS text size descriptor * * radius - CSS border radius descriptor (corner rounding) * *****************************************************************************/ String btnDivHide(HashMap opt) { if(!opt.name || !opt.title || !opt.divName) return "Error missing name, title or division" String computedStyle = 'cursor:pointer;box-shadow: 2px 2px 4px #71797E;' if(!opt.width) opt.width = '100%' computedStyle += "width:${opt.width};" if(opt.background) computedStyle += "background-color:${opt.background};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.fontSize) computedStyle += "font-size:${opt.fontSize};" if(opt.radius) computedStyle += "border-radius:${opt.radius};" if(opt.destPage) { inx = appLocation().lastIndexOf("/") dest = appLocation().substring(0,inx)+"/${opt.destPage}" } else if(opt.destUrl) { dest=opt.destUrl } String btnElem = "" String script= "" if(opt.hidden){ btnElem = "" script="" } opt.title = "${btnElem} ${opt.title}" if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon([name:'fa-circle-info'])}${opt.hoverText}
" return "$script
${opt.title}
" } /***************************************************************************** * Returns a string that will create an button icon element for an app from * * the materials-icon font * * * * name - (required) name of the icon to create * *****************************************************************************/ String btnIcon(HashMap opt) { //modified from jtp10181's code String computedStyle = ' ' if(opt.size) computedStyle += "font-size:${opt.size};" if(opt.color) computedStyle += "color:${opt.color};" if(opt.name.startsWith('he')) return "" else if(opt.name.startsWith('fa')) return ""//fa-circle-info else return "${opt.name}" } @Field static String ttStyleStr = "" @Field static String tableStyle = ""