/* * HE Regions Shell - UI * * A lightweight UI shell for the HE environment featuring draggable, resizeable regions running the * hub's apps/devices/utilities. * * 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 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 * ------------- ------------------- --------------------------------------------------------- * 2026-04-23 thebearmay code cleanup, add app filter * 2026-04-24 About box enhancements using definitionData property */ import groovy.transform.Field //#include thebearmay.uiRegions String version() {return this.definitionData.version} String appDesc() {return this.definitionData.description} definition ( name: "HE Regions Shell - UI", namespace: "thebearmay", author: "Jean P. May, Jr.", description: "A lightweight UI shell for the HE environment featuring draggable, resizeable regions running the hub's apps/devices/utilities.", category: "Utility", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/heRegionShell.groovy", installOnOpen: true, oauth: false, iconUrl: "", iconX2Url: "", menu: "Apps", version: '0.0.3' ) preferences { page name: "_mainPage" //page name: "_appsList" } def installed() { state?.isInstalled = true initialize() } def updated(){ if(!state?.isInstalled) { state?.isInstalled = true } if(debugEnabled) runIn(1800,logsOff) } def initialize(){ } void logsOff(){ app.updateSetting("debugEnabled",[value:"false",type:"bool"]) } def _mainPage(){ //

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

dynamicPage (name: "_mainPage", title: "", install: true, uninstall: true) { section (name:'cPageHndl', title:''){ if(!settings.appFilter){ app.updateSetting('appFilter',[type:'enum',value:'All']) state.holdFilter = 'All' } //if(!state.regionMax) state.regionMax = 3 if(state.regionsList) regionsList = state.regionsList else regionsList = [:] if(!regionsList.containsKey('region-1')){ r1Content = _buildMenu() r1 = getRegion('region-1','Menu',"$r1Content") regionsList.put('region-1', r1) } if(state?.holdFilter != settings.appFilter) { state.special = 'applications' } if(state.special == 'applications'){ r2Content =_appsList() r2 = getRegion('region-2', 'Applications', "$r2Content") regionsList.put('region-2', r2) state.remove('special') } else if(state.special == 'about'){ if(!state.regionMax) state.regionMax = 3 regionMax = state.regionMax //regionsList = state.regionsList regionMax++ state.regionMax = regionMax rContent = "

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

" rContent += "

${appDesc()}

" r=getRegion("region-${regionMax}",'About',"$rContent") regionsList.put("region-${regionMax}", r) state.regionMax = regionMax state.remove('special') } else if(state.special) { log.debug "Unknown Special Request - ${state.special}" state.remove('special') } if(state.closeApp){ state.remove('closeApp') paragraph "" } if(state?.newAppLaunch > '0'){ regionsList = addAppRegion(state.newAppLaunch) state.remove('newAppLaunch') } if(closeReg != null) { removeRegion(closeReg) } if(state?.menuLaunch) { addMenuRegion(state.menuLaunch) state.remove('menuLaunch') } state.regionsList = regionsList pContent = getRegionsPage(regionsList, true) paragraph pContent //pname = "regionShellWork${app.id}.htm" //uploadHubFile ("$pname",pContent.getBytes("UTF-8")) } } } void removeRegion(regId){ regionsList = state.regionsList app.removeSetting('closeReg') regionsList.remove(regId) state.regionsList = regionsList ArrayList saveList = savePos.split(';') String updReg = '' saveList.each { // log.debug "${it.substring(0,it.indexOf(':'))} $regId" if(it.substring(0,it.indexOf(':')) != regId) updReg += "$it;" } app.updateSetting('savePos', [type:'text',value:"$updReg"]) } def addAppRegion(appId){ if(!state.regionMax) state.regionMax = 3 regionMax = state.regionMax regionsList = state.regionsList regionMax++ state.regionMax = regionMax fContent = frameWrap("frame${regionMax}","/installedapp/configure/$appId") newReg = getRegion("region-${regionMax}", "Application-$appId", "$fContent") regionsList.put("region-${regionMax}", newReg) state.regionsList = regionsList return regionsList } def addMenuRegion(refStr){ regionsList = state.regionsList if(!state.regionMax) state.regionMax = 3 regionMax = state.regionMax regionsList = state.regionsList regionMax++ state.regionMax = regionMax refStr=refStr.replace('|','/') fContent = frameWrap("frame${regionMax}","http://${location.hub.localIP}:8080/$refStr") newReg = getRegion("region-${regionMax}", "$refStr", "$fContent") regionsList.put("region-${regionMax}", newReg) state.regionsList = regionsList } String _appsList(){ String dList = "" dList += "" appList.each { dList+= "" } dList+="
"+getInputElemStr(name:"appFilter", type:'enum', radius:'12px', style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:'Filter', width:'15em',options:['All','Apps','Automations','Integrations'],defaultValue:"${settings['appFilter']}" )+"
"+getInputElemStr(name:"app-${it.key}", type:'button', style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:"${it.value}", noBorder:true )+"
" state.holdFilter = settings['appFilter'] return dList } ArrayList getAppList(){ ArrayList appList = [] try{ params = [ uri: "http://127.0.0.1:8080/hub2/appsList", headers: [ "Accept": "application/json" ] ] if(debugEnabled) log.debug "$params" httpGet(params){ resp -> if(debugEnabled) log.debug "${resp.data.apps}" resp.data.apps.each { apps -> fType = '' if(!apps.data.user) { resp.data.systemAppTypes.each{ if(it.id == apps.data.appTypeId) fType = it.menu } } else { resp.data.userAppTypes.each{ if(it.id == apps.data.appTypeId) fType = it.menu } } if(appFilter == 'All' || appFilter == fType ){ HashMap wMap = [key:"${apps.id}",value:"${apps.data.name}"] appList.add(wMap) } } } }catch (e){ log.error "$e" } //log.debug appList return appList } String _buildMenu(){ menuStr=getInputElemStr(name:"href-room|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Rooms ") menuStr+=getInputElemStr(name:"href-dashboard|select", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Dashboard ") menuStr+=getInputElemStr(name:"spec-applications", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Apps ") menuStr+=getInputElemStr(name:"href-device|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Devices ") menuStr+=getInputElemStr(name:"href-hub|edit", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Settings ") menuStr+=getInputElemStr(name:"href-logs", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Logs ")+"
" if(displayDevMenu()){ menuStr+=getInputElemStr(name:"href-driver|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Driver Code ") menuStr+=getInputElemStr(name:"href-app|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" App Code ") menuStr+=getInputElemStr(name:"href-library|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Library Code ") menuStr+=getInputElemStr(name:"href-bundle|list", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" Bundles ") } menuStr+='
'+getInputElemStr(name:"spec-about", type:'button', noBorder:true,style:'background-image:linear-gradient(to right, #a0d8ef, #b2f2e4, #ffffff);', color:'#000000', title:" About ")+"

" menuStr+='
'+getInputElemStr(name:"closeApp", type:'button',width:'12em', radius:'12px',background:'#ff0707', color:'#000000', title:" [X] Close Shell ") return menuStr } boolean displayDevMenu() { boolean retVal = false try{ params = [ uri: "http://127.0.0.1:8080/hub2/hubData", headers: [ "Accept": "application/json" ] ] if(debugEnabled) log.debug "$params" httpGet(params){ resp -> if(resp.data.showDeveloperMenu) retVal = true } } catch (e) { } return retVal } def appButtonHandler(btn) { switch(btn) { case { it.startsWith('href-') }: state.menuLaunch = btn.substring(5,) break case { it.startsWith('app-') }: //addAppRegion(btn.substring(4,)) state.newAppLaunch = btn.substring(4,) break case { it.startsWith('spec-') }: //addAppRegion(btn.substring(4,)) state.special = btn.substring(5,) break case 'closeApp': state.closeApp = true break default: break } } String frameWrap (_frameId, _frameSrc) { String frameMod = """ """ return "$frameMod" } /* * UI Regions * * Library to produce an html block with dragable/resizable regions * * * 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 * ------------- ------------------- --------------------------------------------------------- * */ import java.text.SimpleDateFormat import groovy.transform.Field library ( base: "app", author: "Jean P. May Jr.", category: "UI", description: "Set of methods that allow the customization of the UI ", name: "uiRegions", namespace: "thebearmay", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/refs/heads/main/libraries/uiRegions.groovy", version: "0.0.1", documentationLink: "" ) String getRegion(regionName, regionTitle, regionContent){ String region = """
${regionTitle}
-
${regionContent}
""" return region } String getRegionsPage( regionsList, fullScreen ){ // regionsList should be a list of map elements [regionName:regionContentString] String regionsMerged = '' String dragList = '' String defaultPos = '' int regionsInx = 0 int l = 10 int t = -20 int w = 300 int h = 250 regionsList.each { regionsMerged += it.value if(regionsInx > 0) { dragList += ',' defaultPos += ',' } dragList += "'${it.key}'" defaultPos += "'${it.key}':{ left:'${l}px',top:'${t}px',width:'${w}px',height:'${h}px',zIndex: '${regionsInx+1}' }" regionsInx++ if(!savePos) savePos = '' if(savePos.indexOf("${it.key}") == -1){ saveStr = "${it.key}:{${l}px,${t}px,${w}px,${h}px,${regionsInx+1},0,0};" app.updateSetting('savePos', [type:'text',value:"$savePos$saveStr"]) } t+= 44 l+= 30 } String bodyHtml = """
${inputHiddenElem(name:'savePos', type:'hidden', width:'1em', radius:'12px', background:'#2596be', title:'', submitOnChange:true, defaultValue:'')} ${regionsMerged} ${inputHiddenElem(name:'closeReg', type:'hidden', width:'1em', radius:'12px', background:'#2596be', title:'', submitOnChange:true, defaultValue:'')}
""" if(settings["savePos"]) bodyHtml+="" else bodyHtml+="" if(fullScreen) return bodyHtml + fullScrn else return bodyHtml } String inputHiddenElem(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.type == 'hidden'){ opt.type='text' typeAlt = 'hidden' computedStyle += 'visibility:hidden;' } if (opt.float) computedStyle +="float:${opt.float};" 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+="
" if(typeAlt != 'hidden') retVal +="
Save
" retVal +="
" return retVal } @Field static String fullScrn = "" /* * * 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 * 23May2025 Add device. to capability * 07Oct2025 Added textarea uiType * 20Feb2026 Minor enhancements and added 'hidden' as a valid input field type * 06Mar2026 Fixed mis-matched
in capability element */ 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.9", 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 "hidden": 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 "textarea": return inputTarea(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.type == 'hidden'){ opt.type='text' typeAlt = 'hidden' computedStyle += 'visibility:hidden;' } if (opt.float) computedStyle +="float:${opt.float};" 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+="
" if(typeAlt != 'hidden') retVal +="
Save
" retVal +="
" return retVal } /***************************************************************************** * Returns a string that will create an textArea element for an app - * * * * 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 * * height - CSS descriptor for field height * * 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) * * hoverText - Text to display as a tool tip * *****************************************************************************/ String inputTarea(HashMap opt) { if(!opt.name || !opt.type) return "Error missing name or type" typeAlt = opt.type String computedStyle = 'resize:both;' if(opt.width) computedStyle += "width:${opt.width};min-width:${opt.width};" if(opt.height) computedStyle += "height:${opt.height};" 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.float) computedStyle +="float:${opt.float};" 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.float) computedStyle +="float:${opt.float};" 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 = opt.style ? opt.style:'' if (opt.float) computedStyle +="float:${opt.float};" 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 } int ordinalIndexOf(String str, String substr, int n) { int pos = -1 (0..n).each { if (pos != -1 || it == 0) pos = str.indexOf(substr, pos + 1) } pos } /***************************************************************************** * 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.float) computedStyle +="float:${opt.float};" 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.float) computedStyle +="float:${opt.float};" 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.style) computedStyle += "$opt.style" if(opt.noBorder == true) computedStyle += "outline: none;box-shadow: none;text-align:left;" if (opt.float) computedStyle +="float:${opt.float};" 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.icon) opt.icon = [name:'fa-circle-info'] if(opt.hoverText && opt.hoverText != 'null') opt.title ="${opt.title}
${btnIcon(opt.icon)}${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.float) computedStyle +="float:${opt.float};" 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.icon) opt.icon = [name:'fa-circle-info'] 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(opt.icon)}${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.float) computedStyle +="float:${opt.float};" 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.icon) opt.icon = [name:'fa-circle-info'] 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(opt.icon)}${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}" } /***************************************************************************** * Code sample that returns a string that will create a standard HE table * *****************************************************************************/ /* String listTable() { ArrayList tHead = ["","Disable","Name","Device","Attributes","Interval","Output File",""] String X = "" String O = "" String settingsIcon = "settings_applications" String removeIcon = "" String str = "$tableStyle
" + "" tHead.each { str += "" } str += "" ... } */ //@Field static String fullScrn = "" @Field static String ttStyleStr = "" @Field static String tableStyle = ""
${it}