/*
*
*
* 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
* ---- --- ----
* 12Jul2024 thebearmay Add alternate read for extended characters
* 13Jul2024 charset = UTF-8 for HTML
* 26Jul2024 add getURL endpoint, remove alternate read
*/
static String version() { return '0.0.3' }
import groovy.transform.Field
definition (
name: "Hub Proxy Server",
namespace: "thebearmay",
author: "Jean P. May, Jr.",
description: "Hub Proxy Image Server",
category: "Utility",
importUrl: "https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/hubProxy.groovy",
installOnOpen: true,
oauth: true,
iconUrl: "",
iconX2Url: ""
)
preferences {
page name: "mainPage"
}
mappings {
path("/getImage") {
action: [POST: "serveImage",
GET: "serveImage"]
}
path("/getURL") {
action: [POST: "serveURL",
GET: "serveURL"]
}
}
def installed() {
// log.trace "installed()"
state?.isInstalled = true
initialize()
}
def updated(){
// log.trace "updated()"
if(!state?.isInstalled) { state?.isInstalled = true }
if(debugEnable) runIn(1800,logsOff)
}
def 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(name:"lhi",title:"Local Hub Information", hideablel: true, hidden: false){
paragraph "Local Server API: ${getFullLocalApiServerUrl()}"
paragraph "Cloud Server API: ${getFullApiServerUrl()}"
if(state.accessToken == null) createAccessToken()
paragraph "Access Token: ${state.accessToken}"
paragraph "Cloud Request Formats:
${getFullApiServerUrl()}/getImage?access_token=${state.accessToken}&fName=yourFileName.fileExtension
${getFullApiServerUrl()}/getURL?access_token=${state.accessToken}&url=urlEncodedURL"
}
section(name:"opt",title:"Options", hideablel: true, hidden: false){
input("debugEnabled","bool",title:"Enable Debug Logging", width:4)
}
} else {
section("") {
paragraph title: "Click Done", "Please click Done to install app before continuing"
}
}
}
}
def toCamelCase(init) {
if (init == null)
return null;
init = init.replaceAll("[^a-zA-Z0-9]+","")
String ret = ""
List word = init.split(" ")
if(word.size == 1)
return init
word.each{
ret+=Character.toUpperCase(it.charAt(0))
ret+=it.substring(1).toLowerCase()
}
ret="${Character.toLowerCase(ret.charAt(0))}${ret.substring(1)}"
if(debugEnabled) log.debug "toCamelCase return $ret"
return ret;
}
def appButtonHandler(btn) {
switch(btn) {
default:
log.error "Undefined button $btn pushed"
break
}
}
def jsonResponse(retMap){
return JsonOutput.toJson(retMap)
}
def serveImage(){
if(debugEnabled)
log.debug "serveImage called: ${params} source: ${request.requestSource}"
renderFile(params.fName)
}
def serveURL(){
if(debugEnabled)
log.debug "serveURL called: ${params.url}"
fContent = readFile(params.url)
uploadHubFile("proxyWork${app.id}.htm",fContent.getBytes("UTF-8"))
renderFile("proxyWork${app.id}.htm")
}
void delWork(){
deleteHubFile("proxyWork${app.id}.htm")
}
def renderFile(fName) {
fileExt = fName.substring(fName.lastIndexOf(".")+1)
imageFile = downloadHubFile("${fName}")
if(debugEnabled) log.debug fileExt
switch (fileExt) {
case 'gif':
fType = 'image/gif'
break
case 'htm':
case 'html':
fType = 'text/html; charset=UTF-8'
break
case 'js':
fType = 'text/javascript'
break
case 'json':
fType = 'application/json'
break
case 'jpeg':
case 'jpg':
fType = 'image/jpg'
break
case 'png':
fType = 'image/png'
break
case 'svg':
fType = 'image/svg+xml'
break
case 'txt':
fType = 'text/plain'
break
default:
fType = 'text/plain'
break
}
if(imageFile.size() < 9999999){// limit was originally 131072
if(fileExt in imageType) {
if(debugEnabled) log.debug "Image Type"
contentBlock = [
contentType: fType,
contentLength:imageFile.size(),
gzipContent: true,
data: imageFile,
status:200
]
} else {
if(debugEnabled) log.debug "Text Type"
contentBlock = [
contentType: fType,
contentLength:imageFile.size(),
gzipContent: true,
data: new String(imageFile),
status:200
]
}
} else {
contentBlock = [
contentType: 'text/plain',
data: "File Length Error - ${imageFile.size()} exceeds maximum of 131072",
status:413
]
}
if(debugEnabled) {
log.debug "Content Length: ${imageFile.size()}"
log.debug contentBlock
}
delWork()
render(contentBlock)
}
String readFile(fName){
if(debugEnabled) log.debug "Alternate Read"
uri = "${fName}"
def params = [
uri: uri,
contentType: "text/html",
textParser: true,
ignoreSSLIssues: true
]
try {
httpGet(params) { resp ->
if(resp!= null) {
return """${resp.data}"""
} else {
log.error "Null Response"
return null
}
}
} catch (exception) {
log.error "Alt Read Error: ${exception.message}"
return null
}
}
@Field imageType=['gif','jpeg','jpg','png','svg']