/* * HPM Manifest Generator * * 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, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Change History: * * Date Who What * ---- --- ---- * 18Oct2022 thebearmay New Code * 23Nov2022 thebearmay Fix Repository Entry * 15Jul2023 thebearmay Handle error finding repository * 03May2024 thebearmay Fix namespace not being stored */ static String version() { return '1.0.4' } import java.text.SimpleDateFormat #include thebearmay.localFileMethods import groovy.transform.Field @Field hpmTags = [ "Alarm Systems", "Appliances", "Automations & Groups", "Bathroom", "Buttons", "Cleaning Devices", "Climate Control", "Cloud", "Dashboards", "Doors & Windows", "Energy Monitoring", "Garage Doors", "Health & Fitness", "IR & RF", "Irrigation", "LAN", "Lights & Switches", "Locks", "Misc. Devices", "Monitoring", "Motion Control", "Multimedia", "Multi Sensors", "Notifications", "Pets & Animals", "Pools & Spas", "Presence & Location", "Repeaters & Extenders", "Safety & Security", "Scales", "Shower", "Sleep", "Speakers", "Temperature & Humidity", "Timers", "Tools & Utilities", "Valves", "Vehicles & Transportation", "Voice Assistants", "Water Heater", "Water", "Weather", "Window Coverings", "Zigbee", "ZWave" ] definition ( name: "HPM Manifest Generator", namespace: "thebearmay", author: "Jean P. May, Jr.", description: "HPM Manifest Generator", importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/hpmManifestGen.groovy", installOnOpen: true, oauth: false, iconUrl: "", iconX2Url: "" ) preferences { page name: "mainPage" } void installed() { if(debugEnabled) log.trace "installed()" state?.isInstalled = true initialize() } void updated(){ if(debugEnabled) log.trace "updated()" if(!state?.isInstalled) { state?.isInstalled = true } if(debugEnabled) runIn(1800,logsOff) } void initialize(){ } void logsOff(){ app.updateSetting("debugEnabled",[value:"false",type:"bool"]) } def mainPage(){ dynamicPage (name: "mainPage", title: "", install: true, uninstall: true) { if (app.getInstallationState() == 'COMPLETE') { section("${app.getLabel()} v${version()}") { input "debugEnabled", "bool", title:"Enable Debug Logging:", submitOnChange:true, required:false, defaultValue:false, width:4 if(debugEnabled) { unschedule() runIn(1800,logsOff) } input "retainRepo", "bool", title:"Retain Repository Information", width:4 input "clearAll", "button",title:"Clear Workspace" if(state.clearReq) { state.clearReq = flase settings.each{ if("$it.key" != "nameOveride" && "$it.key" != "debugEnabled" && "$it.key" != "retainRepo" && !("$it.key" == "reposLoc" && retainRepo)) { app.removeSetting("$it.key") } } state.remove("aText") state.remove("bText") state.remove("dText") state.remove("manifest") } } section("Header Information", hideable: true, hidden: false){ sdf = new SimpleDateFormat("yyyy-MM-dd") cDate = new Date() input "packageName", "string", title: "Package Name", required: true, submitOnChange: true input "author", "string", title: "Author", required: true, submitOnChange: true input "versionStr", "string", title: "Version", required: true, submitOnChange: true, width:4 input "minimumHEVersion", "string", title: "Minimum HE Version", submitOnChange: true, defaultValue:"${location.hub.firmwareVersionString}", width:4 input "dateReleased", "string", title: "Date Released (yyyy-mm-dd)", submitOnChange: true, defaultValue: "${sdf.format(cDate)}", width:4 input "releaseNotes", "string", title: "Release Notes", submitOnChange: true input "documentationLink", "string", title: "Documentation Link", submitOnChange: true input "communityLink", "string", title: "Link to the Community Thread", submitOnChange: true } section("Bundles", hideable: true, hidden: false){ input "newBundle", "button", title: "+", width:1 input "clearBundle", "button", title: "Clear", width:1 if(state.newBundleReq){ input "bName", "string", title:"Bundle Name",submitOnChange:true input "bNameSp", "string", title:"Name Space",submitOnChange:true input "bLoc", "string", title:"Location",submitOnChange:true input "bReq", "bool", title: "Required",submitOnChange:true, defaultValue: true, width:4 input "bundleSave", "button", title: "Save" } if(state.bText != null) paragraph "
${state.bText}"
if(state.clearBundle) {
state.clearBundle = false
settings.each{
if("$it.key".startsWith("b")) {
app.removeSetting("$it.key")
}
}
state.remove("bText")
}
}
section("Apps", hideable: true, hidden: false){
input "newApp", "button", title: "+", width:1
input "clearApp", "button", title: "Clear", width:1
if(state.newAppReq){
input "aName", "string", title:"App Name",submitOnChange:true
input "aNameSp", "string", title:"Name Space",submitOnChange:true
input "aLoc", "string", title:"Location",submitOnChange:true
input "aReq", "bool", title: "Required",submitOnChange:true, defaultValue: true, width:4
input "aOauth", "bool", title: "Uses oAuth",submitOnChange:true, defaultValue:false, width:4
input "aPrimary", "bool", title: "Primary",submitOnChange:true, defaultValue: true, width:4
input "appSave", "button", title: "Save"
}
if(state.aText != null) paragraph "${state.aText}"
if(state.clearApp) {
state.clearApp = false
settings.each{
if("$it.key".startsWith("a")) {
app.removeSetting("$it.key")
}
}
state.remove("aText")
}
}
section("Drivers", hideable: true, hidden: false){
input "newDriver", "button", title: "+", width:1
input "clearDriver", "button", title: "Clear", width:1
if(state.newDriverReq){
input "dName", "string", title:"Driver Name",submitOnChange:true
input "dNameSp", "string", title:"Name Space",submitOnChange:true
input "dLoc", "string", title:"Location",submitOnChange:true
input "dReq", "bool", title: "Required",submitOnChange:true, defaultValue: true, width:4
input "driverSave", "button", title: "Save"
}
if(state.dText != null) paragraph "${state.dText}"
if(state.clearDriver) {
state.clearDriver = false
settings.each{
if("$it.key".startsWith("d")) {
app.removeSetting("$it.key")
}
}
}
}
section("Generate", hideable: true, hidden: false){
input "genManifest", "button", title: "Generate Manifest"
if(state.genManifest) {
man="{\n \"packageName\":\"$packageName\",\n"
man+=" \"author\":\"$author\",\n"
man+=" \"version\":\"$versionStr\",\n"
man+=" \"minimumHEVersion\":\"$minimumHEVersion\",\n"
man+=" \"dateReleased\":\"$dateReleased\""
if(releaseNotes != null) man+=",\n \"releaseNotes\":\"$releaseNotes\""
if(documentationLink != null) man+=",\n \"documentationLink\":\"$documentationLink\""
if(communityLink != null) man+=",\n \"communityLink\":\"$communityLink\""
if(state.bText != null) man+= ",\n \"bundles\": [\n$state.bText\n ]"
if(state.aText != null) man+= ",\n \"apps\": [\n$state.aText\n ]"
if(state.dText != null) man+= ",\n \"drivers\": [\n$state.dText\n ]"
man+="\n}"
state.manifest = man
state.genManifest = false
}
if(state.manifest) paragraph "$state.manifest" } section("Add Repository Entry", hideable: true, hidden: false){ input "reposLoc", "string", title: "Location of Repository", submitOnChange:true input "reposCat", "enum", title: "Category", submitOnChange:true, options:["Control","Convenience","Integrations","Notifications","Security","Utility"], width:4 input "reposTag", "enum", title: "Tags", submitOnChange:true, options: hpmTags, multiple:true, width:4 input "reposManLoc", "string", title: "Manifest Location" input "reposDesc", "string", title: "Description for HPM Search Display" input "genRepos", "button", title: "Generate Merged Repository" if(state.genRepos) { reposWork = readExtFile("$reposLoc") if(reposWork) { reposWork = reposWork.substring(0,reposWork.size()-3) reposWork = reposWork.substring(0,reposWork.lastIndexOf("}")+1) reposWork +=",\n {\n" reposWork += " \"id\":\"${GUID()}\",\n" reposWork += " \"name\":\"$packageName\",\n" reposWork += " \"category\":\"$reposCat\",\n" reposWork += " \"location\":\"$reposManLoc\",\n" reposWork += " \"description\":\"$reposDesc\"" if(reposTag != null) { firstTag = true reposWork += ",\n \"tags\":[" reposTag.each{ if(firstTag) { firstTag = false reposWork += "\n \"$it\"" } else reposWork +=",\n \"$it\"" } reposWork +="\n ]\n" } else reposWork += "\n" reposWork += " }\n ]\n}" paragraph "
$reposWork" } else paragraph '
{ "author": "Unknown",\n"gitHubUrl": "https://github.com/unknown",\n"packages": [\n]\n}'
state.genRepos = false
}
}
section("Reset Application Name", hideable: true, hidden: true){
input "nameOverride", "text", title: "New Name for Application", multiple: false, required: false, submitOnChange: true, defaultValue: app.getLabel()
if(nameOverride != app.getLabel()) app.updateLabel(nameOverride)
}
} else {
section("") {
paragraph title: "Click Done", "Please click Done to install app before continuing"
}
}
}
}
String GUID(){
UUID uuid = UUID.randomUUID()
return uuid.toString()
}
void appButtonHandler(btn) {
switch(btn) {
case ("newBundle"):
state.newBundleReq = true
break
case ("bundleSave"):
if(state.bText != null) bText=",\n"
else bText = ""
bText+=" {\n \"id\":\"${GUID()}\",\n"
bText+=" \"name\":\"$bName\",\n"
bText+=" \"namespace\":\"$bNameSp\",\n"
bText+=" \"location\":\"$bLoc\",\n"
bText+=" \"required\":$bReq\n }"
if(state.bText != null) state.bText += bText
else state.bText = bText
state.newBundleReq = false
break
case ("newApp"):
state.newAppReq = true
break
case ("appSave"):
if(state.aText != null) aText=",\n"
else aText = ""
aText+=" {\n \"id\":\"${GUID()}\",\n"
aText+=" \"name\":\"$aName\",\n"
aText+=" \"namespace\":\"$aNameSp\",\n"
aText+=" \"location\":\"$aLoc\",\n"
aText+=" \"required\":$aReq,\n"
aText+=" \"oauth\":$aOauth,\n"
aText+=" \"primary\":$aPrimary\n }"
if(state.aText != null) state.aText += aText
else state.aText = aText
state.newAppReq = false
break
case ("newDriver"):
state.newDriverReq = true
break
case ("driverSave"):
if(state.dText != null) dText=",\n"
else dText = ""
dText+=" {\n \"id\":\"${GUID()}\",\n"
dText+=" \"name\":\"$dName\",\n"
dText+=" \"namespace\":\"$dNameSp\",\n"
dText+=" \"location\":\"$dLoc\",\n"
dText+=" \"required\":$dReq\n }"
if(state.dText != null) state.dText += dText
else state.dText = dText
state.newDriverReq = false
break
case ("clearAll"):
state.clearReq = true
break
case ("clearBundle"):
state.remove("bText")
state.clearBundle = true
break
case ("clearApp"):
state.remove("aText")
state.clearApp = true
break
case ("clearDriver"):
state.remove("dText")
state.clearDriver = true
break
case ("genManifest"):
state.genManifest= true
break
case ("genRepos"):
state.genRepos= true
break
default:
if(debugEnabled) log.error "Undefined button $btn pushed"
break
}
}
void intialize() {
}
void uninstalled(){
chdList = getChildDevices()
chdList.each{
deleteChildDevice(it.getDeviceNetworkId())
}
}